您的位置:網(wǎng)站首頁(yè) > 電器維修資料網(wǎng) > 正文 >
STM32單片機(jī)基礎(chǔ)學(xué)習(xí):從勉強(qiáng)看懂一行程序到IO口研究
來(lái)源: 日期:2013-11-29 9:19:06 人氣:標(biāo)簽:
剛好勉勉強(qiáng)強(qiáng)看懂一行程序
繼續(xù)學(xué)習(xí)中,先把開(kāi)發(fā)板自帶一個(gè)例子做了些精簡(jiǎn),以免看得嚇人。。。。
就是這個(gè),讓portd上接的4個(gè)led分別點(diǎn)亮。
開(kāi)始研究代碼
int main(void)
{
init_all_periph();
。。.。。.
看到這一行,開(kāi)始跟蹤,于是又看到了下面的內(nèi)容
void init_all_periph(void)
{
rcc_configuration();
。。.。。.
繼續(xù)跟蹤
void rcc_configuration(void)
{
systeminit();
。。.。。.
這行代碼在system_stm32f10x.c中找到了。
void systeminit (void)
{
/* reset the rcc clock configuration to the default reset state(for debug purpose) */
/* set hsion bit */
rcc-》cr |= (uint32_t)0x00000001;
/* reset sw, hpre, ppre1, ppre2, adcpre and mco bits */
#ifndef stm32f10x_cl
rcc-》cfgr &= (uint32_t)0xf8ff0000;
#else
rcc-》cfgr &= (uint32_t)0xf0ff0000;
#endif /* stm32f10x_cl */
/* reset hseon, csson and pllon bits */
rcc-》cr &= (uint32_t)0xfef6ffff;
/* reset hsebyp bit */
rcc-》cr &= (uint32_t)0xfffbffff;
/* reset pllsrc, pllxtpre, pllmul and usbpre/otgfspre bits */
rcc-》cfgr &= (uint32_t)0xff80ffff;
#ifndef stm32f10x_cl
/* disable all interrupts and clear pending bits */
rcc-》cir = 0x009f0000;
#else
/* reset pll2on and pll3on bits */
rcc-》cr &= (uint32_t)0xebffffff;
/* disable all interrupts and clear pending bits */
rcc-》cir = 0x00ff0000;
/* reset cfgr2 register */
rcc-》cfgr2 = 0x00000000;
#endif /* stm32f10x_cl */
/* configure the system clock frequency, hclk, pclk2 and pclk1 prescalers */
/* configure the flash latency cycles and enable prefetch buffer */
setsysclock();
}
這一長(zhǎng)串的又是什么,如何來(lái)用呢?看來(lái),偷懶是不成的了,只能回過(guò)頭去研究stm32的時(shí)鐘構(gòu)成了。
相當(dāng)?shù)膹?fù)雜。
系統(tǒng)的時(shí)鐘可以有3個(gè)來(lái)源:內(nèi)部時(shí)鐘hsi,外部時(shí)鐘hse,或者pll(鎖相環(huán)模塊)的輸出。它們由rcc_cfgr寄存器中的sw來(lái)選擇。
sw(1:0):系統(tǒng)時(shí)鐘切換
由軟件置’1’或清’0’來(lái)選擇系統(tǒng)時(shí)鐘源。 在從停止或待機(jī)模式中返回時(shí)或直接或間接作為系統(tǒng)時(shí)鐘的hse出現(xiàn)故障時(shí),由硬件強(qiáng)制選擇hsi作為系統(tǒng)時(shí)鐘(如果時(shí)鐘安全系統(tǒng)已經(jīng)啟動(dòng))
00:hsi作為系統(tǒng)時(shí)鐘;
01:hse作為系統(tǒng)時(shí)鐘;
10:pll輸出作為系統(tǒng)時(shí)鐘;
11:不可用。
////////////////////////////////////////////////////////////////////
pll的輸出直接送到usb模塊,經(jīng)過(guò)適當(dāng)?shù)姆诸l后得到48m的頻率供usb模塊使用。
系統(tǒng)時(shí)鐘的一路被直接送到i2s模塊;另一路經(jīng)過(guò)ahb分頻后送出,送往各個(gè)系統(tǒng),其中直接送往sdi,fmsc,ahb總線;8分頻后作為系統(tǒng)定時(shí)器時(shí)鐘;經(jīng)過(guò)apb1分頻分別控制plk1、定時(shí)器tim2~tim7;經(jīng)過(guò)apb2分頻分別控制plk2、定時(shí)器tim1~tim8、再經(jīng)分頻控制adc;
由此可知,stm32f10x芯片的時(shí)鐘比之于51、avr、pic等8位機(jī)要復(fù)雜復(fù)多,因此,我們立足于對(duì)著芯片手冊(cè)來(lái)解讀程序,力求知道這些程序代碼如何使用,為何這么樣使用,如果自己要改,可以修改哪些部分,以便自己使用時(shí)可以得心應(yīng)手。
單步執(zhí)行,看一看哪些代碼被執(zhí)行了。
/* reset the rcc clock configuration to the default reset state(for debug purpose) */
/* set hsion bit */
rcc-》cr |= (uint32_t)0x00000001;
這是rcc_cr寄存器,由圖可見(jiàn),hsion是其bit 0位。
hsion:內(nèi)部高速時(shí)鐘使能
由軟件置’1’或清零。
當(dāng)從待機(jī)和停止模式返回或用作系統(tǒng)時(shí)鐘的外部4-25mhz時(shí)鐘發(fā)生故障時(shí),該位由硬件置’1’來(lái)啟動(dòng)內(nèi)部8mhz的rc振蕩器。當(dāng)內(nèi)部8mhz時(shí)鐘被直接或間接地用作或被選擇將要作為系統(tǒng)時(shí)鐘時(shí),該位不能被清零。
0:內(nèi)部8mhz時(shí)鐘關(guān)閉;
1:內(nèi)部8mhz時(shí)鐘開(kāi)啟。
///////////////////////////////////////////////////////////////////////
/* reset sw, hpre, ppre1, ppre2, adcpre and mco bits */
#ifndef stm32f10x_cl
rcc-》cfgr &= (uint32_t)0xf8ff0000;
這是rcc_cfgr寄存器
該行程序清零了mc0[2:0]這三位,和adcpre[1:0],ppre2[2:0],ppre1[2:0],hpre[3:0],sws[1:0]和sw[1:0]這16位。
/*
mco: 微控制器時(shí)鐘輸出,由軟件置’1’或清零。
0xx:沒(méi)有時(shí)鐘輸出;
100:系統(tǒng)時(shí)鐘(sysclk)輸出;
101:內(nèi)部8mhz的rc振蕩器時(shí)鐘輸出;
110:外部4-25mhz振蕩器時(shí)鐘輸出;
111:pll時(shí)鐘2分頻后輸出。
*/
/* reset hseon, csson and pllon bits */
rcc-》cr &= (uint32_t)0xfef6ffff;
清零了pllon,hsebyp,hserdy這3位。
/* reset hsebyp bit */
rcc-》cr &= (uint32_t)0xfffbffff;
清零了hsebyp位 ///???為什么不一次寫(xiě)??
hsebyp:外部高速時(shí)鐘旁路,在調(diào)試模式下由軟件置’1’或清零來(lái)旁路外部晶體振蕩器。只有在外部4-25mhz振蕩器關(guān)閉的情況下,才能寫(xiě)入該位。
0:外部4-25mhz振蕩器沒(méi)有旁路;
1:外部4-25mhz外部晶體振蕩器被旁路。
所以要先清hseon位,再清該位。
/* reset pllsrc, pllxtpre, pllmul and usbpre/otgfspre bits */
rcc-》cfgr &= (uint32_t)0xff80ffff;
清零了:usbpre,pllmul,pllxtpr,pllsrc共7位
/* disable all interrupts and clear pending bits */
rcc-》cir = 0x009f0000;
////這個(gè)暫不解讀
setsysclock();
跟蹤進(jìn)入該函數(shù),可見(jiàn)一連串的條件編譯:
單步運(yùn)行,執(zhí)行的是:
#elif defined sysclk_freq_72mhz
setsysclockto72();
為何執(zhí)行該行呢,找到sysclk_preq_**的相關(guān)定義,如下圖所示。
這樣就得到了我們所要的一個(gè)結(jié)論:如果要更改系統(tǒng)工作頻率,只需要在這里更改就可以了。
可以繼續(xù)跟蹤進(jìn)入這個(gè)函數(shù)來(lái)觀察如何將工作頻率設(shè)定為72mhz的。
static void setsysclockto72(void)
{
__io uint32_t startupcounter = 0, hsestatus = 0;
/* sysclk, hclk, pclk2 and pclk1 configuration ---------------------------*/
/* enable hse */
rcc-》cr |= ((uint32_t)rcc_cr_hseon);
//開(kāi)啟hse
/* wait till hse is ready and if time out is reached exit */
do
{
hsestatus = rcc-》cr & rcc_cr_hserdy;
startupcounter++;
} while((hsestatus == 0) && (startupcounter != hsestartup_timeout));
//等待hse確實(shí)可用,這有個(gè)標(biāo)志,即rcc_cr寄存器中的hserdy位(bit 17),這個(gè)等待不會(huì)無(wú)限長(zhǎng),有個(gè)超時(shí)策略,即每循環(huán)一次計(jì)數(shù)器加1,如果計(jì)數(shù)的次數(shù)超過(guò)hsestartup_timeout,就退出循環(huán),而這個(gè)hsestartup_timeout在stm32f10x.h中定義,
#define hsestartup_timeout ((uint16_t)0x0500) /*!《 time out for hse start up */
///////////////////////////////////////////////////////////////////////////////////////////////
if ((rcc-》cr & rcc_cr_hserdy) != reset)
{
hsestatus = (uint32_t)0x01;
}
else
{
hsestatus = (uint32_t)0x00;
}
///再次判斷hserdy標(biāo)志位,并據(jù)此給hsestatus變量賦值。
if (hsestatus == (uint32_t)0x01)
{
/* enable prefetch buffer */
flash-》acr |= flash_acr_prftbe;
/* flash 2 wait state */
flash-》acr &= (uint32_t)((uint32_t)~flash_acr_latency);
flash-》acr |= (uint32_t)flash_acr_latency_2;
/* hclk = sysclk */
rcc-》cfgr |= (uint32_t)rcc_cfgr_hpre_div1;
//找到定義: #define rcc_cfgr_hpre_div1 ((uint32_t)0x00000000) /*!《 sysclk not divided */
/* pclk2 = hclk */
rcc-》cfgr |= (uint32_t)rcc_cfgr_ppre2_div1;
//找到定義:#define rcc_cfgr_ppre2_div1 ((uint32_t)0x00000000) /*!《 hclk not divided */
/* pclk1 = hclk */
rcc-》cfgr |= (uint32_t)rcc_cfgr_ppre1_div2;
//找到定義:#define rcc_cfgr_ppre1_div2 ((uint32_t)0x00000400) /*!《 hclk divided by 2 */
#ifdef stm32f10x_cl
……
#else
/* pll configuration: pllclk = hse * 9 = 72 mhz */
rcc-》cfgr &= (uint32_t)((uint32_t)~(rcc_cfgr_pllsrc | rcc_cfgr_pllxtpre |
rcc_cfgr_pllmull));
rcc-》cfgr |= (uint32_t)(rcc_cfgr_pllsrc_hse | rcc_cfgr_pllmull9);
#endif /* stm32f10x_cl */
//以上是設(shè)定pll的倍頻系數(shù)為9,也就是說(shuō),這個(gè)72m是在外部晶振為8m時(shí)得到的。
/* enable pll */
rcc-》cr |= rcc_cr_pllon;
/* wait till pll is ready */
while((rcc-》cr & rcc_cr_pllrdy) == 0)
{
}
/* select pll as system clock source */
rcc-》cfgr &= (uint32_t)((uint32_t)~(rcc_cfgr_sw));
rcc-》cfgr |= (uint32_t)rcc_cfgr_sw_pll;
/* wait till pll is used as system clock source */
while ((rcc-》cfgr & (uint32_t)rcc_cfgr_sws) != (uint32_t)0x08)
{
}
}
else
{ /* if hse fails to start-up, the application will have wrong clock
configuration. user can add here some code to deal with this error */
/* go to infinite loop */
while (1)
{
}
}
}
至此,我們可以歸納幾條:
(1) 時(shí)鐘源有3個(gè)
(2) 開(kāi)機(jī)時(shí)默認(rèn)是hsi起作用,可以配置為所要求的任意一個(gè)時(shí)鐘
(3) 配置時(shí)必須按一定的順序來(lái)打開(kāi)或都關(guān)閉一些位,并且各時(shí)鐘起作用有一定的時(shí)間,因此要利用芯片內(nèi)部的標(biāo)志位來(lái)判斷是否可以執(zhí)行下一步。
(4) 如果外部時(shí)鐘、pll輸出失效,系統(tǒng)可以自動(dòng)回復(fù)到hsi(開(kāi)啟時(shí)鐘安全系統(tǒng))
(5) hsi的頻率準(zhǔn)確度可以達(dá)到+/- 1%,如果有必要時(shí),還可以用程序來(lái)調(diào)整這個(gè)頻率,可調(diào)的范圍大致在200khz左右。
后讓我們來(lái)感受一下勞動(dòng)的果實(shí)吧--試著改改頻率看有何反應(yīng)。
為查看更改后的效果,先記錄更改前的數(shù)據(jù)。將調(diào)試切換到仿真,在第一條:
delay(0xaffff);
指令執(zhí)行前后,分別記錄下status和sec
status:2507 3606995
sec:0.00022749 0.05028982
將振蕩頻率更改為36mhz,即
。。.
#define sysclk_freq_36mhz 36000000 //去掉該行的注釋
/* #define sysclk_freq_48mhz 48000000 */
/* #define sysclk_freq_56mhz 56000000 */
/*#define sysclk_freq_72mhz 72000000*/ //將該行加上注釋
再次運(yùn)行,結(jié)果如下:
status:2506 3606994
sec:0.00008478 0.10036276
基本上是延時(shí)時(shí)間長(zhǎng)了一倍。改成硬件仿真,將代碼寫(xiě)入板子,可以看到led閃爍的頻率明顯變慢了。
io研究
前面的例子研究了時(shí)鐘,接下來(lái)就來(lái)了解一下引腳的情況
main.c中,有關(guān)i/o口的配置代碼如下:
void gpio_configuration(void)
{
gpio_inittypedef gpio_initstructure;
/* configure io connected to ld1, ld2, ld3 and ld4 leds *********************/
gpio_initstructure.gpio_pin = gpio_pin_8 | gpio_pin_9 | gpio_pin_10 | gpio_pin_11;
gpio_initstructure.gpio_mode = gpio_mode_out_pp;
gpio_initstructure.gpio_speed = gpio_speed_50mhz;
gpio_init(gpiod, &gpio_initstructure);
這幾行代碼是將gpiod的第8,9,10和11引腳配置成輸出,并且還可以設(shè)定輸出引腳的速度(驅(qū)動(dòng)能力?),這里設(shè)定為 50mhz,這應(yīng)該是常用的,還有可以設(shè)置為2mhz的。那么如何將引腳設(shè)置成輸入呢?查看電路原理圖,gpiod.0~gpio.4是接一個(gè)搖桿的5個(gè)按鈕的,因此,下面嘗試著將它們?cè)O(shè)置成為輸入端。
gpio_initstructure.gpio_pin=gpio_pin_0|gpio_pin_1|gpio_pin_2|gpio_pin_3|gpio_pin_4;
gpio_initstructure.gpio_mode = gpio_mode_in_floating;
gpio_init(gpiod, &gpio_initstructure);
第1行和第3行完全是照抄,第2行那個(gè)gpio_mode_in_floating是在stm32f10x_gpio.h中找到的。
當(dāng)然是因?yàn)檫@里還有g(shù)pio_mode_out_pp,所以猜測(cè)應(yīng)該是它了。至于還有其他那么多的符號(hào)就不管了。
定義完成,編譯完全通過(guò),那就接下來(lái)準(zhǔn)備完成下面的代碼了。
int main(void)
{
init_all_periph();
while(1)
{ if( gpio_readinputdatabit(gpiod,gpio_pin_0)) //1
{ gpio_resetbits(gpiod, gpio_pin_8);
}
else
{ /* turn on ld1 */
gpio_setbits(gpiod, gpio_pin_8);
/* insert delay */
}
。。.。。.
標(biāo)號(hào)為1的行顯然其作用是判斷gpiod.0引腳是0還是1。這個(gè)函數(shù)是在stm32f10x_gpio.c中找到的。
uint8_t gpio_readinputdatabit(gpio_typedef* gpiox, uint16_t gpio_pin)
{
uint8_t bitstatus = 0x00;
/* check the parameters */
assert_param(is_gpio_all_periph(gpiox));
assert_param(is_get_gpio_pin(gpio_pin));
if ((gpiox-》idr & gpio_pin) != (uint32_t)bit_reset)
{
bitstatus = (uint8_t)bit_set;
}
else
{
bitstatus = (uint8_t)bit_reset;
}
return bitstatus;
}
雖然程序還有很多符號(hào)看不懂(沒(méi)有去查),但憑感覺(jué)它應(yīng)該是對(duì)某一個(gè)引腳的狀態(tài)進(jìn)行判斷,因?yàn)檫@個(gè)函數(shù)的類(lèi)型是uint8_t,估計(jì)stm32沒(méi)有bit型函數(shù)(需要驗(yàn)證),所以就用了uint8_t型了),如果是讀的端口的值,應(yīng)該用uint16_t型。這一點(diǎn)在下面也可以得到部分的驗(yàn)證:
uint16_t gpio_readinputdata(gpio_typedef* gpiox)
uint16_t gpio_readoutputdata(gpio_typedef* gpiox)
這些函數(shù)是讀引腳及輸出寄存器的數(shù)據(jù)的。
再次編譯,也是順利通過(guò),依法炮制,將其他三個(gè)引腳輸入控制led的代碼也寫(xiě)上,為保險(xiǎn)起見(jiàn),先用軟件仿真,免得反復(fù)擦寫(xiě)flash(順便說(shuō)一句,目前還沒(méi)有搞定將代碼寫(xiě)入ram及從ram中執(zhí)行)
進(jìn)入仿真后打開(kāi)外圍部件接口,單步執(zhí)行,果然如同設(shè)想那樣運(yùn)作了,單擊pins 0后面的勾,再次運(yùn)行,果然pin8后面的勾沒(méi)了。做到這里,就感覺(jué)到用keil的好處了,這塊熟啊,幾乎沒(méi)有花時(shí)間在上面,一用就成了。
- 1
- 2
- 下一頁(yè)
【看看這篇文章在百度的收錄情況】