標籤:

stm32的「Hello World」——LED流水燈

(本文默認讀者已經掌握C語言基礎,如文中出現的位運算符操作就是所謂的基礎。另,文章看起來較長,其實以圖片居多喔)

LED流水燈堪稱stm32里「Hello World」級別的入門實驗。該怎麼概括這個所謂的「入門」呢?這麼說吧,流水燈實驗本身很簡單,無論是寄存器編程還是庫函數編程,其實就那麼幾句代碼,關鍵在於寫出這幾句代碼的過程是怎樣的。比如你需要查閱stm32參考手冊里的相關寄存器描述,你需要查看晶元的原理圖等。

而庫函數實現,說白了,就是對寄存器實現的封裝,查閱相關函數實現源碼便可知道其底層就是寄存器實現。所以很明顯,庫函數用起來更方便,無論從前期開發或是後期維護的角度都是。但從學習的角度,我們主要從寄存器實現的角度分析,懂得寄存器實現的原理,庫函數實現也就分分鐘的事了。

(本文實驗所採用的晶元型號為:STM32F103ZET6)

大概總結下實現流水燈效果的幾個步驟:

  1. 查閱開發板的晶元原理圖,得到控制LED燈的引腳;
  2. 新建工程,完成工程配置,開始編程;
  3. 開啟相應外設時鐘(需要查閱參考手冊里關於匯流排架構的結構圖和時鐘寄存器描述);
  4. 配置引腳模式(需要查閱stm32參考手冊里配置寄存器的描述);
  5. 控制輸出電平高低(需要查閱stm32參考手冊里輸出寄存器的描述)。

我們逐步來分析。

步驟1:

通過查閱晶元原理圖可得我們要控制的LED燈的引腳為:PB5(LED_R)、PF7(LED1)、PF8(LED2),如圖2-1。

圖2-1

步驟2:

在Keil5或其他IDE里新建工程,完成工程配置,然後開始編程。

步驟3:

每個外設對應一個時鐘,想要某個外設工作,就必須把相應外設的時鐘打開。stm32的所有外設都由RCC寄存器來管理,我們查看參考手冊里的匯流排架構圖(stm32中文參考手冊里第二章)可得知所有的GPIO外設都掛載到APB2匯流排上,如圖2-2。

圖2-2

所以查閱參考手冊(中文參考手冊第六章)可得到控制APB2外設時鐘使能的寄存器為RCC_APB2ENR,其描述如下,如圖2-3。

圖2-3

可得圖2-3中的兩個紅圈即為我們需要開啟的外設(GPIOB、GPIOF)時鐘位,分別在第3位和第7位。具體代碼如下:

RCC->APB2ENR |= (1<<3) | (1<<7);t // 開啟GPIOB和GPIOF外設時鐘n

步驟4:

接下來需要配置GPIO引腳的輸出模式和輸出速度,這就用到了埠配置寄存器GPIOx_CRL或GPIOx_CRH。至於具體用哪個寄存器就得仔細看手冊了,CRL配置低8位引腳,CRH配置高8位引腳,這裡我們都有用到(PB0、PF7、PF8)。如圖2-4和圖2-5。

圖2-4

圖2-5

我們將其配置為通用推挽輸出模式(00)、最高速度10MHz(01)。

具體代碼如下:

/**n * @brief GPIOx_CRL和GPIOx_CRH寄存器配置函數n * @param GPIOx: x可以是A,B,C,D或者E,來選擇 GPIO 外設n * @param GPIO_num: 相應GPIO外設的引腳序號n * @retval 無n */nvoid GPIO_CR_L_H_Configuration(GPIO_TypeDef* GPIOx, int16_t GPIO_num){n if((GPIO_num >= 0) && (GPIO_num <= 7)){// 0~7引腳n // 清空控制GPIOx的GPIO_num引腳n GPIOx->CRL &= ~(0x0F << (4 * GPIO_num)); n // 配置GPIOx的GPIO_num引腳輸出模式為推挽輸出、最大速度為10MHzn GPIOx->CRL |= (1 << (4 * GPIO_num)); n }else{// 8~15引腳n GPIOx->CRH &= ~(0x0F << (4 * (GPIO_num - 8))); n GPIOx->CRH |= (1 << (4 * (GPIO_num - 8)));n }n}nnGPIO_CR_L_H_Configuration(GPIOB, 5);t//GPIOB5nGPIO_CR_L_H_Configuration(GPIOF, 7);t//GPIOF7ttttnGPIO_CR_L_H_Configuration(GPIOF, 8); //GPIOF8n n

步驟5:

配置好引腳模式後,可以控制引腳輸出電平的高低了,這就需要用到nGPIOx_ODR n

埠輸出數據寄存器了(也可以使用BSRR/BRR寄存器控制,有興趣可以自行查看參考手冊里相關寄存器描述)。查閱手冊,GPIOx_ODR寄存器描述如圖2-6。

圖2-6

由圖可知,其只有低16位有效,並且每一位對應一個IO,則圖中的紅圈位為我們要操作的位。以PB5(LED_R)為例,具體代碼如下:

/* GPIOB5(LED_R 紅) */nGPIOB->ODR &= ~(1<<5);ttt// 低電平亮nDelay(0x2FFFFF); // 延時nGPIOB->ODR |= (1<<5);ttt// 高電平滅 n

至此,流水燈實驗已經分析得差不多了,最終實現了一個循環亮滅的流水燈效果。至於庫函數實現流水燈,只要你理解了以上的寄存器實現,庫函數實現就是手到擒來的事了,這裡就不再分析了。

下面貼出實驗結果。

GPIO流水燈實驗結果—在線播放—優酷網,視頻高清在線觀看 http://v.youku.com/v_show/id_XMTgxNTM4NTI2OA==.html

流量不夠的童鞋可以看照片(雖然視頻只有4秒):

附:寄存器實現流水燈完整源碼

#include "stm32f10x.h"nnvoid Delay(__IO u32 nCount); nvoid GPIO_CR_L_H_Configuration(GPIO_TypeDef* GPIOx, int16_t GPIO_num);nnint main(void){n RCC->APB2ENR |= (1<<3) | (1<<7);t // 開啟GPIOB和GPIOF外設時鐘n GPIO_CR_L_H_Configuration(GPIOB, 5); // GPIOB5n GPIO_CR_L_H_Configuration(GPIOF, 7); // GPIOF7ttttn GPIO_CR_L_H_Configuration(GPIOF, 8); // GPIOF7n // 將相應外設位置高電平,以關閉所有LED燈n GPIOB->ODR |= (1<<5);ttn GPIOF->ODR |= (1<<7) | (1<<8);ntn while(1){n /* GPIOB5(LED_R 紅) */ntGPIOB->ODR &= ~(1<<5);ttt// 低電平亮ntDelay(0x2FFFFF); // 延時ntGPIOB->ODR |= (1<<5);ttt// 高電平滅 nttnt/* GPIOF7(LED1) */ntGPIOF->ODR &= ~(1<<7);ntDelay(0x2FFFFF);ntGPIOF->ODR |= (1<<7);tnnt/* GPIOF8(LED2) */ntGPIOF->ODR &= ~(1<<8);ntDelay(0x2FFFFF);ntGPIOF->ODR |= (1<<8);n }n}nn/**n * @brief GPIOx_CRL和GPIOx_CRH寄存器配置函數n * @param GPIOx: x可以是A,B,C,D或者E,來選擇 GPIO 外設n * @param GPIO_num: 相應GPIO外設的引腳序號n * @retval 無n */nvoid GPIO_CR_L_H_Configuration(GPIO_TypeDef* GPIOx, int16_t GPIO_num){n if((GPIO_num >= 0) && (GPIO_num <= 7)){// 0~7引腳n // 清空控制GPIOx的GPIO_num引腳n GPIOx->CRL &= ~(0x0F << (4 * GPIO_num)); n // 配置GPIOx的GPIO_num引腳輸出模式為推挽輸出、最大速度為10MHzn GPIOx->CRL |= (1 << (4 * GPIO_num)); n }else{// 8~15引腳n GPIOx->CRH &= ~(0x0F << (4 * (GPIO_num - 8))); n GPIOx->CRH |= (1 << (4 * (GPIO_num - 8)));n }n}nn/* 簡易延時函數 */nvoid Delay(__IO uint32_t nCount){n for(; nCount != 0; nCount--);n}n

註:本文所有圖片除最後的結果照片,其他均截圖自《STM32F10x-中文參考手冊》

--------------------------------------------------------------------

文章首發於知乎,轉載請私信,並註明原文出處。


推薦閱讀:

CPU寄存器到底有多大?《深入理解計算機系統》說大概有幾百位元組,可是彙編課上卻說理論上有64kb
stm32中的「位帶操作」淺析

TAG:寄存器 | LED |