1. 程式人生 > >STM32開發入門及實戰 (1)

STM32開發入門及實戰 (1)

本部落格的編寫目的:
一、自我總結,記錄。 
二、分享,輸出,加深思考。 
三、不作細緻如書本般編排,儘管那樣的排版很好看,但是過於耗費時間,還有很多東西沒有必要說明,完全可以自己去解決,但還是儘量做好排版,便於閱讀。 
四、儘可能舉一反三,做到真正能夠處理實際問題。

STM32開發實戰 (1)
目錄

一、概述,目的
二、搭建步驟
三、時鐘部分案例分析
四、理論總結

一、概述,目的

 目的:解決STM32入門問題

 個人認為STM32的最快,最直接的入門方法之一就是:從STM32CubeMx+keilV5入手。無論 你採用FreeRTOS還是Keil自帶的RTX,通過圖形化的介面配置,都能快速生成專案所需的基礎層架構程式碼,從而將主要精力用於自身專案需求開發上,大大提高開發效率。

 上一段話包含兩層意思:1、在不熟悉STM32的情況下,如何入手學習相關的技術知識。2、在不熟悉STM32的情況下,作為公司在職開發人員,如何快速進入STM32相關的專案開發工作中,保證開發效率。

二、搭建步驟

看圖去官網或者下載站下載:STM32CubeMX,MDK5(MDK-ARM V5)

這裡寫圖片描述

安裝完成後,就可以選擇你要使用的具體晶片型號,本篇晶片為stm32030系列 、stm32103系列
這裡寫圖片描述
初始介面如下,圖形化的管腳配置,點點滑鼠就可以,so easy!更深入的在後續章節再說。
左側欄先要注意的幾個問題:
1、你可以選擇是否使用FREERTOS
2、如果選擇外部時鐘,請務必選擇 “RCC-HSE 選項,如圖配置”否則 Input frequency 輸入選項不可更改,系統時鐘最高只能為64MHZ,達不到72MHZ
3、SYS選項,時鐘源雖然預設看起來是SysTick,但實際上沒起作用,所以,需要重新選擇一次,知道SYS標題變綠色,即選擇成功。

這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

自己摸索一下,看看網上的教程,比如“微雪教程”。然後,選單欄 project->Generate code
這裡寫圖片描述
注意一些相關提示:
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

生成程式碼之後,就可以直接開啟工程了。
這裡寫圖片描述

這是沒有安裝MDK-ARM V5的提示:

這裡寫圖片描述

開啟工程後預設的專案檔案列表:

這裡寫圖片描述

三、時鐘部分案例分析

  對於微控制器系統來說,CPU和匯流排以及外設的時鐘設定是非常重要的,因為沒有時鐘就沒有時序。

*AHB匯流排,這是貫穿所有外設的一條匯流排,上圖可知:AHB經過橋接,由APB1、APB2控制著幾乎所有外設;
APB2屬於高速裝置; (控制著如:ADC、GPIO、EXIT、TIM1等外設)
APB1屬於低速裝置; (控制著如:DAC、TIMx、USART、I2C等外設)*

 **很多人在講解知識時,如上作以解釋,有用嗎?反正我覺得是沒用。那怎麼做更好呢?
 看一個我碰到的專案例項:一同事在用STM32CubeMx生成的程式碼,要交到我這裡來對專案程式碼進行整合,程式碼裡用到的延時函式有兩個HAL_Delay(), osDelay(),理論上,這兩個延時函式的引數延時基準都是ms,也就是說HAL_Delay(1000), osDelay(1000)都表示延時1000ms,但是我還是要測試一下延時是否準確,因為還有其他好多地方要用到,而且對延時精度要求可能更高點**。

通過示波器測試得知osDelay(1000)是準確的,而HAL_Delay(1000)的延時值實際只有500ms,問題在哪呢?通過圖形配置部分,得知他設定的SYS 時鐘源為TIM1,那麼,理論上是和APB2的FCKL2相關。我們先定得找到延時函式所用到的引數配置,看原始碼:
函式原型:
__weak void HAL_Delay(__IO uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
while((HAL_GetTick() - tickstart) < Delay)
{
}
}

–>
__weak uint32_t HAL_GetTick(void)
{
return uwTick;
}
–>
static __IO uint32_t uwTick;
__weak void HAL_IncTick(void)
{
uwTick++;
}
–>
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) // 在 此檔案下,定義了 TIM_HandleTypeDef htim1;
{
HAL_IncTick();
}
–>
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)

{
/Configure the SysTick to have interrupt in 1ms time basis/ // 1ms 中斷 時基
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

/Configure the SysTick IRQ priority /
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0);

/* Return function status */
return HAL_OK;
}
–>
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
……// 此處省略

/* Compute TIM1 clock */
uwTimclock = HAL_RCC_GetPCLK1Freq(); // 開始這裡用的PCLK1

……// 此處省略
}

由uwTimclock = HAL_RCC_GetPCLK1Freq(); // 開始這裡用的PCLK1,顯然不符合理論要求
串列埠輸出除錯資訊:
DBSTRLONG(“PCLK1Freq”,HAL_RCC_GetPCLK1Freq());
DBSTRLONG(“PCLK2Freq”,HAL_RCC_GetPCLK2Freq());

PCLK1Freq 36000000
PCLK2Freq 18000000
由uwTimclock = HAL_RCC_GetPCLK2Freq();// 這裡修改後,測試延時仍然不正確,為什麼?PCLK2Freq 18000000 頻率是不對的,而要修改PCLK2Freq的值,無非就是修改APB2的分頻值。本來是可以直接再圖形配置這裡直接修改的,但是我要做程式碼整合,很多程式碼自動升後,修改不方便,就直接通過原始碼修改。在系統時鐘初始化函式裡,如下:
SystemClock_Config(void)
{
……// 此處省略
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // 這裡修改RCC_HCLK_DIV4 –> RCC_HCLK_DIV1
……// 此處省略
}
串列埠輸出除錯資訊:
DBSTRLONG(“PCLK1Freq”,HAL_RCC_GetPCLK1Freq());
DBSTRLONG(“PCLK2Freq”,HAL_RCC_GetPCLK2Freq());

PCLK1Freq 36000000
PCLK2Freq 72000000

再次測試,結果就正確了。

上邊的問題說明三點:

1、雖然定時器(Timer)1是由APB2的PCLK2提供的時鐘輸出,但是解決問題的辦法並不是死的,所以由HAL_RCC_GetPCLK1Freq();提供的頻率輸出,結果不會錯誤,然而不符合理論要求:所以還是要在源
頭修改。特別是整合程式時,基本我不再用STM32CubeMX去自動生成程式碼,不然很多程式碼被自動修改,會造成很大麻煩。

2、STM32CubeMX生成的程式碼,有可能存在BUG,所以除錯需全面考慮。

3、在不用手冊,通過觀察CubeMX圖形配置部分,然後明確具體有關時鐘匯流排,外設關係的情況下,就可以找到程式碼的問題所在。

然後參照上圖詳細總結一下系統時鐘的關係如下:

其中40kHz的LSI供獨立看門狗IWDG使用,另外它還可以被選擇為實時時鐘RTC的時鐘源。另外,實時時鐘RTC的時鐘源還可以選擇LSE,或者是HSE的128分頻。RTC的時鐘源通過RTCSEL[1:0]來選擇。
  STM32中有一個全速功能的USB模組,其序列介面引擎需要一個頻率為48MHz的時鐘源。該時鐘源只能從PLL輸出端獲取,可以選擇為1.5分頻或者1分頻,也就是,當需要使用USB模組時,PLL必須使能,並且時鐘頻率配置為48MHz或72MHz。
  另外,STM32還可以選擇一個時鐘訊號輸出到MCO腳(PA8)上,可以選擇為PLL輸出的2分頻、HSI、HSE、或者系統時鐘。
  系統時鐘SYSCLK,它是供STM32中絕大部分部件工作的時鐘源。系統時鐘可選擇為PLL輸出、HSI或者HSE。系統時鐘最大頻率為72MHz,它通過AHB分頻器分頻後送給各模組使用,AHB分頻器可選擇1、2、4、8、16、64、128、256、512分頻。其中AHB分頻器輸出的時鐘送給5大模組使用:
  ①、送給AHB匯流排、核心、記憶體和DMA使用的HCLK時鐘。
  ②、通過8分頻後送給Cortex的系統定時器時鐘。
  ③、直接送給Cortex的空閒執行時鐘FCLK。
  ④、送給APB1分頻器。APB1分頻器可選擇1、2、4、8、16分頻,其輸出一路供APB1外設使用(PCLK1,最大頻率36MHz),另一路送給定時器(Timer)2、3、4倍頻器使用。該倍頻器可選擇1或者2倍頻,時鐘輸出供定時器2、3、4使用。
  ⑤、送給APB2分頻器。APB2分頻器可選擇1、2、4、8、16分頻,其輸出一路供APB2外設使用(PCLK2,最大頻率72MHz),另一路送給定時器(Timer)1倍頻器使用。該倍頻器可選擇1或者2倍頻,時鐘輸出供定時器1使用。另外,APB2分頻器還有一路輸出供ADC分頻器使用,分頻後送給ADC模組使用。ADC分頻器可選擇為2、4、6、8分頻。
  在以上的時鐘輸出中,有很多是帶使能控制的,例如AHB匯流排時鐘、核心時鐘、各種APB1外設、APB2外設等等。當需要使用某模組時,記得一定要先使能對應的時鐘。
  需要注意的是定時器的倍頻器,當APB的分頻為1時,它的倍頻值為1,否則它的倍頻值就為2。
  連線在APB1(低速外設)上的裝置有:電源介面、備份介面、CAN、USB、I2C1、I2C2、UART2、UART3、SPI2、視窗看門狗、Timer2、Timer3、Timer4。注意USB模組雖然需要一個單獨的48MHz時鐘訊號,但它應該不是供USB模組工作的時鐘,而只是提供給序列介面引擎(SIE)使用的時鐘。USB模組工作的時鐘應該是由APB1提供的。
  連線在APB2(高速外設)上的裝置有:UART1、SPI1、Timer1、ADC1、ADC2、所有普通IO口(PA~PE)、第二功能IO口。