公司開發需要用到stm32,nxp,gd32等系列的單片機,大學幾乎沒接觸過單片機的學習,所以只能自己下來慢慢學,如有錯誤,歡迎指出。
ARM(公司,處理器,架構)
ARM處理器家族
- 經典:ARM7,ARM9,ARM11
- Cortex-M:單片機驅動(控制)
- Cortex-A:開發式系統的高性能處理器(智能手機 ,數字電視)
- Cortex-R:實時系統,實時的控制需求(汽車制動,動力系統)
想要操作stm32的外設通常可以使用寄存器或者stm32庫,在當今stm32庫已經十分成熟,並且操作十分簡單。
寄存器:寄存器則是內置於各個 IP 外設中,是一種用於配置外設功能的存儲器,就是一種內存,並且有相對應的地址。
GPIO:通用輸入輸出接口
按鍵:通過輸入電平的高低感知來得到按鍵的按下與否。
led燈:通過輸出高低電平控制led的亮與滅。
GPIO也可以作為串口數據的收發管腳或AD接口等復用功能,根據數據手冊配置相應的功能寄存器。
GPIO寄存器MODER
32位兩位為一組,分為了16個,分別對應每一組GPIO的16個 管腳。如PA0-PA15。
點燈:學習嵌入式開發最開始的操作就是點燈,以下使用寄存器操作和庫函數操作來點燈。
寄存器:從技術參考手冊中可以找到ODR 寄存器的地址偏移是: 0CH,這個偏移地址是基於端口的起始地址而言的。在 STM32 中,每個外設都有一個起始地址,叫做外設基址,外設的寄存器就以這個基地址為標准按照順序排列,跟結構體里面的成員差不多。
假設我們需要點亮的燈是由GPIOB-0這個管腳去控制的,找到GPIOB對應的基地址,再加上ODR的偏移地址就可以去實現輸出功能。


#define RCC_APB2ENR *(volatile unsigned long *)0x40021018 #define GPIOB_CRL *(volatile unsigned long *)0x40010C00 #define GPIOB_ODR *(volatile unsigned long *)0x40010C0C int main( void ) { // 開啟端口 B 的時鍾 RCC_APB2ENR |= 1<<3; // 配置 PB0 為通用推挽輸出模式,速率為 2M GPIOB_CRL = (2<<0) | (0<<2); // PB0 輸出低電平,點亮 LED GPIOB_ODR = 0<<0; } void SystemInit( void ) { }
這種就是比較愚蠢的配置方式,每次配置一個外設就去從參考手冊中去找,浪費時間且容易出錯,但新手入門是需要理解並且操作學習一下的。
如果要以寄存器配置的話,會將所有的寄存器位置都先定義好,這樣就不用一個個去找。實際開發過程中很少使用寄存器操作,一般都是庫函數的操作,下面介紹庫的操作。
keil的stm32環境自行去百度配置,篇幅太長就不寫出了。
固件庫分析:看看每個文件的作用是什么,這對我們能否清晰的調用庫函數編程非常重要。
STM32 由 Cortex-M3 內核和內核之外的各種外設組成,庫在編寫的時候也遵循這中組成結構,把代碼分成兩大部分,一種是操作內核外設的,
另外一種是內核之外的外設,為了聽起來不那么繞,下面我們把內核之外的外設用處理器外設來代替。

- startup_stm32f10x_hd.s:
這個是由匯編編寫的啟動文件,是 STM32 上電啟動的第一個程序,啟動文件主要實現了:1、初始化堆棧指針SP;2、設置 PC 指針 =Reset_Handler ;3、設置向量表的地址,並初始化向量表,向量表里面放的是 STM32 所有中斷函數的入口地址4、調用庫函數SystemInit ,把系統時鍾配置成 72M ,SystemInit 在庫文件 stytem_stm32f10x.c 中定義;5、跳轉到標號 _main,最終去到 C 的世界。
- system_stm32f10x.c:
這個文件的作用是里面實現了各種常用的系統時鍾設置函數,有 72M,56M, 48,36,24,8M,我們使用的是把系統時鍾設置成 72M。
- stm32f10x.h:
這個頭文件非常重要,可以說是上帝之手。這個頭文件實現了: 1、處理器外設寄存器的結構體定義 2、處理器外設的內存映射 3、處理器外設寄存器的位定義。
- stm32f10x_xxx.h:
外設 xxx 應用函數庫頭文件,這里面主要定義了實現外設某一功能的結構體,比如通用定時器有很多功能,有定時功能,有輸出比較功能,有輸入捕捉功能,而通用定時器有非常多的寄存器要實現某一個功能,比如定時功能,我們根本不知道具體要操作哪些寄存器,這個頭文件就為我們打包好了要實現某一個功能的寄存器,是以結構體的形式定義的,比如通用定時器要實現一個定時的功能,我們只需要初始化TIM_TimeBaseInitTypeDef 這個結構體里面的成員即可,里面的成員就是定時所需要操作的寄存器。 有了這個頭文件,我們就知道要實現某個功能需要操作哪些寄存器,然后再回手冊中精度這些寄存器的說明即可。
- stm32f10x_xxx.c :
外設 xxx 應用函數庫,這里面寫好了操作 xxx 外設的所有常用的函數,我們使用庫編程的時候,使用的最多的就是這里的函數。
- cor_cm3.h:
這個頭文件實現了: 1、內核結構體寄存器定義 2、內核寄存器內存映射 3、內存寄存器位定義。跟處理器相關的頭文件 stm32f10x.h 實現的功能一樣,一個是針對內核的寄存器,一個是針對內核之外,即處理器的寄存器。
- misc.c:
內核應用函數庫文件,對應 stm32f10x_xxx.c 。在 CM3 這個內核里面還有一些功能組件,如 NVIC、SCB、ITM 、MPU、CoreDebug ,CM3 帶有非常豐富的功能組件,但是芯片廠商在設計 MCU 的時候有一些並不是非要不可的,是可裁剪的,比如 MPU、ITM 等在STM32 里面就沒有。其中 NVIC 在每一個 CM3 內核的單片機中都會有,但都會被裁剪,只能是 CM3 NVIC 的一個子集。在 NVIC 里面還有一個 SysTick,是一個系統定時器,可以提供時基,一般為操作系統定時器所用。
-
misc.h:內核應用函數庫頭文件,對應 stm32f10x_xxx.h 。
直接寫庫函數操作了:
void LED_GPIO_Config(void) { /*定義一個GPIO_InitTypeDef類型的結構體*/ GPIO_InitTypeDef GPIO_InitStructure; /*開啟GPIOC的外設時鍾*/ RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); /*選擇要控制的GPIOB引腳*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /*設置引腳模式為通用推挽輸出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*設置引腳速率為50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; /*調用庫函數,初始化GPIOB*/ GPIO_Init(GPIOB, &GPIO_InitStructure); /* 關閉led燈 */ GPIO_SetBits(GPIOB, GPIO_Pin_0);
/* 開啟led燈 */
GPIO_ResetBits(GPIOB, GPIO_Pin_0);
}
typedef struct { uint16_t GPIO_Pin; /*指定將要進行配置的 GPIO 引腳* GPIOSpeed_TypeDef GPIO_Speed; /*指定 GPIO 引腳可輸出的最高頻率*/ GPIOMode_TypeDef GPIO_Mode; /*指定 GPIO 引腳將要配置成的工作狀態*/ }GPIO_InitTypeDef;
Mode和Speed相關結構體也是被設定好的,可以自行查找這里就不寫出來了,直接賦值就可以了,十分方便並且相對於寄存器操作便於理解。
GPIO_Init(GPIO_TypeDef *, GPIO_InitTypeDef *);第一個參數,說明它將要對GPIOB 端口進行初始化。初始化的配置以第二個參數 GPIO_InitStructure 結構 體的成員值為准。這個結構體的成員,我們在調用GPIO_Init()前,已對它們 賦予了控制參數。