養(yǎng)成良好的編程能力很重要!!!否則還以后的生活和工作當中會吃很大的虧的!
一、在MDK中進行對stm32的學習中用寄存器進行學習只需要很簡單的操作,比起用庫函數(shù)大大降低了學習的難度。。。
1)首先將system文件夾中的delay、sys以及usart三個文件夾復制到工程文件夾中并添加進工程當中去,然后再把一個啟動文件復制到工程當中去并添加即可。
二、LED跑馬燈的學習:
1)編寫LED初始化函數(shù),這其中包括GPIOX時鐘的使能,然后就是對應LED的端口的設置,其中包括輸入輸出模式以及輸出的形式(是高電平還是低電平);
2)編寫完成之后就是調用LED初始化函數(shù)進行試驗。代碼如下:
void LED_Init(void)
{
RCC->APB2ENR|=1<<3; //使能PORTB時鐘
RCC->APB2ENR|=1<<6; //使能PORTE時鐘
GPIOB->CRL&=0XFF0FFFFF; //輸出模式
GPIOB->CRL|=0X00300000;//PB.5 推挽輸出
GPIOB->ODR|=1<<5; //PB.5 輸出高
GPIOE->CRL&=0XFF0FFFFF;
GPIOE->CRL|=0X00300000;//PE.5推挽輸出
GPIOE->ODR|=1<<5; //PE.5輸出高
}
3)stm32中的IO口初始化之后默認的形式是下拉的。下面是幾種常見的輸入形式,最好記住
三、用串口發(fā)送和接受數(shù)據(jù)
1)首先肯定是使能串口時鐘和串口所對應的io口時鐘,設置io口的輸入輸出模式,然后復位串口、停止復位,設置波特率和校檢位。至此串口已經(jīng)初始化完畢。串口的波特率是根據(jù)以下公式算的以下是初始化串口的函數(shù)
void uart_init(u32 pclk2,u32 bound)
{
float temp;
u16 mantissa;
u16 fraction;
temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV
mantissa=temp; //得到整數(shù)部分
fraction=(temp-mantissa)*16; //得到小數(shù)部分
mantissa<<=4;//將最后的一個16進制位移開再與小數(shù)位相加
mantissa+=fraction;
RCC->APB2ENR|=1<<2; //使能PORTA口時鐘
RCC->APB2ENR|=1<<14; //使能串口時鐘
GPIOA->CRH&=0XFFFFF00F;//IO狀態(tài)設置
GPIOA->CRH|=0X000008B0;//IO狀態(tài)設置
RCC->APB2RSTR|=1<<14; //復位串口1
RCC->APB2RSTR&=~(1<<14);//停止復位
//波特率設置
USART1->BRR=mantissa; // 波特率設置
USART1->CR1|=0X200C; //1位起始,無校驗位.
#if EN_USART1_RX //如果使能了接收
//使能接收中斷
USART1->CR1|=1<<8; //PE中斷使能
USART1->CR1|=1<<5; //接收緩沖區(qū)非空中斷使能
MY_NVIC_Init(3,3,USART1_IRQn,2);//組2,最低優(yōu)先級
#endif
}
四、外部中斷事件
1)初始化IO口為輸入。
2)開啟IO口復用時鐘,設置IO口與中斷線的映射關系。APB2ENR寄存器
3)開啟與該IO口相對的線上事件,設置觸發(fā)事件的條件。EXTI->FTSR下降沿觸發(fā)發(fā)、EXTI->RTSR 上升沿觸
4)配置中斷分組(NVIC)并使能中斷。RCC_CIR寄存器
5)編寫中斷服務函數(shù)。
五、獨立看門狗事件(注意,在初始化時鐘或者是計數(shù)器的時候應該是最后才使能時鐘或者是計數(shù)器)
1)在鍵寄存器(IWDG_KR)中寫入0xCCCC,開始啟用獨立看門狗
2)IWDG_PR和IWDG_RLR寄存器具有寫保護功能。要修改這兩個寄存器的值,必須先向IWDG_KR寄存器中寫入0x5555。以不同的值寫入這個寄存器將會打亂操作順序,寄存器將重新被保護。重裝載操作(即寫入0xAAAA)也會啟動寫保護功能。
void IWDG_Init(u8 prer,u16 rlr)
{
IWDG->KR=0X5555;//使能對IWDG->PR和IWDG->RLR的寫,因為KR寄存器有寫保護功能
IWDG->PR=prer; //設置分頻系數(shù)
IWDG->RLR=rlr; //從加載寄存器 IWDG->RLR
IWDG->KR=0XAAAA;//reload
IWDG->KR=0XCCCC;//使能看門狗
}
3)獨立看門狗計數(shù)器一旦開始后就要不斷地對其進行喂狗操作,否則當其被計數(shù)到零時就會引起系統(tǒng)復位。喂狗操作就是想KR寄存器中寫入0xAAAA,使其自動進行重載操作。
void IWDG_Feed(void)
{
IWDG->KR=0XAAAA;//reload
}
六、窗口看門狗事件
1)首先使能窗口看門狗時鐘,在RCC->APB1ENR中的11位置1
2)控制寄存器的配置(WWDG->CR),這個寄存器只有低7位有效。第7位為看門狗使能位,[6:0]為看門狗計數(shù)器的值,當?shù)?位為0時就會引起系統(tǒng)復位。
3)看門狗配置寄存器(WWWDG_CFR),這個寄存器為低10位有效其中[6:0]位為窗口值;值得注意的是只有當計數(shù)器的值小于窗口值時并且大于0x40時才能重新寫入計數(shù)器的值,否則會引起系統(tǒng)復位。不管是提前還是延后都會引起系統(tǒng)復位。具體在stm32中文參考手冊的321頁
4)窗口看門狗的狀態(tài)寄存器WWDG_SR只有一位,但是起作用卻很重要。當該寄存器的0位置1時為提前喚醒中斷標志,即當控制寄存器的中的計數(shù)器的值從0x40轉變?yōu)?x3f時會引起中,可以利用這個中斷來進行喂狗。以下是初始化窗口看門狗的初始化函數(shù)
//初始化窗口看門狗
//tr :T[6:0],計數(shù)器值
//wr :W[6:0],窗口值
//fprer:分頻系數(shù)(WDGTB),僅最低2位有效
//Fwwdg=PCLK1/(4096*2^fprer).
void WWDG_Init(u8 tr,u8 wr,u8 fprer)
{
RCC->APB1ENR|=1<<11; //使能wwdg時鐘
WWDG_CNT=tr&WWDG_CNT; //初始化WWDG_CNT.
WWDG->CFR|=fprer<<7; //PCLK1/4096再除2^fprer
WWDG->CFR&=0XFF80;
WWDG->CFR|=wr; //設定窗口值
WWDG->CR|=WWDG_CNT; //設定計數(shù)器值
WWDG->CR|=1<<7; //開啟看門狗
MY_NVIC_Init(2,3,WWDG_IRQn,2);//搶占2,子優(yōu)先級3,組2
WWDG->SR=0X00; //清除提前喚醒中斷標志位
WWDG->CFR|=1<<9; //使能提前喚醒中斷
}
中斷服務函數(shù)為
void WWDG_IRQHandler(void)
{
WWDG_Set_Counter(WWDG_CNT);//重設窗口看門狗的值!
WWDG->SR=0X00;//清除提前喚醒中斷標志位
LED1=!LED1;
BEEP=0;
}
七、通用計數(shù)器的使用
通用計數(shù)器的初始化步驟如下
1)使能計數(shù)器的時鐘RCC->APB1ENRd的第1位
2)設置自動加載寄存器的值,即為計數(shù)值
3)設置預分頻器的分頻值
4)設置計數(shù)器的計數(shù)防方向,是向下還是向上計數(shù),默認狀態(tài)下是向上計數(shù)的
5)使能計數(shù)器
6)設置計數(shù)器的中斷方式并編寫中斷服務函數(shù)(當計數(shù)器溢出時會響應一個中斷,中斷使能寄存器的0位會置1,但是前提是在DMA/中斷使能寄存器中將更新事件的中斷使能位置1)
//通用定時器3中斷初始化
//這里時鐘選擇為APB1的2倍,而APB1為36M
//arr:自動重裝值。
//psc:時鐘預分頻數(shù)
//這里使用的是定時器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
RCC->APB1ENR|=1<<1; //TIM3時鐘使能
TIM3->ARR=arr; //設定計數(shù)器自動重裝值//剛好1ms
TIM3->PSC=psc; //預分頻器7200,得到10Khz的計數(shù)時鐘
TIM3->DIER|=1<<0; //允許更新中斷
TIM3->CR1|= 1<<4; //設置計數(shù)器是向下計數(shù),默認狀態(tài)下是向上計數(shù)的
TIM3->CR1|=0x01; //使能定時器3
MY_NVIC_Init(1,3,TIM3_IRQn,2);//搶占1,子優(yōu)先級3,組2
}
//定時器3中斷服務程序
void TIM3_IRQHandler(void)
{
if(TIM3->SR&0X0001)//溢出中斷,當一個更新事件到來的時候,狀態(tài)寄存器的0位就會置位,否則為零
{
LED1=!LED1;
}
TIM3->SR&=~(1<<0);//清除中斷標志位 ,0為無更新事件發(fā)生
}
八、用timer3進行PWM輸出
1)首先是使能計數(shù)器3的時鐘,并且要使能相應管腳的時鐘
2)設置計數(shù)器復用相應管腳的輸出模式,一般是設置為推挽輸出
3)使能管腳復用時鐘
4)設置計數(shù)器自動重載值和預分頻器的值
5)設置PWM的輸出模式
6)使能預裝載寄存器
7)進行輸出使能和計數(shù)器使能
//TIM3 PWM部分初始化
//PWM輸出初始化
//arr:自動重裝值
//psc:時鐘預分頻數(shù)
void TIM3_PWM_Init(u16 arr,u16 psc)
{
//此部分需手動修改IO口設置
RCC->APB1ENR|=1<<1; //TIM3時鐘使能
RCC->APB2ENR|=1<<3; //使能PORTB時鐘
GPIOB->CRL&=0XFF0FFFFF; //PB5輸出
GPIOB->CRL|=0X00B00000; //復用功能輸出
RCC->APB2ENR|=1<<0; //開啟輔助時鐘
AFIO->MAPR&=0XFFFFF3FF; //清除MAPR的[11:10]
AFIO->MAPR|=1<<11; //部分重映像,TIM3_CH2->PB5
TIM3->ARR=arr; //設定計數(shù)器自動重裝值
TIM3->PSC=psc; //預分頻器不分頻
TIM3->CCMR1|=7<<12; //CH2 PWM2模式
TIM3->CCMR1|=1<<11; //CH2預裝載使能
TIM3->CCER|=1<<4; //OC2 輸出使能
TIM3->CR1=0x0080; //ARPE使能
TIM3->CR1|=0x01; //使能定時器3
}
九、輸入捕獲實驗 (以TIMER5為例)
1)開啟TIM5時鐘,配置PA0為下拉輸入
2)設置TIM5的ARR和PSC
3)設置TIM5的CCMR1
4)設置TIM5的CCER,開啟輸入捕獲,并設置為上升沿捕獲
5)設置TIM5的DIER,使能捕獲和更新中斷,并編寫中斷服務函數(shù)
6)設置TIM5的CR1,使能定時器
//定時器5通道1輸入捕獲配置
//arr:自動重裝值
//psc:時鐘預分頻數(shù)
void TIM5_Cap_Init(u16 arr,u16 psc)
{
RCC->APB1ENR|=1<<3; //TIM5 時鐘使能
RCC->APB2ENR|=1<<2; //使能PORTA時鐘
GPIOA->CRL&=0XFFFFFFF0; //PA0 清除之前設置
GPIOA->CRL|=0X00000008; //PA0 輸入
GPIOA->ODR|=0<<0; //PA0 下拉
TIM5->ARR=arr; //設定計數(shù)器自動重裝值
TIM5->PSC=psc; //預分頻器
TIM5->CCMR1|=1<<0; //CC1S=01 選擇輸入端 IC1映射到TI1上
TIM5->CCMR1|=0<<4; //IC1F=0000 配置輸入濾波器 不濾波
TIM5->CCMR1|=0<<10; //IC2PS=00 配置輸入分頻,不分頻
TIM5->CCER|=0<<1; //CC1P=0 上升沿捕獲
TIM5->CCER|=1<<0; //CC1E=1 允許捕獲計數(shù)器的值到捕獲寄存器中
TIM5->DIER|=1<<1; //允許捕獲中斷
TIM5->DIER|=1<<0; //允許更新中斷
TIM5->CR1|=0x01; //使能定時器2
MY_NVIC_Init(2,0,TIM5_IRQn,2);//搶占2,子優(yōu)先級0,組2
}
//捕獲狀態(tài)
//[7]:0,沒有成功的捕獲;1,成功捕獲到一次.
//[6]:0,還沒捕獲到高電平;1,已經(jīng)捕獲到高電平了.
//[5:0]:捕獲高電平后溢出的次數(shù)
u8 TIM5CH1_CAPTURE_STA=0; //輸入捕獲狀態(tài),這里的TIM5CH1_CAPTURE_STA當做一個 //寄存器來用
u16 TIM5CH1_CAPTURE_VAL; //輸入捕獲值
//定時器5中斷服務程序
void TIM5_IRQHandler(void)
{
u16 tsr;
tsr=TIM5->SR;
if((TIM5CH1_CAPTURE_STA&0X80)==0)//還未成功捕獲 ,第7位為捕獲完成標志位
{
if(tsr&0X01)//溢出
{
if(TIM5CH1_CAPTURE_STA&0X40)//已經(jīng)捕獲到高電平了
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高電平太長了,捕獲標志位全部為1
{
TIM5CH1_CAPTURE_STA|=0X80;//標記成功捕獲了一次
TIM5CH1_CAPTURE_VAL=0XFFFF;
}else TIM5CH1_CAPTURE_STA++;
}
}
if(tsr&0x02)//捕獲1發(fā)生捕獲事件
{
if(TIM5CH1_CAPTURE_STA&0X40) //捕獲到一個下降沿,由于前面已經(jīng)有了高電平的標志位了
{
TIM5CH1_CAPTURE_STA|=0X80; //標記成功捕獲到一次高電平脈寬
TIM5CH1_CAPTURE_VAL=TIM5->CCR1; //獲取當前的捕獲值.
TIM5->CCER&=~(1<<1); //CC1P=0 設置為上升沿捕獲
}else //還未開始,第一次捕獲上升沿
{
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM5CH1_CAPTURE_STA|=0X40; //標記捕獲到了上升沿
TIM5->CNT=0; //計數(shù)器清空
TIM5->CCER|=1<<1; //CC1P=1 設置為下降沿捕獲
}
}
}
TIM5->SR=0;//清除中斷標志位
}
十、電容觸摸按鍵實驗
1)電容觸摸按鍵的原理是根據(jù)電容值不同充放電的時間不同,用定時器對電容的充電時間的計時比較,就可以判斷是否有被觸摸
Vc=V0*(1-e^(-t/RC))
其中Vc為電容電壓,V0為充電電壓,R為充電電阻,C為電容容值,e為自然底數(shù),t為充電時間。根據(jù)這個公式,我們就可以計算出Cs和Cx。先用開關將Cs(或Cs+Cx)上的電放盡,然后斷開開關,讓R給Cs(或Cs+Cx)充電,當沒有手指觸摸的時候,Cs的充電曲線如圖中的A曲線。而當有手指觸摸的時候,手指和TPAD之間引入了新的電容Cx,此時Cs+Cx的充電曲線如圖中的B曲線。從上圖可以看出,A、B兩種情況下,Vc達到Vth的時間分別為Tcs和Tcs+Tcx。,就已經(jīng)可以實現(xiàn)觸摸檢測了,當充電時間在Tcs附近,就可以認為沒有觸摸,而當充電時間大于Tcs+Tx時,就認為有觸摸按下(Tx為檢測閥值)。
2)、實驗方法如下:本章,我們使用PA1(TIM5_CH2)來檢測TPAD是否有觸摸,在每次檢測之前,我們先配置PA1為推挽輸出,將電容Cs(或Cs+Cx)放電,然后配置PA1為浮空輸入,利用外部上拉電阻給電容Cs(Cs+Cx)充電,同時開啟TIM5_CH2的輸入捕獲,檢測上升沿,當檢測到上升沿的時候,就認為電容充電完成了,完成一次捕獲檢測。
3)、實驗原理圖如下
//定時器5通道2輸入捕獲配置
//arr:自動重裝值
//psc:時鐘預分頻數(shù)
void TIM5_CH2_Cap_Init(u16 arr,u16 psc)
{
//此部分需手動修改 IO口設置
RCC->APB1ENR|=1<<3; //TIM5 時鐘使能
RCC->APB2ENR|=1<<2; //使能PORTA時鐘
GPIOA->CRL&=0XFFFFFF0F; //PA1 輸入
GPIOA->CRL|=0X00000040; //浮空輸入
TIM5->ARR=arr; //設定計數(shù)器自動重裝值//剛好1ms
TIM5->PSC=psc; //預分頻器,1M的計數(shù)頻率
TIM5->CCMR1|=1<<8; //CC2S=01 選擇輸入端 IC2映射到TI2上
TIM5->CCMR1|=0<<12; //IC2F=0011 配置輸入濾波器 8個定時器時鐘周期濾波
TIM5->CCMR1|=0<<10; //IC2PS=00 配置輸入分頻,不分頻
TIM5->CCER|=0<<5; //CC2P=0 上升沿捕獲
TIM5->CCER|=1<<4; //CC2E=1 允許捕獲計數(shù)器的值到捕獲寄存器中
TIM5->CR1|=0x01; //使能定時器5
}
|