從51開(kāi)始,單片機(jī)玩了很長(zhǎng)時(shí)間了,有51,PIC,AVR等等,早就想跟潮流玩玩ARM,但一直沒(méi)有開(kāi)始,原因-----不知道玩了ARM可以做什么(對(duì)我自己而言)。如果為學(xué)習(xí)而學(xué)習(xí),肯定學(xué)不好。然后cortex-m3出來(lái)了,據(jù)說(shuō),這東西可以替代單片機(jī),于是馬上開(kāi)始關(guān)注。也在第一時(shí)間開(kāi)始學(xué)習(xí),可惜一開(kāi)始就有點(diǎn)站錯(cuò)了隊(duì),選錯(cuò)了型(仍是對(duì)我自己而言)。我希望這種芯片應(yīng)該是滿(mǎn)大街都是,隨便哪里都可以買(mǎi)得到,但我選的第一種顯然做不到。為此,大概浪費(fèi)了一年多時(shí)間吧,現(xiàn)在,回到對(duì)我來(lái)說(shuō)是正確的道路上來(lái)啦,邊學(xué)邊寫(xiě)點(diǎn)東西。
這里寫(xiě)的是我的學(xué)習(xí)的過(guò)程,顯然,很多時(shí)候會(huì)是不全面的,不系統(tǒng)的,感悟式的,甚至有時(shí)會(huì)是錯(cuò)誤的,有些做法會(huì)是不專(zhuān)業(yè)的。那么,為什么我還要寫(xiě)呢?這是一個(gè)有趣的問(wèn)題,它甚至涉及到博客為什么要存在的問(wèn)題。顯然,博客里面的寫(xiě)的東西,其正確性、權(quán)威性大多沒(méi)法和書(shū)比,可為什么博客會(huì)存在呢?理由很多,我非專(zhuān)家,只說(shuō)作為一個(gè)學(xué)習(xí)32位單片機(jī)的工程師角度來(lái)分享整個(gè)學(xué)習(xí)過(guò)程,整理成一個(gè)學(xué)習(xí)手記,也便于以后文檔備份。
本章節(jié)將學(xué)習(xí)
一、認(rèn)識(shí)ADC兼進(jìn)一步看懂STM的庫(kù)
ADC是多少位的?
12位
ADC有多少個(gè)?
1個(gè)、2個(gè)或多至3個(gè),視不同的器件而不同;每個(gè)又有多個(gè)通道。
關(guān)于通道的名堂:
10.3.3 通道選擇
有16個(gè)多路通道。可以把轉(zhuǎn)換分成兩組:規(guī)則的和注入的。在任意多個(gè)通道上以任意順序進(jìn)行的一系列轉(zhuǎn)換構(gòu)成成組轉(zhuǎn)換。例如,可以如下順序完成轉(zhuǎn)換:通道3、通道8、通道2、通道2、通道0、通道2、通道2、通道15。
● 規(guī)則組由多達(dá)16個(gè)轉(zhuǎn)換組成。規(guī)則通道和它們的轉(zhuǎn)換順序在ADC_SQRx寄存器中選擇。規(guī)則組中轉(zhuǎn)換的總數(shù)寫(xiě)入ADC_SQR1寄存器的L[3:0]位中。
● 注入組由多達(dá)4個(gè)轉(zhuǎn)換組成。注入通道和它們的轉(zhuǎn)換順序在ADC_JSQR寄存器中選擇。注入組里的轉(zhuǎn)換總數(shù)目必須寫(xiě)入ADC_JSQR寄存器的L[1:0]位中。
它們有什么區(qū)別:
l 不同的組轉(zhuǎn)換后保存數(shù)據(jù)的地方不一樣,產(chǎn)生的中斷標(biāo)志不一樣。
l 在掃描模式下,規(guī)則組會(huì)有能力把各通道數(shù)據(jù)通過(guò)DMA傳給SRAM,而注入組的數(shù)據(jù)總是存在在ADC_JDRx中。
還有其他的一些區(qū)別,這里暫不一一羅列。
ST為什么這么樣來(lái)設(shè)計(jì)AD轉(zhuǎn)換,肯定是有理由的,但是我不知道,因此,我也就難以深入地理解AD轉(zhuǎn)換的各種模式。這也就是說(shuō),對(duì)于知識(shí)的理解,要把它放在其應(yīng)用背景中去學(xué)習(xí)才能學(xué)得好。因此,其他知識(shí)積累得越多,學(xué)起來(lái)也就越快,這也就是所謂的“功底”問(wèn)題。某人功底深厚,意味著他見(jiàn)多識(shí)廣,遇到的事情多,能夠很快找到處理某件事情的“原型”。當(dāng)然,也有一些人抽象學(xué)習(xí)能力極強(qiáng),就算找不到“原型”,他也能學(xué)得很好。基本上,這類(lèi)人的科學(xué)素養(yǎng)更高一些,在工程師、工科類(lèi)學(xué)生中并不多見(jiàn)。
閑話(huà)少說(shuō),下面來(lái)看怎么樣來(lái)使用AD轉(zhuǎn)換器?
以一段源程序?yàn)槔謩e來(lái)解讀,同時(shí)進(jìn)一步理解STM32中有關(guān)符號(hào)的含義,相信以后再讀庫(kù)源程序,定能更上一層樓。
為看得清楚一些,以下代碼用一種顏色表示。
/* ADC1 開(kāi)始準(zhǔn)備配置*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
/*設(shè)置ADC-》CR1的19:16,確定ADC工作模式,一共有10種工作模式
#define ADC_Mode_Independent ((uint32_t)0x00000000) 0000:獨(dú)立模式
#define ADC_Mode_RegInjecSimult ((uint32_t)0x00010000) 0001:混合的同步規(guī)則+注入同步模式
#define ADC_Mode_RegSimult_AlterTrig ((uint32_t)0x00020000) 0010:混合的同步規(guī)則+交替觸發(fā)模式
#define ADC_Mode_InjecSimult_FastInterl ((uint32_t)0x00030000) 0011:混合同步注入+快速交替模式
#define ADC_Mode_InjecSimult_SlowInterl ((uint32_t)0x00040000) 0100:混合同步注入+慢速交替模式
#define ADC_Mode_InjecSimult ((uint32_t)0x00050000) 0101:注入同步模式
#define ADC_Mode_RegSimult ((uint32_t)0x00060000) 0110:規(guī)則同步模式
#define ADC_Mode_FastInterl ((uint32_t)0x00070000) 0111:快速交替模式
#define ADC_Mode_SlowInterl ((uint32_t)0x00080000) 1000:慢速交替模式
#define ADC_Mode_AlterTrig ((uint32_t)0x00090000) 1001:交替觸發(fā)模式
*/
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
/* ADC_ScanConvMode在stm32f10x_adc.h中定義如下:
FunctionalState ADC_ScanConvMode;
這個(gè)參數(shù)用來(lái)指定轉(zhuǎn)換是掃描(多通道模式)還是單個(gè)轉(zhuǎn)換(單通道模式),該參數(shù)可以被設(shè)置為DISABLE或者ENABLE。
在數(shù)據(jù)手冊(cè)中,SCAN位是這樣描述的:掃描模式
該位由軟件設(shè)置和清除,用于開(kāi)啟或關(guān)閉掃描模式。在掃描模式中,由ADC_SQRx或ADC_JSQRx寄存器選中的通道被轉(zhuǎn)換。
0:關(guān)閉掃描模式
1:使用掃描模式
注:如果分別設(shè)置了EOCIE或JEOCIE位,只在最后一個(gè)通道轉(zhuǎn)換完畢才會(huì)產(chǎn)生EOC或JEOC中斷。
這樣,如果一次需要對(duì)多個(gè)通道進(jìn)行轉(zhuǎn)換,這位就必須設(shè)置為ENABLE。
*/
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
/* FunctionalState ADC_ContinuousConvMode;
這個(gè)參數(shù)用來(lái)指定轉(zhuǎn)換是連續(xù)進(jìn)行還是單次進(jìn)行,它可以設(shè)置為ENABLE或者DISABLE。
這兩個(gè)參數(shù)中出現(xiàn)了FunctionalState數(shù)據(jù)類(lèi)型,那么它是什么呢,順滕摸瓜,可以看到它的的定義如下:
typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
因此,它相當(dāng)于是一個(gè)位變量,我的理解,DISPABLE=0這個(gè)沒(méi)有問(wèn)題,ENABLE=!DISABLE是否應(yīng)該確切的是1??否則下面的設(shè)置就會(huì)有問(wèn)題。
用這兩個(gè)符號(hào)來(lái)對(duì)寄存器中的位進(jìn)行設(shè)置的話(huà),還需要提供位置信息,如下面的代碼所示:
tmpreg1 |= (uint32_t)(ADC_InitStruct-》ADC_DataAlign | ADC_InitStruct-》ADC_ExternalTrigConv |
((uint32_t)ADC_InitStruct-》ADC_ContinuousConvMode 《《 1));
這個(gè)《《1就是位置信息,CONT是CON2寄存器的位1
這樣,我們看STM32的庫(kù)又能多看懂一點(diǎn)了。
用于設(shè)定CON2的CONT位(位1):是否連續(xù)轉(zhuǎn)換
該位由軟件設(shè)置和清除。如果設(shè)置了此位,則轉(zhuǎn)換將連續(xù)進(jìn)行直到該位被清除。
0:?jiǎn)未无D(zhuǎn)換模式 1:連續(xù)轉(zhuǎn)換模式
*/
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
/* uint32_t ADC_ExternalTrigConv;
定義如何來(lái)觸發(fā)AD轉(zhuǎn)換,一共有8個(gè)可選項(xiàng),以下給出兩個(gè)來(lái)解釋一下:
#define ADC_ExternalTrigConv_T1_CC3 ((uint32_t)0x00040000)
將0x00040000寫(xiě)成二進(jìn)制,就是:
0000 0000 0000 0100 0000 0000 0000 0000
對(duì)照下面的說(shuō)明,不難看出,第19:17位是 010,即定時(shí)器1的CC3事件觸發(fā)。
#define ADC_ExternalTrigConv_None ((uint32_t)0x000E0000)
將0x000E0000寫(xiě)成二進(jìn)制,就是:
0000 0000 0000 1110 0000 0000 0000 0000
對(duì)照下面的說(shuō)明,是SWSTART方式,即用軟件標(biāo)志來(lái)啟動(dòng)轉(zhuǎn)換。
關(guān)于EXTSEL[2:0]的說(shuō)明:
位19:17 EXTSEL[2:0]:選擇啟動(dòng)規(guī)則通道組轉(zhuǎn)換的外部事件
這些位選擇用于啟動(dòng)規(guī)則通道組轉(zhuǎn)換的外部事件
ADC1和ADC2的觸發(fā)配置如下
000:定時(shí)器1的CC1事件 100:定時(shí)器3的TRGO事件
001:定時(shí)器1的CC2事件 101:定時(shí)器4的CC4事件
010:定時(shí)器1的CC3事件 110:EXTI線(xiàn)11/ TIM8_TRGO,
僅大容量產(chǎn)品具有TIM8_TRGO功能
011:定時(shí)器2的CC2事件 111:SWSTART
ADC3的觸發(fā)配置如下
000:定時(shí)器3的CC1事件 100:定時(shí)器8的TRGO事件
001:定時(shí)器2的CC3事件 101:定時(shí)器5的CC1事件
010:定時(shí)器1的CC3事件 110:定時(shí)器5的CC3事件
011:定時(shí)器8的CC1事件 111:SWSTART
*/
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
/*
這個(gè)是用來(lái)設(shè)定數(shù)據(jù)對(duì)齊模式的,有兩種可能:
#define ADC_DataAlign_Right ((uint32_t)0x00000000)
#define ADC_DataAlign_Left ((uint32_t)0x00000800)
找到數(shù)據(jù)手冊(cè)上的相關(guān)說(shuō)明:
位11:ALIGN:數(shù)據(jù)對(duì)齊
該位由軟件設(shè)置和清除。
0:右對(duì)齊 1:左對(duì)齊
*/
ADC_InitStructure.ADC_NbrOfChannel = 1;
/* ADC_NbrOfChannel的定義如下:
uint8_t ADC_NbrOfChannel;
指定有多少個(gè)通道會(huì)被轉(zhuǎn)換,它的值可以是1~16,這個(gè)數(shù)據(jù)將會(huì)影響到寄存器ADC_SQR1,下面是stm32f10x_adc.c中的相關(guān)代碼:
。。.。。.
tmpreg2 |= (uint8_t) (ADC_InitStruct-》ADC_NbrOfChannel - (uint8_t)1);
tmpreg1 |= (uint32_t)tmpreg2 《《 20;
ADCx-》SQR1 = tmpreg1;
看到mpreg1 |= (uint32_t)tmpreg2 《《 20;中的:20,用上面我們剛理解到的原則,這個(gè)值的低位將在ADC_SQR1的20位,而它的值是1~16,從代碼中可以看到這里又減去1,則其設(shè)置值為:0~15,即4bit就夠了,那么從20往前數(shù),也就是[23:20],那么SQR1中這幾位的用途是什么呢?順這條線(xiàn)索我們?nèi)フ襍QR1中的23:20位,看它是怎么用的。
位23:20 L[3:0]:規(guī)則通道序列長(zhǎng)度
這些位定義了在規(guī)則通道轉(zhuǎn)
0000:1個(gè)轉(zhuǎn)換
0001:2個(gè)轉(zhuǎn)換
……
1111:16個(gè)轉(zhuǎn)換
也就是設(shè)置一次進(jìn)行幾個(gè)通道的轉(zhuǎn)換,看來(lái)我們的理解完全正確。
*/
ADC_Init(ADC1, &ADC_InitStructure);
//通過(guò)前面一系列的設(shè)置,可以執(zhí)行ADC_Init函數(shù)了。
/* ADC1 規(guī)則通道15(Channel15)配置(規(guī)則通道見(jiàn)文章開(kāi)頭)*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 1, ADC_SampleTime_55Cycles5);
/* 這個(gè)函數(shù)一共有4個(gè)參數(shù),第一個(gè)是指定轉(zhuǎn)換器,根據(jù)所采用的器件的不同,可以是ADC1,ADC2,ADC3;第二個(gè)參數(shù)是指定通道號(hào);第三個(gè)參數(shù)是指定該通道在轉(zhuǎn)換序列中第幾個(gè)開(kāi)始轉(zhuǎn)換,第四個(gè)參數(shù)是指定轉(zhuǎn)換時(shí)間
第一、二個(gè)參數(shù)不難理解,這里就不再多說(shuō)了,看一看第三個(gè)參數(shù)。
先看一看這個(gè)函數(shù)的內(nèi)容,它在stm32f10x_adc.c中,這是STM庫(kù)提供的一個(gè)函數(shù):
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)
{ 。。.。。.前面的不寫(xiě)了
/* For Rank 1 to 6 */
if (Rank 《 7) //這個(gè)Rand就是第三個(gè)參數(shù)
{
/* Get the old register value */
tmpreg1 = ADCx-》SQR3;
/* Calculate the mask to clear */
tmpreg2 = SQR3_SQ_Set 《《 (5 * (Rank - 1));
SQR3的值如下:
//#define SQR3_SQ_Set ((uint32_t)0x0000001F)
之所以用5去乘,看下圖中的表格:ADC_SQ3中SQ1~SQ6每個(gè)都是占5位。
這下理解了:如果這個(gè)Rank是1,那么tmpreg2這個(gè)變量第[4:0]這5位將會(huì)是11111(即SQR3_SQ_Set的初始值:0x0000001f),如果Rank是2,那么tmpreg2這個(gè)變量的第[9:5]將會(huì)是11111,即tmpreg2將等于:0x00001f00,依此類(lèi)推。
/* Clear the old SQx bits for the selected rank */
tmpreg1 &= ~tmpreg2;
/* tmpreg2取反再與,即清掉tmpreg1中相應(yīng)的5位*/
tmpreg2 = (uint32_t)ADC_Channel 《《 (5 * (Rank - 1));
/*這次tmpreg2取的是通道值了,然后同相根據(jù)Rank的值左移5、10或更多位 */
tmpreg1 |= tmpreg2;
/* Store the new register value */
ADCx-》SQR3 = tmpreg1;
}
*/
?
第四個(gè)參數(shù)是采樣時(shí)間設(shè)定,代碼如下:
tmpreg2 = (uint32_t)ADC_SampleTime 《《 (3 * ADC_Channel);
/* 設(shè)定新的采樣時(shí)間,這里為什么用3,理由和上面的5一樣,看下圖。*/
tmpreg1 |= tmpreg2;
/* Store the new register value */
ADCx-》SMPR2 = tmpreg1;
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
至此一次ADC轉(zhuǎn)換配置完畢。很麻煩。。.。。.也許功能強(qiáng)大的副產(chǎn)品就是麻煩吧,沒(méi)有辦法。
二、使用內(nèi)置溫度傳感器測(cè)量溫度
學(xué)習(xí)使用ADC多通道轉(zhuǎn)換方式,驗(yàn)證溫度測(cè)量的準(zhǔn)確性,為以后的工程實(shí)踐打好基礎(chǔ)。
(1) ADC的單次與連續(xù)轉(zhuǎn)換
ADC轉(zhuǎn)換可以在一次轉(zhuǎn)換后停止,然后再次觸發(fā)后進(jìn)行下一次轉(zhuǎn)換;也可以是持續(xù)不斷地轉(zhuǎn)換下去。這個(gè)是通過(guò)設(shè)定ADC_CR2的CONT位來(lái)確定。
而在ST提供的庫(kù)里面,是這樣來(lái)設(shè)定的:
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
(2) ADC的掃描模式
ADC的掃描模式是用來(lái)掃描一組選定的通道的,它們將會(huì)被依次轉(zhuǎn)換。這個(gè)在上一份筆記中已說(shuō)明過(guò)。
那么連續(xù)轉(zhuǎn)換和掃描轉(zhuǎn)換之間又是什么關(guān)系呢?字面上理解,似乎它們都是持續(xù)不斷地轉(zhuǎn)換啊。
答案是:連續(xù)轉(zhuǎn)換的層次比掃描更高,它管著掃描呢。也就是說(shuō),對(duì)連續(xù)轉(zhuǎn)換來(lái)說(shuō),它所謂的“一次轉(zhuǎn)換”可并不是指的一個(gè)通道的轉(zhuǎn)換結(jié)束,而是指的“一組”轉(zhuǎn)換結(jié)束,當(dāng)然,這個(gè)“一組”有可能只有一個(gè)通道而已。再說(shuō)得明確一些:當(dāng)ADC掃描一次結(jié)束以后,如果CONT位是“1”(設(shè)定為連續(xù)轉(zhuǎn)換方式),那么將繼續(xù)下一輪的轉(zhuǎn)換。
(3) EOC什么時(shí)候產(chǎn)生?
我的理解應(yīng)該是每個(gè)通道(Channel)轉(zhuǎn)換結(jié)束時(shí)都會(huì)發(fā)生。但這里有些問(wèn)題(見(jiàn)下圖):
上面的說(shuō)明中:該位由硬件在(規(guī)則或注入)通道組換結(jié)束時(shí)設(shè)置…其中有個(gè)“組”字,字面的理解似乎應(yīng)該是指一次轉(zhuǎn)換組的所有通道都結(jié)束后才置1?但如果是這樣,那么又如何進(jìn)行數(shù)據(jù)的傳遞呢?要知道,對(duì)于ADC1來(lái)說(shuō),它的多個(gè)通道只有一個(gè)用于數(shù)據(jù)何存的寄存器:ADC1-》DR啊。
而這個(gè)問(wèn)題在其他兩個(gè)地方也沒(méi)有說(shuō)得清楚(見(jiàn)下圖):
我們前面討論了說(shuō)連續(xù)轉(zhuǎn)換是針對(duì)一組轉(zhuǎn)換而言的,所以這里所謂的:每個(gè)轉(zhuǎn)換后EOC標(biāo)志被設(shè)置,究竟是一組轉(zhuǎn)換結(jié)束后呢還是一個(gè)通道結(jié)束后呢?不明確。
而在掃描模式是這么說(shuō)的(見(jiàn)下圖):
這里僅說(shuō)到:如果設(shè)置了DMA位,在每次EOC后…,而并沒(méi)有說(shuō)到什么時(shí)候會(huì)有EOC產(chǎn)生?是所有掃描結(jié)束還是每個(gè)通道轉(zhuǎn)換結(jié)束?
而關(guān)于SCAN位又有這樣的說(shuō)明(見(jiàn)下圖):
注意最后的注:如果分別設(shè)置了EOCIE或JEOCIE位,只在最后一個(gè)通道轉(zhuǎn)換完畢才會(huì)產(chǎn)生EOC或者JEOC中斷。
對(duì)這一行話(huà)的理解同樣會(huì)有歧義:究竟是只在最后一個(gè)通道轉(zhuǎn)換完畢才產(chǎn)生EOC或者JEOC呢,還是每個(gè)通道轉(zhuǎn)換時(shí)都產(chǎn)生EOC或者JEOC,但是僅在最后一個(gè)通道轉(zhuǎn)換完畢時(shí)的EOC/JEOC才會(huì)引發(fā)中斷?
手冊(cè)上說(shuō)得清楚,手冊(cè)不保證正確,有問(wèn)題可以找英文原版……可憐我,如果漢語(yǔ)語(yǔ)法也搞不清楚,那么英語(yǔ)語(yǔ)法豈非更頭大?看來(lái)非得進(jìn)修個(gè)英文六級(jí)再來(lái)學(xué)啦。
還好,我們還能做實(shí)驗(yàn)驗(yàn)證。經(jīng)驗(yàn)證,我認(rèn)為應(yīng)該是每次通道轉(zhuǎn)換時(shí)都有EOC產(chǎn)生,并且這個(gè)EOC可以觸發(fā)DMA事件。但是畢意自己驗(yàn)證的不能保證一定理解正確,所以啰啰嘍嘍寫(xiě)了這么多。
(4)為了要使用內(nèi)置的溫度傳感器,得要先打開(kāi)溫度傳感器(同時(shí)也打開(kāi)了內(nèi)部REF測(cè)量通道),數(shù)據(jù)手冊(cè)上說(shuō)是設(shè)置ADC-》CR2中的TSRVEFF位。這個(gè)位當(dāng)然可以寫(xiě)個(gè)代碼自行設(shè)置,不過(guò)我們現(xiàn)在是用庫(kù)編程,那就遵守紀(jì)律,找到相應(yīng)的庫(kù)函數(shù)吧。
打開(kāi)stm32f10x_adc.c,用盡一切手段找,在這里(見(jiàn)下圖):
根據(jù)上次的解讀,我們已知FunctionalState相當(dāng)于是一個(gè)“位”變量,它只能取Enable和Disable兩個(gè)值之一。
所以,main.c中加入這樣一行:
ADC_TempSensorVrefintCmd(ENABLE); //開(kāi)啟溫度傳感器及Vref通道
(5)選定待轉(zhuǎn)換組中的通道,并設(shè)定轉(zhuǎn)換順序,轉(zhuǎn)換時(shí)間
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_239Cycles5);
/* 設(shè)置ADC1的Channel15通道在轉(zhuǎn)換序列中第 2 個(gè)進(jìn)行轉(zhuǎn)換,轉(zhuǎn)換時(shí)間設(shè)定為239.5個(gè)周期
*/
這一段中的第一個(gè)參數(shù)是指定由ADC1轉(zhuǎn)換器轉(zhuǎn)換,因?yàn)闇囟葌鞲衅鹘釉谶@個(gè)轉(zhuǎn)換器的第16通道上,第二個(gè)參數(shù)顯然就是選定第16通道了,而第三個(gè)參數(shù)2是說(shuō)這個(gè)通道第二個(gè)轉(zhuǎn)換;第四個(gè)參數(shù)是設(shè)定采樣時(shí)間。
說(shuō)到采樣時(shí)間,又要多說(shuō)幾句了。
ADC1轉(zhuǎn)換器的時(shí)鐘是ADCCLK,這個(gè)時(shí)鐘是由APB2時(shí)鐘經(jīng)過(guò)分頻器而得到的,由于代碼中沒(méi)有對(duì)預(yù)分頻器進(jìn)行設(shè)置,所以用默認(rèn)值2分頻,所以ADCCLK的時(shí)鐘是36M。
對(duì)于溫度傳感器的使用,數(shù)據(jù)手冊(cè)中這么樣寫(xiě)(見(jiàn)下圖):
看第2條,即要求采樣時(shí)間大于2.2us,那么我們只能取最大的采樣周期239.5了。因?yàn)樵俚鸵粰n的就是71.5個(gè)周期,這是無(wú)法滿(mǎn)足要求的。
但是說(shuō)到這里,又出來(lái)問(wèn)題了,就在緊挨著這段話(huà)的上面有這么一段(見(jiàn)下圖):
也就是它要求采樣時(shí)間是17.1us,這這豈不是明顯不相符?
先標(biāo)志于此,稍后查資料或做實(shí)驗(yàn)來(lái)驗(yàn)證。
(6)設(shè)置DMA通道,將轉(zhuǎn)換得到的數(shù)據(jù)保存到SRAM中去。
vu16 ADCConvertedValue[2]; //定義一個(gè)2個(gè)字的數(shù)組,用來(lái)保存數(shù)據(jù)
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADCConvertedValue[0];
//設(shè)定SRAM中的起始地址
DMA_InitStructure.DMA_BufferSize = 2; //2個(gè)字節(jié)
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
//使用內(nèi)存地址自增模式
……
其他的不多寫(xiě)了。這樣,2個(gè)通道的測(cè)試數(shù)據(jù)分別被保存到ADCConvertedValue[0]和ADCConvertedValue[1]中去了,只要讀出這兩個(gè)單元中的值,就可以分別得到PC5(ADC_Channel15)上外接電位器的分壓值和內(nèi)部溫度傳感器的值了。
(7)第17通道Refint
在片內(nèi)有一個(gè)片內(nèi)基準(zhǔn),連接到ADC1的Channel17上,開(kāi)始我以為,可以利用這個(gè)通道來(lái)做校準(zhǔn)工作,但是看一看數(shù)據(jù)手冊(cè),我知道沒(méi)戲了。
居然從1.16變到了1.24V。
而我實(shí)測(cè)的結(jié)果更令我嘆息,開(kāi)空調(diào)吹一下板子,實(shí)測(cè)的數(shù)值是1470,其時(shí)溫度大概是在25度左右,空調(diào)一停,幾度的變化,結(jié)果變成了1475,再試,我拿著板子對(duì)著空調(diào)出風(fēng)口一陣吹,數(shù)值變到了1465.(其時(shí)溫度值為從1700變到1753)雖然基準(zhǔn)電壓值的變化遠(yuǎn)沒(méi)有溫度值變化大,可…。一個(gè)是基準(zhǔn),一個(gè)是傳感器啊。看來(lái),非得用外部基準(zhǔn)不可了。我的板子上VREF并沒(méi)有用基準(zhǔn)源,是通過(guò)一個(gè)簡(jiǎn)單的濾波電路接到VCC上的,這個(gè)基本上還算穩(wěn)定,說(shuō)明那個(gè)低壓差穩(wěn)壓集成電路AMS1117的溫度特性還是不錯(cuò)的。
最后,報(bào)告一下測(cè)試結(jié)果
(1) 室溫下讀到的溫度傳感器的輸出為1700。
要將其轉(zhuǎn)換成溫度,還要找張表:(見(jiàn)下圖)
由于該表都是用電壓來(lái)表示的,所以要將1700轉(zhuǎn)換成電壓值。
(1686/4096)*3.3=1.3583
那么溫度就是:
T=(1.43-1.3583)/4.3*1000)+25
=14.03+25
=39度
(2)拿電吹風(fēng)來(lái),一陣吹,讀數(shù)變?yōu)?550
電壓值:(1550/4096)*3.3=1.2488V
再次計(jì)算:
T=(1.42-1.2488)/4.3*1000+25
=42.1+25
=67.1度
(3)開(kāi)空調(diào)吹,讀數(shù)變?yōu)?730,這個(gè)就不計(jì)算了,但是可以肯定數(shù)值變化趨勢(shì)是對(duì)的了。
從第一個(gè)39這個(gè)值來(lái)看,測(cè)溫的大體范圍是對(duì)的,因?yàn)槲以谑覂?nèi),估計(jì)當(dāng)時(shí)的溫度可能會(huì)有33~34度左右,這個(gè)39差了很多,原因:(1)V25和Avg_Slope都是取的中間值,這個(gè)未必對(duì);(2)測(cè)量值和電壓沒(méi)有精確對(duì)照測(cè)量,估計(jì)誤差也比較大。(3)是否與采樣時(shí)間有關(guān)系?這個(gè)還要驗(yàn)證。
因此,如果某個(gè)應(yīng)用中只是單獨(dú)測(cè)溫的話(huà),這兩點(diǎn)都要注意,要在生產(chǎn)后有個(gè)修正的表格,否則誤差會(huì)比較大。
三、外部引腳中斷使用
這里描述的僅僅只是諸多可能性中的一種,并不表示以下內(nèi)容全部正確,因?yàn)镃ontex的中斷和8位單片機(jī)的相比,真的是太復(fù)雜了。
我想要實(shí)現(xiàn)的功能
PD0,PD1,PD2作為輸入管腳,使用它們的下降沿觸發(fā),分別令PD8,PD9,PD10管腳上的電平取反。
實(shí)現(xiàn)的過(guò)程
(1)管腳配置:這個(gè)不復(fù)雜,分別把PD0…PD2配置成Float Input,將PD8…PD10配置成推挽輸出即可,這里不再寫(xiě)出源代碼。
(2)外部中斷線(xiàn)配置:
這里需要說(shuō)明,在STM32內(nèi)部有19條外部中斷線(xiàn),但是它們并不完全確定連接到哪些位置。其中EXTI0線(xiàn)可以和以下這些引腳連接:
其他的就不一一列舉了,16條線(xiàn)分別可能和一組I/O中的16條引線(xiàn)連接在一起。這是通過(guò)EXTIO[3:0]這組寄存器來(lái)設(shè)置的。那么用STM32的庫(kù)編程的話(huà),庫(kù)函數(shù)是什么,在什么位置呢?(以 3.1.2庫(kù)為例)
設(shè)置管腳與中斷線(xiàn)連接的函數(shù)不在stm 32f10x_exti.c中,而是在stm 32f10x_gpio.c中。
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
{……
}
參數(shù)是兩個(gè)字節(jié)型變量,分別指定端口,及端口中指定的管腳,這些當(dāng)然也是有預(yù)定義的。這些預(yù)定義在stm 32f10x_gpio.h頭文件中。
下面給出的例子:
/*把PORTD 0,1,2三條引腳與EXT0,1,2分別相連*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource0) ;
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource1) ;
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource2) ;
看了例子,如果要配置其他的管腳,應(yīng)該可以依葫蘆畫(huà)瓢了。
這樣19條外中斷線(xiàn)就清楚了。
(3)對(duì)EXTI各引線(xiàn)如何中斷進(jìn)行設(shè)置
這些先直接給出代碼:
void Exti_Config(void)
{ EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0|EXTI_Line1|EXTI_Line2;
//哪些線(xiàn)將被配置
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
//中斷模式還是事件模式
/*typedef enum
{
EXTI_Mode_Interrupt = 0x00,
EXTI_Mode_Event = 0x04
}EXTIMode_TypeDef;
*/
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿觸發(fā)
/*typedef enum
{
EXTI_Trigger_Rising = 0x08,
EXTI_Trigger_Falling = 0x 0C,
EXTI_Trigger_Rising_Falling = 0x10
}EXTITrigger_TypeDef;
可見(jiàn),可選的模式有3種:上升沿觸發(fā)、下降沿觸發(fā)、上升沿和下降沿均觸發(fā)
*/
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //中斷線(xiàn)使能
如果沒(méi)有這行,那么設(shè)置就無(wú)法進(jìn)行了,看一看XTI_Init的代碼:
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
{
……
if (EXTI_InitStruct-》EXTI_LineCmd != DISABLE)
{……各種設(shè)置都在下面的代碼中進(jìn)行,而執(zhí)行到的條件是上面那行程序;
}
*/
EXTI_Init(&EXTI_InitStructure); //初始化中斷
/*結(jié)構(gòu)中該填寫(xiě)的內(nèi)容都填寫(xiě)了,執(zhí)行初始化程序*/
EXTI_GenerateSWInterrupt(EXTI_Line0|EXTI_Line1|EXTI_Line2);
/*
而EXTI_Line0、EXTI_Line1、EXTI_Line2的定義則在stm 32f10x_exti.h中
#define EXTI_Line0 ((uint32_t)0x00001) /*!《 External interrupt line 0 */
#define EXTI_Line1 ((uint32_t)0x00002) /*!《 External interrupt line 1 */
#define EXTI_Line2 ((uint32_t)0x00004) /*!《 External interrupt line 2 */
*/
所以綜合起來(lái),這么寫(xiě)就是允許這三條線(xiàn)中斷
}
(4)還要對(duì)NVIC寄存器進(jìn)行配置
void NVIC_Configuration(void)
{ NVIC_InitTypeDef NVIC_InitStructure;
/* Configure the NVIC Preemption Priority Bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
#ifdef VECT_TAB_RAM
/* Set the Vector Table base location at 0x20000000 */
NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else /* VECT_TAB_FLASH */
/* Set the Vector Table base location at 0x08000000 */
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
/*上面的程序代碼來(lái)自于ST的例子程序,下面是我自己寫(xiě)的,我不知是不是會(huì)讓人笑掉大牙,但以我自己的理解能力,我暫時(shí)還就只能寫(xiě)出這樣的代碼來(lái),這其中尤其對(duì)優(yōu)先級(jí)和次優(yōu)先級(jí)的設(shè)定,非常的沒(méi)有把握
*/
/*允許EXTI0中斷 */
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7;//優(yōu)先級(jí)設(shè)定
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //次優(yōu)先級(jí)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //通道中斷使能
NVIC_Init(&NVIC_InitStructure); //初始化中斷
////允許EXTI1中斷
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7;//優(yōu)先級(jí)設(shè)定
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //次優(yōu)先級(jí)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //通道中斷使能
NVIC_Init(&NVIC_InitStructure); //初始化中斷
////允許EXTI2中斷
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7;//優(yōu)先級(jí)設(shè)定
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //次優(yōu)先級(jí)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //通道中斷使能
NVIC_Init(&NVIC_InitStructure); //初始化中斷
}
此外,這里要提醒一點(diǎn):
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //中斷通道
這其中的:EXTI2_IRQn是新版本的庫(kù)中所使用的符號(hào),在2.0版本(也許還有其他版本)中,是這么樣來(lái)寫(xiě)的:
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQChannel;
至此,配置工作完成。
(5)我的中斷程序在哪里?
隨便找個(gè)st的例子程序,打開(kāi)stm 32f10x_it.c可以看到里面已先寫(xiě)好了一些中斷處理程序,如:
如果是51單片機(jī)的話(huà),會(huì)有個(gè)關(guān)鍵字:interrupt 后加個(gè)數(shù)字來(lái)說(shuō)明究竟是哪一級(jí)中斷,這樣,中斷函數(shù)的名字可以隨便起。可是,這里看來(lái),這些函數(shù)就像是普通的函數(shù),并沒(méi)有什么特別的,那么我們要增加的3個(gè)中斷處理函數(shù)起什么名字呢?這回用到的工具是:Fined in File,就是下面的對(duì)話(huà)框:
以SysTick_Handler為關(guān)鍵字在文件中搜一下,找到線(xiàn)索了,原來(lái)在這里:
那么我們?cè)趕tm 32f10x_it.c中寫(xiě)上:
void EXTI0_IRQHandler(void)
//這個(gè)就是處理外中斷線(xiàn)0(目前連到PD0上)中斷的代碼的
{ /* Clear EXTI0 bit */
EXTI_ClearITPendingBit(EXTI_Line0); //0.17US
GPIO_WriteBit(GPIOD, GPIO_Pin_8, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOD, GPIO_Pin_8))); //0.5US
}
余者不多言,相差無(wú)幾。
至此,該解決的問(wèn)題都已解決,下面就運(yùn)行一下,看一看效果了。
進(jìn)行軟件仿真,打開(kāi)Peripherals-》External Interrupt,可見(jiàn)下面的圖:
單步執(zhí)行到所有設(shè)置代碼完成,可以看到變成這樣:
這里的變化,對(duì)照著數(shù)據(jù)手冊(cè)上的變化,可以一一解讀,并不困難,這里就不再說(shuō)明了。
接下來(lái)的軟件仿真和硬件測(cè)試都能夠達(dá)到當(dāng)初的設(shè)計(jì)目標(biāo),但程序是否最優(yōu),是否存在著不合理之處,很不好說(shuō),因?yàn)镾TM32的中斷實(shí)在是夠復(fù)雜的。這個(gè)留著后面繼續(xù)學(xué)習(xí)的螺旋式上升中提高吧!
四、數(shù)據(jù)的保存與毀滅-BKP功能
通過(guò)STM32庫(kù)自帶的例子來(lái)做,就是這個(gè):
通過(guò)研究,大體明白了BKP的功能,簡(jiǎn)述如下:
1. BKP可以用來(lái)保存數(shù)據(jù)
BKP中包括了42個(gè)16位的寄存器,共可保存84字節(jié)的內(nèi)容,它們由VBAT的供電來(lái)維掛。
2. BKP內(nèi)保存的數(shù)據(jù)可以被毀滅(如果有人希望惡意得到這些數(shù)據(jù)的話(huà),令其丟失比保護(hù)數(shù)據(jù)更重要)。STM32提供了一種稱(chēng)之為T(mén)AMPER的機(jī)制來(lái)完成。中文譯為“侵入檢測(cè)”,這需要占用一個(gè)外部引腳(PC13)。
3. 如果不用侵入檢測(cè)功能,那么這個(gè)外部引腳可以用作RTC校準(zhǔn)功能,這個(gè)稍后再研究。
4. 當(dāng)有系統(tǒng)復(fù)位/電源復(fù)位/待機(jī)模式下被喚醒這三種情況時(shí),BKP中的值不會(huì)丟失或被復(fù)位。
先回來(lái)研究一下STM32的復(fù)位機(jī)制。以下是數(shù)據(jù)手冊(cè)的相關(guān)部分。
6.1 復(fù)位
STM 32F10xxx支持三種復(fù)位形式,分別為系統(tǒng)復(fù)位、上電復(fù)位和備份區(qū)域復(fù)位。
6.1.1 系統(tǒng)復(fù)位
系統(tǒng)復(fù)位將復(fù)位除時(shí)鐘控制寄存器CSR中的復(fù)位標(biāo)志和備份區(qū)域中的寄存器以外的所有寄存器
當(dāng)以下事件中的一件發(fā)生時(shí),產(chǎn)生一個(gè)系統(tǒng)復(fù)位:
1.NRST管腳上的低電平(外部復(fù)位)
例如:按下板子上的RESET按鈕就產(chǎn)生一個(gè)外部復(fù)位(屬于系統(tǒng)復(fù)位)
2.窗口看門(mén)狗計(jì)數(shù)終止(WWDG復(fù)位)
3.獨(dú)立看門(mén)狗計(jì)數(shù)終止(IWDG復(fù)位)
4.軟件復(fù)位(SW復(fù)位)
5.低功耗管理復(fù)位
可通過(guò)查看RCC_CSR控制狀態(tài)寄存器中的復(fù)位狀態(tài)標(biāo)志位識(shí)別復(fù)位事件來(lái)源
以下是RCC_CSR的內(nèi)容:
調(diào)試時(shí)不太容易區(qū)分,以下是某次調(diào)試中截到的RCC_CSR數(shù)據(jù)。
6.1.2 電源復(fù)位
當(dāng)以下事件中之一發(fā)生時(shí),產(chǎn)生電源復(fù)位:
1. 上電/掉電復(fù)位(POR/PDR復(fù)位)
2. 從待機(jī)模式中返回
電源復(fù)位將復(fù)位除了備份區(qū)域外的所有寄存器。(見(jiàn)圖3)
圖中復(fù)位源將最終作用于RESET管腳,并在復(fù)位過(guò)程中保持低電平。復(fù)位入口矢量被固定在地址0x0000_0004。更多細(xì)節(jié),參閱表36。
檢測(cè)可以是否上電/掉電復(fù)位可以用以下的函數(shù):
RCC_GetFlagStatus(RCC_FLAG_PORRST)
其中RCC_FLAG_PORRST也可以被替代成以下的一些符號(hào),以檢測(cè)不同的內(nèi)容:
**************************************************************************
5. 如果必須要人為地令備份域復(fù)位(所有數(shù)據(jù)都被清零),那么有兩種方法:
a) 軟件復(fù)位(操作RCC_BDCR中的BDRST位產(chǎn)生。);以下是RCC_BDCR中相關(guān)的內(nèi)容:
6.3.9 備份域控制寄存器 (RCC_BDCR)
b) VDD和VBAT均掉電,那么在VDD或都VBAT上電時(shí)將引發(fā)備分域復(fù)位(這是為了保護(hù)數(shù)據(jù)的完整性?)
6. 數(shù)據(jù)寄存器究竟是哪些呢?
那么在STM32提供的庫(kù)里又是如何來(lái)用這些寄存器的呢?我們找一找,在stm 32f10x_bkp.c中,代碼如下:
/**
* @brief Writes user data to the specified Data Backup Register.
* @param BKP_DR: specifies the Data Backup Register.
* This parameter can be BKP_DRx where x:[1, 42]
* @param Data: data to write
* @retval None
*/
void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data)
{
__IO uint32_t tmp = 0;
/* Check the parameters */
assert_param(IS_BKP_DR(BKP_DR));
tmp = (uint32_t)BKP_BASE;
tmp += BKP_DR;
*(__IO uint32_t *) tmp = Data;
}
即只需要提供兩個(gè)參數(shù),第一個(gè)是BKP地址,第二個(gè)是數(shù)據(jù),兩個(gè)都是16位的數(shù)據(jù)。第二個(gè)參數(shù)沒(méi)有問(wèn)題,第一個(gè)參數(shù)如何提供呢?看例子中的代碼:
/**
* @brief Writes data Backup DRx registers.
* @param FirstBackupData: data to be written to Backup data registers.
* @retval None
*/
void WriteToBackupReg(uint16_t FirstBackupData)
{
uint32_t index = 0;
for (index = 0; index 《 BKP_DR_NUMBER; index++)
{
BKP_WriteBackupRegister(BKPDataReg[index], FirstBackupData + (index * 0x 5A));
}
}
從上面的代碼可以看到,第一個(gè)參數(shù)是用
BKPDataReg[index]
來(lái)提供的,這個(gè)又是什么東西呢?再找:
uint16_t BKPDataReg[BKP_DR_NUMBER] =
{
BKP_DR1, BKP_DR2, BKP_DR3, BKP_DR4, BKP_DR5, BKP_DR6, BKP_DR7, BKP_DR8,
BKP_DR9, BKP_DR10, BKP_DR11, BKP_DR12, BKP_DR13, BKP_DR14, BKP_DR15, BKP_DR16,
BKP_DR17, BKP_DR18, BKP_DR19, BKP_DR20, BKP_DR21, BKP_DR22, BKP_DR23, BKP_DR24,
BKP_DR25, BKP_DR26, BKP_DR27, BKP_DR28, BKP_DR29, BKP_DR30, BKP_DR31, BKP_DR32,
BKP_DR33, BKP_DR34, BKP_DR35, BKP_DR36, BKP_DR37, BKP_DR38, BKP_DR39, BKP_DR40,
BKP_DR41, BKP_DR42
};
原來(lái)最終還是用BKP_DR**這樣的格式來(lái)用的,其中的**代表的序號(hào)。即 5.4.1中的x。
7.復(fù)位后,對(duì)備份寄存器和RTC的訪問(wèn)被禁止,并且備份域被保護(hù)以防止可能存在的意外的寫(xiě)操作。執(zhí)行以下操作可以使能對(duì)備份寄存器和RTC的訪問(wèn)。
● 通過(guò)設(shè)置寄存器RCC_APB1ENR的PWREN和BKPEN位來(lái)打開(kāi)電源和后備接口的時(shí)鐘
以下是相關(guān)代碼:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
這個(gè)沒(méi)有什么可說(shuō)的,關(guān)于打開(kāi)時(shí)鐘,前面已多次涉及到。
● 電源控制寄存器(PWR_CR)的DBP位來(lái)使能對(duì)后備寄存器和RTC的訪問(wèn)。
以下是相關(guān)代碼:
PWR_BackupAccessCmd(ENABLE);
代碼本身相當(dāng)簡(jiǎn)潔,不過(guò)我們還是再深入一點(diǎn)點(diǎn)。
這個(gè)PWR_BackupAccessCmd代碼如下:(在stm 32f10x_pwr.c文件中)
/**
* @brief Enables or disables access to the RTC and backup registers.
* @param NewState: new state of the access to the RTC and backup registers.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void PWR_BackupAccessCmd(FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_FUNCTIONAL_STATE(NewState));
*(__IO uint32_t *) CR_DBP_BB = (uint32_t)NewState;
}
而CR_DBP_BB在這里(stm 32f10x_pwr.c文件中):
/* Alias word address of DBP bit */
#define CR_OFFSET (PWR_OFFSET + 0x00)
#define DBP_BitNumber 0x08
#define CR_DBP_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (DBP_BitNumber * 4))
8.一番探索,暫告一段落。由于我的板子與EVAL板略有不同,4個(gè)發(fā)光管分別接GPIOD的8,9,10和11引腳,所以在程序中做了如下改動(dòng)(stm3210e_eval.h文件中):
#define LEDn 4
#define LED1_GPIO_PORT GPIOD
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOD
#define LED1_GPIO_PIN GPIO_Pin_8
#define LED2_GPIO_PORT GPIOD
#define LED2_GPIO_CLK RCC_APB2Periph_GPIOD
#define LED2_GPIO_PIN GPIO_Pin_9
#define LED3_GPIO_PORT GPIOD
#define LED3_GPIO_CLK RCC_APB2Periph_GPIOD
#define LED3_GPIO_PIN GPIO_Pin_10
#define LED4_GPIO_PORT GPIOD
#define LED4_GPIO_CLK RCC_APB2Periph_GPIOD
#define LED4_GPIO_PIN GPIO_Pin_11
然后在板子上將JP6插到VBAT端,并且為板子上現(xiàn)成的電池座中裝入一塊電池。
執(zhí)行程序,結(jié)果是LED4亮(程序運(yùn)行)LED1和LED3燈點(diǎn)亮,其含義如下:
(1. LD3 on / LD1 on: a Power On Reset occurred and the values in the BKP data registers are correct)。
按下復(fù)位按鈕后,LD1,LD2,LED3均滅,其含義如下:
(3. LD3 off / LD1 off / LD2 off: no Power On Reset occurred)
?
——下次將繼續(xù)討論學(xué)習(xí)STM32之RTC實(shí)踐,敬請(qǐng)關(guān)注。
?
評(píng)論