STM32F0單片機快速入門四 翻轉引腳

1.第一個工程 翻轉引腳

上一篇文章我們詳細介紹了 STM32F030 從復位時取得復位向量,系統初始化,然後跳轉到 main( ) 函數的過程。下面我們結合一個最簡單的例子,對 Cube 庫的使用做一個簡單的介紹。

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

STM32Cube_FW_F0_V1.11.0\Projects\STM32F030R8-Nucleo\Examples\GPIO\GPIO_IOToggle\MDK-ARM\Project.uvprojx

編譯下載運行此代碼,會看到一個 LED燈(連至MCU的 PA5引腳)不停地閃爍。爲了完成這個簡單的功能,我們看到這個工程裏包含了不少文件:

如果是初次用這種庫的方式做開發,乍一看還真感覺有點亂。不過讓我們一個一個看一下這些文件,理清它們的關係後就會體會到這種方式的巨大優點。

2.文件分類解釋

工程裏的文件分爲五大類:啓動代碼,M0內核初始化,驅動,板級支持包(BSP),用戶代碼。一般來說我們開發應用程序,主要關注用戶代碼文件就行了。如果硬件電路板做了改動,則修改BSP裏的內容。

在早期的單片機開發中,芯片內資源很少,通常的情況是一個工程師就從硬件到軟件編程都做了,是沒有 BSP(Board Support Package)這種概念的。BSP概念來源於較複雜的CPU系統的開發,一般是廠家設計主板,並提供 BSP(包含啓動代碼,驅動,Bootloader等)。我們這裏的 BSP 概念稍有不同,它是指對某一塊兒以 MCU 爲核心的電路板的支持代碼包。啓動代碼,內核初始化和驅動,沒有包含在內。BSP會調用驅動層的代碼。

對於 STM32 Nucleo 這塊兒開發板來說,板上資源很少,所以BSP只提供了相應的按鍵(BUTTON)和指示燈(LED)支持代碼。裏面的ADC,SPI,LCD等代碼是支持其它板子的,可以先忽略。

啓動代碼

爲理解彙編代碼,我們先熟悉一下這些僞指令:

ALIGN 變量或代碼對齊。如:

ALIGN = 3 以8(2的3次方)字節對齊。

EQU 給標號賦值。如:

Stack_Size    EQU     0x400;

DCD 分配1個或多個字(words)的內存空間。如:

Data DCD 1,5,8; 定義3個字並賦值爲 1,5 和 8。

AREA 定義一個代碼或數據段(section),命名並指定屬性。如:

AREA Func01, CODE, READONLY;

定義了一個名字爲 Func01 的只讀代碼段。

SPACE 保留一段空間並初始化爲 0。如:

Data SPACE 100; 爲 Data 保留 100個字節初始化爲 0 的內存空間。

IMPORT 導入其它文件中的標號,以在當前文件中引用。如:

IMPORT  SystemInit

LDR     R0, =SystemInit

BLX     R0

從文件 system_stm32f0xx.c 中導入 SystemInit 這個函數並調用。

EXPORT 導出能被連接器(Linker)識別的標號。從ASM文件導出的標號可以在C中引用。

[WEAK] 如果在其它地方定義了相同的標號,則此處定義被覆蓋。

PROC 定義一個函數的起始地址。

ENDP 標誌當前函數結束。

例子:

SysTick_Handler PROC

                EXPORT  SysTick_Handler                [WEAK]

                B.

                ENDP

導出 SysTick_Handler 這個中斷處理函數。如果在其它地方定義了一個新的 SysTick_Handler 函數,那麼新函數將覆蓋此處定義的這個陷阱函數。彙編語句 B.爲在當前語句死循環。

下面我們看一下啓動文件 startup_stm32f030x8.s

定義堆和棧:

中斷向量表:

現在這個工程用到的只有綠線框中的幾個向量:

__initial_sp

初始堆棧指針

Reset_Handler

復位向量,我們在上一篇文章已經講到如何從復位向量一步一步執行到用戶代碼中的主程序main( )。

SysTick_Handler

系統時鐘中斷向量。此程序每 1ms 產生一次中斷。

需要注意的是 SysTick_Handler 這個中斷處理函數在用戶代碼文件stm32f0xx_it.c

中進行了重定義,所以當 SysTick 中斷髮生時,實際會跳轉到用戶代碼的中斷處理函數,而不是跳到下圖所示的彙編代碼中斷處理函數進入死循環。

再往下可以看到,對所有芯片級中斷定義了一個共享的陷阱函數。用戶在實際使用到某一箇中斷的時候,要在中斷處理文件 stm32f0xx_it.c 中用相同的函數名定義,從而在中斷髮生時跳轉到實際的中斷處理函數。

在此文件的最下面的代碼,是用來傳遞堆棧信息給庫的:

在芯片資源比較少時,可以通過選中 Options for Target->Target->Use MicroLIB 選項,使用簡化版的庫來實現 printf 等操作。若資源充足時使用標準庫,庫將調用下面的 __user_initial_stackheap 函數來獲得堆棧信息。

 

M0 內核初始化

system_stm32f0xx.c

此文件只有兩個函數:

SystemInit( ),在啓動代碼中調用,把系統時鐘復位到初始默認狀態(8MHz的高頻內部時鐘 HSI)。

SystemCoreClockUpdate( ), 在用戶調用庫函數更改時鐘配置後,需要調用此函數以更新全局系統時鐘變量 SystemCoreClock。其它模塊基於此時鐘的計算纔會正確。一般來說更改時鐘配置的 HAL函數已經包含此函數的調用,如 HAL_RCC_ClockConfig( ), 無需用戶再次調用。

驅動

stm32f0xx_hal_cortex.c

包含 Cortex 內核中兩個重要模塊的驅動:

可嵌套中斷向量控制器 NVIC(Nested Vectored Interrupt Controller),

系統滴答時鐘 SYSTICK。

stm32f0xx_hal.c

此文件包含用戶程序必須首先調用的 HAL_Init( ),它會使能數據和指令緩存,預取指令隊列;配置系統滴答時鐘產生 1ms 中斷;調用 HAL_MspInit( )回調函數。

HAL_MspInit( )函數用來做系統級的初始化,配置某一模塊相關的 時鐘,引腳,DMA,中斷等資源,但是在所有的例程中都沒有實際用到此函數。可以先忽略。

stm32f0xx_hal_rcc.c

stm32f0xx_hal_rcc_ex.c

RCC(Reset and Clock Controller)模塊的驅動。一個模塊爲什麼要兩個驅動文件呢?前一個文件提供了基本的通用的功能驅動,後一個文件是擴展功能驅動,通常針對某一特定型號的芯片。如同我們喫飯需要餐具,_rcc.c 提供碗筷等常用必備工具,_rcc_ex 可能提供的就是酒杯,燭臺等這些東西。

stm32f0xx_hal_gpio.c

GPIO 模塊的驅動。

BSP 板級支持包

stm32f0xx_nucleo.c

針對 STM32 Nucleo 開發板的類型,宏定義,支持代碼。

用戶代碼

main.c  主程序

stm32f0xx_it.c 中斷處理

前面介紹了一大堆文件,主要是爲了清除系統的工作流程。在開發中使用庫還是很簡單的。在主程序中調用庫,只需要通過 main.h 包含下面這個頭文件:

stm32f0xx_hal.h

如果有 BSP 則包含 BSP 的頭文件,在本工程是:

stm32f0xx_nucleo.h

使用到哪個模塊就在配置文件中打開使能該模塊的宏定義。

stm32f0xx_hal_conf.h

然後第一步必須調用 HAL_Init( )。

第二步,如果希望系統時鐘工作在默認內部時鐘(8MHz HIS)以外的頻率,則需要調用 SystemClock_Config( )。此函數又調用 HAL_RCC_ClockConfig( ) 完成新配置。

下面是應用代碼:

所有模塊一般都是這三個步驟:使能模塊的時鐘,初始化模塊,使用模塊的功能。

stm32f0xx_it.c 中的中斷處理函數 SysTick_Handler( ) 很簡單,每次進入就對滴答計時變量 uwTick 加1,其它 HAL 函數可以基於此變量計時。

3.參考資料

Description of STM32F0 HAL and low-layer drivers

ARM Compiler armasm User Guide

STM32F030 Datasheet

STM32F030 Reference Manual

 

4.關注 TopSemic 讓我們一起成長!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章