STM32F0單片機快速入門六:用庫操作串口(UART)原來如此簡單

1. 從 GPIO 到 UART

前面幾節我們講了MCU如何啟動,如何用翻轉IO引腳,以及用按鍵去觸發中斷。接下來我們介紹的也是最常用的一個模塊,串口(UART)。

串口可以說是最古老,而且生命力最強的一種通信接口了。RS485總線更是久經考驗。雖然串口早已經從大多數PC的標配中去掉了,但是嵌入式系統跟上位PC機通信用的最多的應該還是通過串口轉USB吧。

我們用 Keil 打開下面這個工程:

STM32Cube_FW_F0_V1.11.0\Projects\STM32F030R8-Nucleo\Examples\UART\UART_TwoBoards_ComPolling\MDK-ARM\Project.uvprojx

這個代碼配置串口為 9600,8 N 1,我們把代碼編譯下載後,可以通過 UART to USB 轉換器連接到 PC 的 USB 口,在PC端用串口觀察MCU發送的數據。

STM32F0單片機快速入門六:用庫操作串口(UART)原來如此簡單


STM32F0單片機快速入門六:用庫操作串口(UART)原來如此簡單

2. UART 的初始化

我們看一下代碼,串口參數的設置是在主程序裡完成的,還有一部分是在stm32f0xx_hal_msp.c 裡完成的。為什麼要這麼費事兒,而不把初始化代碼全放在一個主程序裡完成呢?

我們要慢慢體會這樣做帶來的好處。我們調用一個驅動時,這個驅動難免會跟底層硬件打交道,比如串口驅動,它最終是利用用戶選擇的某一個串口模塊,和與此模塊連接的收發引腳進行數據收發的。

HAL(Hardware Abstract Layer) 把跟具體硬件細節相關的代碼單獨剝離了出來,並在Cube庫中引入了 MSP(MCU Support Package) 的概念, 具體的硬件細節交給用戶在這裡面配置。

HAL庫裡面對應每個硬件模塊有兩個函數 例如:

HAL_UART_Init( ) 功能上的描述:設置收發模式、奇偶校驗位、停止位數等等(與芯片無關)。

HAL_UART_MspInit( ) 硬件的描述: IO初始化,不同芯片,不同引腳設置不同。

STM32F0單片機快速入門六:用庫操作串口(UART)原來如此簡單

回到程序,我們要使用串口時要調用驅動層的初始化函數 HAL_UART_Init( ),這個初始化函數回過頭來調用了 HAL_UART_MspInit( ) 這個函數來完成 UART 時鐘和收發引腳時鐘的使能,以及收發引腳的配置。之後初始化函數繼續進行 UART 端口的參數配置。

這樣做的一個好處就是使驅動層的初始化函數與硬件無關。一般我們做好一塊板子後,所用的串口和引腳也就固定下來了,在 HAL_UART_MspInit( ) 裡配置一次就好了,之後不需要頻繁的改變這些代碼。

STM32F0單片機快速入門六:用庫操作串口(UART)原來如此簡單

STM32F0單片機快速入門六:用庫操作串口(UART)原來如此簡單

3. 熟悉 Handle

跟 GPIO 的初始化有所不同,在UART這個模塊引入了 Handle 這一概念。看 Handle 之前我們先熟悉一下在驅動裡經常用到的結構體及其指針的用法:

STM32F0單片機快速入門六:用庫操作串口(UART)原來如此簡單

typedef struct __MY_TypeDef

{

uint8_t Var1;

uint8_t Var2;

uint16_t Var3;

uint8_t* Var4;

}MY_TypeDef;

MY_TypeDef* MY_VAR;

MY_VAR 是一個 MY_TypeDef 類型的指針,我們看看把它指向不同的地址時會發生什麼?


MY_VAR = (MY_TypeDef*) 0x20000018;

STM32F0單片機快速入門六:用庫操作串口(UART)原來如此簡單

需要注意 MY_VAR->Var4 是個字節型指針變量,這個變量本身佔用4個字節,它的值是 0x20000018, 而

* MY_VAR->Var4 的值是 0x02。


把 MY_VAR 指向另一個地址:

MY_VAR = (MY_TypeDef*) 0x2000001C;

STM32F0單片機快速入門六:用庫操作串口(UART)原來如此簡單

與此類似,對於串口模塊,驅動定義了一個結構體類型 UART_HandleTypeDef,我們可以用這個類型定義多個結構體,並通過把串口模塊寄存器區的起始地址付給一個結構體,使該結構體和串口之間建立起聯繫:

STM32F0單片機快速入門六:用庫操作串口(UART)原來如此簡單

我們運行的當前程序操作串口的方式為查詢(polling)方式,結構體中和DMA,中斷方式相關的內容可以先忽略,只需要關注結構體中下面這些成員即可:

USART_TypeDef *Instance;

USART_TypeDef 類型的指針,需要指向欲操作的串口寄存器區起始地址。以把此 Handle 和該串口建立起聯繫。


UART_InitTypeDef Init;

在調用初始化函數前,需要把初始化參數如 波特率,是否奇偶校驗等寫入此結構體。


UART_AdvFeatureInitTypeDef AdvancedInit;

串口擴展功能初始化參數。當前未用到擴展功能。


使用 Handle 的好處是,我們操作某個模塊時,把這個模塊對應的 Handle 的首地址傳給驅動函數就行了。此函數通過 Handle 就可以找到所有需要的東西。如:

HAL_UART_Transmit(&UartHandle, (uint8_t*)aTxBuffer, TXBUFFERSIZE, 5000);

&UartHandle 為 UART1 對應的 Handle 的首地址。


Handle 除了保存自己對應模塊的參數信息,還保存緩衝數據,以及當前工作狀態。它可以保證各模塊之間互不干擾,在代碼執行過程中被打斷,恢復後又可以正確繼續執行。這樣也便於把驅動集成到操作系統中。在以後的中斷方式和 DMA 操作模式中,我們可以更深刻的體會到這種方法的優點。在理解了串口模塊的工作方式後,理解其它模塊就非常容易了。

需要提到的是,在M0芯片內,有一些共享的或系統級的硬件模塊不使用 Handle 的方式來處理:

GPIO

SYSTICK

NVIC

PWR

RCC

FLASH.


參考資料:

PM0215 STM32F0xxx Cortex-M0 programming manual

UM1785 Description of STM32F0 HAL and low-layer drivers

STM32F030 Datasheet

STM32F030 Reference Manual


碼字辛苦,如果喜歡就隨手點個贊吧!


分享到:


相關文章: