背景
STM32開發平臺,時至今日發展的已經相當成熟了,尤其對於外圍硬件接口的抽象封裝庫,即HAL。好多基於STM32開發的工程師,習慣於直接操作外圍接口相關的寄存器來完成所謂的驅動開發,其實,ST公司早就爲大家準備好了對於這些外圍接口的驅動框架,我們只需要直接拿來就可以使用。
可是以前,基於STM32平臺開發時,存在一個問題就是,變換MCU型號時,需要在新老MCU之間移植驅動程序,移植過程中可能出現很多莫名其妙的問題,導致浪費了很多寶貴的開發時間,如何避免這種問題呢?其實,ST早就爲大家考慮到了這一點,其開發了STM32CubeMX工具,基於該工具,我們可以輕鬆的完成各種外圍接口的配置工作,而且最爲神奇的就是,配置完成之後,該工具會自動生成工程代碼,我們基於此,就可以直接進行開發了,免去了很多不必要的硬件寄存器配置工作。當然,對於特殊需求,我們還是需要去手動配置一些硬件寄存器,但是,這項工作大部分時間是不需要的。
既然,ST公司爲我們提供了這麼好用的工具,我們爲什麼不用起來呢?下面就基於UART的配置過程,來逐步的講解一下,如何基於STM32CubeMX、Kiel和STM32 HAL來配置生成一個可用的開發環境。
開發步驟
配置環境
工欲善其事,必先利其器。首先第一步就是安裝STM32CubeMX環境,關於STM32CubeMX環境的安裝,網上有好多寫的特別棒的文中,這裏引用一篇
ybhuangfugui的博客,在此感謝ybhuangfugui的無私貢獻。大家可以閱讀該文章完成STM32CubeMX環境的安裝。
在這裏說一句,STM32CubeMX提供所有STM32家族的MCU芯片的初始配置抽象,並提供了HAL,封裝、抽象了關於底層硬件的配置、使用過程,這是一把雙刃劍,有利有弊:利是它增強了STM32不同MCU之間的兼容性,大大加快了初始開發進度,其提供的HAL爲開發者提供了統一的開發接口,這樣節省了寶貴開發時間。同時,基於其提供的配套的中間件(RTOS、TCP/IP、FS等),開發者可以快速的完成更高級需求的環境定製,這對於開發者來說是不小的福音;弊是,STM32 MCU畢竟是比較低級的硬件開發平臺,其開發過程中涉及到大量的底層硬件操作,如果我們想徹底理解這些基礎的硬件操作原理的話,可能還是需要從最爲原始的硬件寄存器配置開始。
不過,個人感覺STM32CubeMX利大於弊,就像通用操作系統,其屏蔽了所有的硬件相關的操作,爲開發者抽象出了進程、內存管理、文件系統、網絡等等,易於開發、理解的開發接口,也是正是操作系統的逐步成熟,才最終促使了後來的計算機一次又一次的革命。我想,對於MCU來說,其過程是類似的。如今,已經進入了物聯網時代,各種各樣的的硬件開發平臺層出不窮,各種層次的開發者都涌入到了這一開發領域。試想對於一個原來從事於Web開發的人員來說,其不可能從頭去理解底層的硬件知識,其需要的就是一個快速、高效的配置工具,完成原始開發環境的搭建。這時,STM32CubeMX就可以提供很大的幫助。
相信,STM32CubeMX未來肯定會發展的更爲智能,任何一個有志於基於該平臺開發的開發者,都會被其強度的功能所折服的,零基礎的開發人員也能夠快速上手該平臺。我想,這可能也是STM32CubeMX未來的目標吧。
最後說明一下,本文所使用的STM32CubeMX的版本是:5.5.0。溫馨提示,不同版本的操作步驟也能不太一樣,所以建議使用相同的版本進行配置。
開始配置
首先,我們需要確定我們所使用的STM32的MCU具體型號,然後,打開STM32CubeMX,創建一個新的工程。
我們選擇第一個“ACCESSTO MCU SELECTOR”,開始創建一個工程。我們選擇的MCU是STM32L010K8,安裝如下圖的步驟創建工程。
- 選擇MCU型號
- 查看MCU型號信息
- 創建項目
然後,選擇Project Manager項目,配置本項目的一些信息。
接下來,我們進行Pinout & Configuration配置,這裏面會涉及到MCU提供的所有外圍硬件的相關配置。本文主要關注的是UART的配置過程,STM32L010K8提供一個UART2和LPUART,我們選擇的配置項爲UART2。
UART2的基本配置步驟如下,
-
選擇UART2配置項。
-
選擇UART2的工作模式,這裏選擇的是Asynchronous。
-
配置UART2的參數,參數分爲Basic Parameters、Advanced Paramters、Advanced Features幾類。
- 基本參數配置就是波特率、字長、校驗方式、停止位;
- 高級參數配置就是數據傳輸方向、採集相關的配置;
- 高級特性配置:就是UART2提供的一些比較高級的功能,比如,自適應波特率、Overrun等。
我們選擇UART2的中斷方式處理數據的傳輸模式,所以,需要進一步配置NVIC。配置步驟如下:
- 點擊System Core中的NVIC配置。
- 使能USART2中斷,並配置中斷搶佔優先級爲0。
還有就是需要確定UART2使用GPIO引腳,配置方式如下:
這裏使用的是PA2和PA3。
最後,配置SysClock,我們最終選擇的系統主時鐘頻率爲16MHz。
完成上述配置之後,點擊GENERATE CODE完成項目代碼的生成。
HAL
項目代碼生成之後,我們使用Kiel打開,如下圖:
下面是main中的芯片初始化代碼:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
我們看到,STM32CubeMX自動生成了關於HAL、系統時鐘、UART2的初始化代碼。下面看一下UART2的初始化代碼:
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
stm32l0xx_it.c中定義了UART2的中斷處理函數,代碼如下:
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
該中斷處理函數用來處理所有與UART2相關的中斷事件。那如何才能開啓UART2的數據發送和接收呢?這就需要HAL中關於UART2數據發送接收的接口函數:
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
當我們需要已中斷的方式接收數據時,就調用HAL_UART_Receive_IT函數,其中第一個參數是UART的描述符,pData保存數據的緩存區,Size是緩存區的大小。注意,調用完該接口之後,UART2開始已中斷的方式接收數據,數據的大小爲Size,當接收了Size個字節的數據之後,UART2就不會再接收數據。若想再次接收數據,需要再一次調用HAL_UART_Receive_IT函數。一個比較好的實現就是,在重新覆蓋實現下面的函數,
`__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)`
該函數會在數據接收完之後被調用,然後我們在函數中,可以將數據拷貝走,然後再次調用HAL_UART_Receive_IT。
對於數據的發送,處理過程與接收類似,所不同的就是發送的接口函數爲:HAL_UART_Transmit_IT,該發送完成之後的回調函數是:
__weak void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
好了,至此我們基本完成了基於STM32CubeMX的UART的配置過程,可以看到基於此種方式,我們基本上不會觸碰到硬件寄存器的配置過程,所要做的就是基於HAL框架提供的接口完成APP的開發。這也許就是STM32CubeMX的魅力所在吧,簡單,高效,可移植性強。
UART_TxCpltCallback(UART_HandleTypeDef *huart)
好了,至此我們基本完成了基於STM32CubeMX的UART的配置過程,可以看到基於此種方式,我們基本上不會觸碰到硬件寄存器的配置過程,所要做的就是基於HAL框架提供的接口完成APP的開發。這也許就是STM32CubeMX的魅力所在吧,簡單,高效,可移植性強。