工程師STM32單片機(jī)學(xué)習(xí)基礎(chǔ)手記(2):從勉強(qiáng)看懂一行程序到IO口研究
至此,按我的習(xí)慣,要翻開STM32F的數(shù)據(jù)手冊(cè),研究一下其IO端口了。下面是數(shù)據(jù)手冊(cè)中的一段話:
-------------------------------------
每個(gè)GPI/O端口有兩個(gè)32位配置寄存器(GPIOx_CRL,GPIOx_CRH),兩個(gè)32位數(shù)據(jù)寄存器(GPIOx_IDR,GPIOx_ODR),一個(gè)32位置位/復(fù)位寄存器(GPIOx_BSRR),一個(gè)16位復(fù)位寄存器(GPIOx_BRR)和一個(gè)32位鎖定寄存器(GPIOx_LCKR)。
根據(jù)數(shù)據(jù)手冊(cè)中列出的每個(gè)I/O端口的特定硬件特征, GPIO端口的每個(gè)位可以由軟件分別配置成多種模式。
─ 輸入浮空
─ 輸入上拉
─ 輸入下拉
─ 模擬輸入
─ 開漏輸出
─ 推挽式輸出
─ 推挽式復(fù)用功能
─ 開漏復(fù)用功能
----------------------------------------------------
當(dāng)然,數(shù)據(jù)手冊(cè)上關(guān)于IO端口的描述是很多很多的,我也只是大概地了解了一下,真正要設(shè)計(jì)產(chǎn)品時(shí),肯定還要細(xì)看。但至少,知道了IO端口復(fù)位后處于浮空狀態(tài),也就是其電平狀態(tài)由外圍電路決定,這很重要,如果設(shè)計(jì)工業(yè)品的話,這是必須要確定的;知道了IO引腳可以兼容5V電源;知道了在什么地方可以找到這些引腳在庫(kù)中的定義而不必看著數(shù)據(jù)手冊(cè)去控制那些位;也知道了這些引腳的一些基本操作函數(shù)(連猜帶蒙帶測(cè)試應(yīng)該可以搞定大部分功能),那么我心里基本就有底啦。
最后用一段電子熒火蟲(也就是呼吸燈)的簡(jiǎn)單程序作為結(jié)束。
int main(void)
{ uint8_t Count;
uint32_t DelayTim=“0x1000”; //基數(shù)
uint32_t ChangTim=“0x2000”; //每次變化的量
Init_All_Periph();
while(1)
{
for(Count=0;Count《16;Count++) //漸亮
{
GPIO_SetBits(GPIOD, GPIO_Pin_8); //點(diǎn)亮燈
Delay(DelayTim+Count*ChangTim);
GPIO_ResetBits(GPIOD, GPIO_Pin_8); //熄滅燈
Delay(DelayTim+(16-Count)*ChangTim);
}
for(Count=16;Count》0;Count--) //漸暗
{
GPIO_SetBits(GPIOD, GPIO_Pin_8); //點(diǎn)亮燈
Delay(DelayTim+Count*ChangTim);
GPIO_ResetBits(GPIOD, GPIO_Pin_8); //熄滅燈
Delay(DelayTim+(16-Count)*ChangTim);
}
}
}
定時(shí)器初步
接下來(lái)研究定時(shí)器。為什么開始研究定時(shí)器了呢?那個(gè)I/O還是剛剛開了頭啊,還有很多很多知識(shí)沒有掌握!不怕,方法論告訴我們:對(duì)事物的認(rèn)識(shí)是螺旋式上升的,所以不要一桿子打到底,想著把I/O口的所有情況都掌握了再學(xué)下面的,那會(huì)是很困難的
STM32的定時(shí)器是什么樣子的,心里一點(diǎn)底也沒有,還是找個(gè)現(xiàn)成的例子來(lái)吧。在ST提供的庫(kù)里,有很多的例子
第一個(gè)就是它了。
把整個(gè)文件夾復(fù)制一份到自己的實(shí)驗(yàn)文件夾中
在Source文件夾中再建立名為APP的文件夾,將上圖中所有源程序文件全部復(fù)制到APP文件夾中。然后將庫(kù)所提供的CMSIS文件夾和 STM32F10x_StdPeriph_Driver文件夾復(fù)制到Source文件夾中。然后按照前面的方法建立項(xiàng)目。
這是建好的項(xiàng)目的結(jié)構(gòu)。
下面開始研究,首先看附帶的readme.txt文件,了解到該例子的大體用途是驗(yàn)證Tim2的Output Compare Timing mode的。于是打開PDF文件,直接翻到下面的位置:
-----------------------------------------------------------------------------------------
13.3.8 輸出比較模式
此項(xiàng)功能是用來(lái)控制一個(gè)輸出波形或者指示何時(shí)一段給定的的時(shí)間已經(jīng)到時(shí)。
當(dāng)計(jì)數(shù)器與捕獲/比較寄存器的內(nèi)容相同時(shí),輸出比較功能做如下操作:
● 將輸出比較模式(TIMx_CCMRx寄存器中的OCxM位)和輸出極性(TIMx_CCER寄存器中的CCxP位)定義的值輸出到對(duì)應(yīng)的管腳上。在比較匹配時(shí),輸出管腳可以保持它的電平
?。∣CxM=000)、被設(shè)置成有效電平(OCxM=001)、被設(shè)置成無(wú)有效電平(OCxM=010)或進(jìn)行翻轉(zhuǎn)(OCxM=011)。
● 設(shè)置中斷狀態(tài)寄存器中的標(biāo)志位(TIMx_SR寄存器中的CCxIF位)。
● 若設(shè)置了相應(yīng)的中斷屏蔽(TIMx_DIER寄存器中的CCXIE位),則產(chǎn)生一個(gè)中斷。
● 若設(shè)置了相應(yīng)的使能位(TIMx_DIER寄存器中的CCxDE位,TIMx_CR2寄存器中的CCDS位選擇DMA請(qǐng)求功能),則產(chǎn)生一個(gè)DMA請(qǐng)求。
TIMx_CCMRx中的OCxPE位選擇TIMx_CCRx寄存器是否需要使用預(yù)裝載寄存器。
在輸出比較模式下,更新事件UEV對(duì)OCxREF和OCx輸出沒有影響。
同步的精度可以達(dá)到計(jì)數(shù)器的一個(gè)計(jì)數(shù)周期。輸出比較模式(在單脈沖模式下)也能用來(lái)輸出一個(gè)單脈沖。
輸出比較模式的配置步驟:
1. 選擇計(jì)數(shù)器時(shí)鐘(內(nèi)部,外部,預(yù)分頻器)
2. 將相應(yīng)的數(shù)據(jù)寫入TIMx_ARR和TIMx_CCRx寄存器中
3. 如果要產(chǎn)生一個(gè)中斷請(qǐng)求和/或一個(gè)DMA請(qǐng)求,設(shè)置CCxIE位和/或CCxDE位。
4. 選擇輸出模式,例如:必須設(shè)置OCxM=’011’、OCxPE=’0’、CCxP=’0’和CCxE=’1’,當(dāng)計(jì)數(shù)器CNT與CCRx匹配時(shí)翻轉(zhuǎn)OCx的輸出管腳,CCRx預(yù)裝載未用,開啟OCx輸出且高電平有效。
5. 設(shè)置TIMx_CR1寄存器的CEN位啟動(dòng)計(jì)數(shù)器--------------------------------------------------------------------------------
說(shuō)得并不復(fù)雜,但是要弄清楚也絕非易事,況且main.c中一系列的符號(hào)究竟是什么意思也不是那么容易搞清楚的。這些東西搞不清,將來(lái)自己編程時(shí),就算想要依葫蘆畫瓢都難。怎么辦呢?可能并沒有多少偷巧的辦法,只能是一步一步地摸索吧。
進(jìn)入調(diào)試,打開Peripherals-》Timers-》Tim2,出現(xiàn)下面的窗口。
然后單步執(zhí)行程序,并觀察界面的變化,并將這些符號(hào)作為線索,在PDF文件,stmf10x_tim.c等文件中搜索,了解相關(guān)符號(hào)的含義。
如下圖是執(zhí)行到:while(1)前,也就是所有設(shè)置完成后的圖。
沿著這些線索一路追蹤,邊看源程序,邊找數(shù)據(jù)手冊(cè),邊找相關(guān)的頭文件,其他源程序,總算大體有了個(gè)明白,下面將main.c中的部分代碼作為注釋,也算是做點(diǎn)記錄。
/* Includes ------------------------------------------------------------------*/
#include “stm32f10x.h”
//這個(gè)頭文件需要根據(jù)所選擇的芯片進(jìn)行更改
/* Private variables ---------------------------------------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure; //定義兩個(gè)結(jié)構(gòu)型變量
__IO uint16_t CCR1_Val = 49152;
__IO uint16_t CCR2_Val = 32768;
__IO uint16_t CCR3_Val = 16384;
__IO uint16_t CCR4_Val = 8192; //
ErrorStatus HSEStartUpStatus;
/* Private function prototypes -----------------------------------------------*/
void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
int main(void)
{
/* System Clocks Configuration */
RCC_Configuration();
/* NVIC Configuration */
NVIC_Configuration();
/* GPIO Configuration */
GPIO_Configuration();
/* ---------------------------------------------------------------
TIM2 Configuration: Output Compare Timing Mode:
TIM2CLK = 36 MHz, Prescaler = 4, TIM2 counter clock = 7.2 MHz
CC1 update rate = TIM2 counter clock / CCR1_Val = 146.48 Hz
CC2 update rate = TIM2 counter clock / CCR2_Val = 219.7 Hz
CC3 update rate = TIM2 counter clock / CCR3_Val = 439.4 Hz
CC4 update rate = TIM2 counter clock / CCR4_Val = 878.9 Hz
--------------------------------------------------------------- */
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 65535; //它對(duì)應(yīng)TIM2_ARR
TIM_TimeBaseStructure.TIM_Prescaler = 0;
/* 它對(duì)應(yīng)TIM2_PSC,相關(guān)代碼如下(stm32f10x_tim.c中):
TIMx-》PSC = TIM_TimeBaseInitStruct-》TIM_Prescaler;
但很奇怪,這里令其為0,然后再在下面設(shè)置為4??為何??
*/
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0200;
//ClockDivision是對(duì)CKD位進(jìn)行設(shè)置的,但是這個(gè)必須要自己給其賦正確的值
/*在stm32f10_tim.c文件中是這樣操作的
TIMx-》CR1 |= (uint32_t)TIM_TimeBaseInitStruct-》TIM_ClockDivision |
TIM_TimeBaseInitStruct-》TIM_CounterMode;
*/
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
/*計(jì)數(shù)模式,st32f10x_tim.h中定義,其他可用的符號(hào)還有:
TIM_CounterMode_Up,TIM_CounterMode_Down,TIM_CounterMode_CenterAligned1
TIM_CounterMode_CenterAligned2,TIM_CounterMode_CenterAligned3
*/
TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure);
/* Prescaler configuration */
TIM_PrescalerConfig(TIM2, 4, TIM_PSCReloadMode_Immediate);
/*stm32f10x_tim.c中相關(guān)代碼:
TIMx-》PSC = Prescaler;
*/
/* Output Compare Timing Mode configuration: Channel1 *///輸出比較模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val; //預(yù)置值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //對(duì)CCER中CCxP的操作
TIM_OC1Init(TIM2, TIM_OCInitStructure);
/*對(duì)捕獲/比較使能寄存器(TIMx_CCER)進(jìn)行操作,置CC1E為1 */
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);
/* Output Compare Timing Mode configuration: Channel2 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
//這個(gè)是將CCR2_Val的值送到TMI2_CCR2中
TIM_OC2Init(TIM2, TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);
/* Output Compare Timing Mode configuration: Channel3 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
TIM_OC3Init(TIM2, TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable);
/*
*/
/* Output Compare Timing Mode configuration: Channel4 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
TIM_OC4Init(TIM2, TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Disable);
/* TIM IT enable */
TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);
對(duì)DIER寄存器操作,中斷允許配置,相關(guān)代碼如下:
而DIER寄存器如下:
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
//開啟定時(shí)器的運(yùn)行
while (1);
}
解讀:
(1) 時(shí)鐘來(lái)源 CK_INT,設(shè)置的方法是讓TIMx_SMCR中的SMS[2:0]=000,最后通過(guò)TIM_Cmd(TIM2, ENABLE);函數(shù)將TIMx_CR1中的CEN置1,開啟定時(shí)器的運(yùn)行;
?。?) 接下來(lái)設(shè)置定時(shí)器的基本參數(shù)
(3) 然后是設(shè)置定時(shí)器的各個(gè)通道
?。?) 最后開啟定時(shí)器運(yùn)行
最后,將這個(gè)例子稍加修改,令其運(yùn)行在我的實(shí)驗(yàn)板上。
需要修改的僅是將其原來(lái)的輸出從GPIOC的第6~第9腳變?yōu)镚PIOD的第8~第11腳。為此,需要改的地方有:
將藍(lán)色框內(nèi)的GPIOC改為GPIOD。這個(gè)是最先寫的,但實(shí)際上一開始根本沒注意到這個(gè)地方,沒有改成GPIOD,結(jié)果一仿真,不正確,再一細(xì)查,原來(lái)端口還要配置時(shí)鐘,改過(guò)來(lái)就對(duì)了。瞧,這不驗(yàn)證了上面的說(shuō)法“認(rèn)識(shí)事物是螺旋式上升的”學(xué)了定時(shí)器,對(duì)于I/O口和時(shí)鐘又有了更進(jìn)一步的理解了。
將紅色框內(nèi)的GPIO分別改成8,9,10,11,將藍(lán)色框內(nèi)的GPIOC改為GPIOD。
最后,到stm32f10x_it.c中,修改相應(yīng)的輸出
參考上圖紅色框內(nèi),將GPIOC改為GPIOD,將6腳改為8腳,其他部分類推。
修改好后運(yùn)行,所有燈亮了,看不出效果,于是又將預(yù)分頻系數(shù)由4改為64,這樣一來(lái),LED開始閃爍了。本文引用地址:http://m.butianyuan.cn/article/170756.htm
單片機(jī)相關(guān)文章:單片機(jī)教程
單片機(jī)相關(guān)文章:單片機(jī)視頻教程
單片機(jī)相關(guān)文章:單片機(jī)工作原理
分頻器相關(guān)文章:分頻器原理 塵埃粒子計(jì)數(shù)器相關(guān)文章:塵埃粒子計(jì)數(shù)器原理 晶振相關(guān)文章:晶振原理 鎖相環(huán)相關(guān)文章:鎖相環(huán)原理
評(píng)論