STM32日志之總結(jié)篇1--跑馬燈實(shí)驗(yàn)
//======================================================//
**基于學(xué)習(xí)STM32有一段時(shí)間了,特意寫下一篇關(guān)于一個(gè)簡(jiǎn)單的跑馬燈
的例程,梳理思路,也希望我自己的理解能幫到一些學(xué)習(xí)STM32的初學(xué)者
//======================================================//
首先,GPIO的配置種類有8種。分別為模擬輸入、浮空輸入,上拉輸入、下拉輸入、開(kāi)漏輸出、推挽輸出、復(fù)用開(kāi)漏輸出、復(fù)用推挽輸出。
下面將以實(shí)例的方式講解GPIO的設(shè)置及實(shí)現(xiàn)過(guò)程。
事例1:跑馬燈實(shí)驗(yàn)
跑馬燈實(shí)驗(yàn)的功能:LED燈進(jìn)行有規(guī)律閃爍。(下面的LED燈的數(shù)量都為2個(gè),所以關(guān)于寄存器的配置也將以兩組的方式進(jìn)行講解)
首先要知道普通的IO就兩種功能一個(gè)為輸入,一個(gè)為輸出。然后再以輸入和輸出細(xì)分為以哪種模式輸出、以哪種模式輸入。要設(shè)置IO的模式這時(shí)就要使用到寄存器進(jìn)行設(shè)置,STM32的IO端口一般由7個(gè)寄存器來(lái)進(jìn)行控制。
分別為
(1)配置模式的2個(gè)32位的端口配置寄存器 CRL 和 CRH;
(2)2 個(gè) 32 位的數(shù)據(jù)寄存器 IDR 和 ODR;
(3)1 個(gè) 32 位的置位/復(fù)位寄存器BSRR;
(4)1個(gè) 16 位的復(fù)位寄存器 BRR;
(5)1 個(gè) 32 位的鎖存寄存器 LCKR;
而我們常用的 IO 端口寄存器只有 4 個(gè):CRL、CRH、IDR、ODR。
首先,一般一個(gè)程序的開(kāi)始都會(huì)從入口main函數(shù)開(kāi)始執(zhí)行,而一個(gè)功能的實(shí)現(xiàn)之前都需要做一些準(zhǔn)備工作,當(dāng)然跑馬燈也不例外,在程序執(zhí)行前,IO需要初始化,而初始化要做的就是對(duì)程序需要用到的GPIO的寄存器進(jìn)行配置。然后設(shè)定輸出設(shè)備的初始狀態(tài),即跑馬燈的初始狀態(tài)。一般程序在執(zhí)行前,輸出設(shè)備都會(huì)是處于關(guān)閉狀態(tài)。最后增加延時(shí)函數(shù),讓LED燈在亮與滅之間有一段時(shí)間的狀態(tài)保留,不然時(shí)間過(guò)快,人類的視覺(jué)根本捕抓它的狀態(tài)變化。
下面來(lái)講一下實(shí)現(xiàn)跑馬燈效果的IO要怎么配置。首先先說(shuō)一下使用LED來(lái)進(jìn)行跑馬功能的話只需要用到RCC->APB2ENR、GPIOX->CRL、GPIOX->ODR三個(gè)寄存器(GPIOX中的X表示為GPIO的組別,例如GPIOB表示在GPIO的B組上,因?yàn)橐话銌纹瑱C(jī)上的GPIO一般都會(huì)有很多組的,這樣也只是為了區(qū)分這些IO罷了)
CRL 和 CRH 控制著每個(gè) IO 口的模式及輸出速率。
這里需要注意的是在配置GPIO前,都需要先使能該GPIO的時(shí)鐘?。赡苡腥藭?huì)問(wèn)“為什么一定要是使能時(shí)鐘呢?”答案是:GPIO也是外設(shè)的一種,然后外設(shè)是需要提供時(shí)鐘信號(hào)工作,以便于設(shè)置GPIO的數(shù)據(jù)傳輸速度的高速/低速輸出,所以有關(guān)數(shù)據(jù)的傳輸都是在時(shí)鐘信號(hào)的基礎(chǔ)上的。51單片的IO口也有時(shí)鐘,只是為了方便,默認(rèn)開(kāi)啟的,ST的為了更好的控制功耗,電路上做的可以選擇開(kāi)關(guān)時(shí)鐘,降低功耗。補(bǔ)充網(wǎng)友的答案:“寄存器是基于觸發(fā)器的,觸發(fā)器的賦值是一定需要時(shí)鐘的,而寄存器的時(shí)鐘是由總線時(shí)鐘提供的,就是說(shuō)沒(méi)有總線時(shí)鐘的話,你給寄存器值它是不會(huì)讀入的”)
然后設(shè)置兩個(gè)LED的GPIO的模式,因?yàn)長(zhǎng)ED是輸出設(shè)備,所以GPIO將設(shè)置成輸出模式,其中配置模式要用到的寄存器為配置寄存器CRL和CRH。其中CRH為高位寄存器,CRL為低位寄存器。(有人可能有疑問(wèn)為什么用的是32位的寄存器為什么還要為兩個(gè)寄存器來(lái)管理呢?難道是32位還不夠配置嗎?答案是:是的,不夠用。配置寄存器對(duì)一個(gè)GPIO的配置要用4個(gè)位來(lái)完成。一組GPIO有16個(gè)引腳,一個(gè)引腳要4位的話,那么16個(gè)引腳就要16*4=64,那么就要2個(gè)32位的寄存器來(lái)實(shí)現(xiàn),所以就干脆把高八位給一個(gè)寄存器,低八位給另外一個(gè)寄存器,這就出現(xiàn)了CRL和CRH。也許有人還會(huì)有疑問(wèn)說(shuō)為什么一定要用4位來(lái)對(duì)一個(gè)引腳的配置呢?答案是肯定的,因?yàn)榍懊嬲f(shuō)了,對(duì)GPIO的配置首先配置為輸入模式或者輸出模式,然后再對(duì)輸入、輸出配置為哪種輸出,哪種輸入,是推挽輸出呢,還是開(kāi)漏輸入呢,這都是需要至少2個(gè)位來(lái)進(jìn)行配置,而且輸入、輸出模式各有4種模式,共8中,這樣要以最少的資源來(lái)實(shí)現(xiàn)的話,用00、01、10、11這種2個(gè)位來(lái)實(shí)現(xiàn)是最省資源的,所以2*2=4,所以對(duì)于1個(gè)GPIO的模式的配置至少也就要4位了。)
接著就是查看LED的GPIO的引腳接在IC哪個(gè)引腳上。怎么查看LED的引腳到底接在哪呢?這個(gè)要看開(kāi)發(fā)板的原理圖,不同的板子來(lái)說(shuō)或者對(duì)于不同的商家來(lái)說(shuō)在GPIO的引腳接線上會(huì)有不同,以我的開(kāi)發(fā)板的原理圖可以發(fā)現(xiàn)LED燈的GPIO分別接在了GPIOB的pin5和GPIOE的pin5上,根據(jù)電路性質(zhì),一般會(huì)將LED設(shè)置為推挽輸出模式(可能這時(shí)就會(huì)有人問(wèn)了,為什么就是推挽呢?為什么不是開(kāi)漏呢?我的理解是:這是有電路性質(zhì)決定的,推挽顧名思義就是灌電流與拉電流都可以輸出,通俗地講就是輸出高低電平。而開(kāi)漏是一直輸出低電平,當(dāng)你要輸出高電平的時(shí)候需要加上拉)
通過(guò)查閱寄存器的數(shù)據(jù)手冊(cè)可知,00位通用推挽模式,那么
//======================================================//
GPIOB->CRL |= 3 << 20;//設(shè)置模數(shù)數(shù)據(jù)傳輸速率
GPIOB->CRL &=~(3 << 22);//設(shè)置GPIO模式
//======================================================//
這樣就把GPIOB組的pin5設(shè)置成了通用推挽模式,前面說(shuō)了一個(gè)引腳需要4個(gè)位來(lái)配置,查看寄存器數(shù)據(jù)手冊(cè)可看到,前面2個(gè)位是設(shè)置通用推挽模式的,后面2個(gè)位是基于這種模式的數(shù)據(jù)傳輸速率,在這里我們懸著的是“最大速度50MHz”,而GPIOB->CRL|=3<<20;就是設(shè)置數(shù)據(jù)傳輸速率為最大速度50MHz,GPIOB->CRL&=~(3<<22);為設(shè)置GPIOB的pin5為通用推挽模式。
最后就是設(shè)置LED的初始狀態(tài)了,再說(shuō)之前先說(shuō)一下ODR寄存器。
ODR是一個(gè)端口輸出數(shù)據(jù)寄存器,也只用了低16位。該寄存器為可讀寫,從該寄存器讀出來(lái)的數(shù)據(jù)可以用于判斷當(dāng)前IO 口的輸出狀態(tài)。而向該寄存器寫數(shù)據(jù),則可以控制某個(gè) IO 口的輸出電平。
所以在配置LED初始化的時(shí)候就需要調(diào)用這個(gè)寄存器設(shè)置LED的初始狀態(tài)。
//======================================================//
GPIOE->ODR|=1<<5;//設(shè)置LED的初始化狀態(tài)為1(1為到電平,亮滅有其電路決定,我的開(kāi)發(fā)板由于接了上拉,使得低電平才是有效的,即0為L(zhǎng)ED燈亮,1為L(zhǎng)ED燈滅)
//======================================================//
這樣初始化函數(shù)就完成了。
具體初始化代碼如下:
//=======================led.c===============================//
#include "stm32f10x.h"
#include "led.h"
void led_Init(void)
{
RCC ->APB2ENR |= 1 << 3;//使能 PORTB 時(shí)鐘
RCC ->APB2ENR |= 1 << 6;//使能 PORTE 時(shí)鐘
GPIOB ->CRL |= 3 << 20;
GPIOB ->CRL &=~ (3 << 22);//PB.5 推挽輸出
GPIOB ->ODR |= 1 << 5;//PB.5 輸出高
GPIOE ->CRL |= 3 << 20;
GPIOE ->CRL &= ~(3 << 22);//PE.5 推挽輸出
GPIOE ->ODR |= 1 << 5;//PE.5 輸出高
}
//======================================================//
其頭文件如下:
//=========================led.h=============================//
#ifndef __LED_H_
#define __LED_H_
extern void led_Init(void);
#endif
//======================================================//
接下來(lái)說(shuō)一下初始化好了,主函數(shù)要怎么寫。
第一需要的是點(diǎn)亮LED的語(yǔ)句,在初始化中有提到,設(shè)置LED初始化狀態(tài)即:GPIOB ->ODR |= 1 << 5;//PB.5 輸出高
那么同時(shí)也能使用這句來(lái)進(jìn)行LED的亮滅控制。代碼如下:
//==========================main.c============================//
#include "stm32f10x.h"
#include "led.h"
int main(void)
{
int i=0;
led_Init();//LED初始化
while(1)
{
GPIOB ->ODR &= ~ (1 << 5);//PB.5輸出低電平,即點(diǎn)亮LED
GPIOE ->ODR &= ~(1 << 5);//PB.5輸出低電平,即點(diǎn)亮LED
for(i=0;i<1000000;i++);//延時(shí)
GPIOB ->ODR |= 1 << 5;//PE.5輸出高電平,即滅掉LED
GPIOE ->ODR |= 1 << 5;//PB.5輸出高電平,即滅掉LED
for(i=0;i<1000000;i++);//延時(shí)
}
}
//======================================================//
整個(gè)跑馬燈的工程就只需這三個(gè)文件,就能簡(jiǎn)單實(shí)現(xiàn)跑馬的功能,如果你想要延時(shí)的時(shí)間是規(guī)律的,那么你就要去使用定時(shí)器了。因?yàn)槎〞r(shí)器定時(shí)的時(shí)間是準(zhǔn)時(shí)的,軟件延時(shí)第一消耗CPU的資源,然后時(shí)間也不夠精確,所以一般在對(duì)時(shí)間要求沒(méi)有很精確的要求是才會(huì)使用軟件延時(shí)的做法。