STM32学习笔记之定时器的配置

时间:2022-12-30 23:21:09

转载于 http://blog.csdn.net/u010661782/article/details/50281229,感谢原作者非常详细的指导学习

实验目的:

让蜂鸣器每隔一秒响一次;


实验步骤:

STM32学习笔记之定时器的配置


实验程序:

[cpp]  view plain  copy
  1. /************************led.c***********************/  
  2. #include "stm32f4xx.h"  //在SYSTEM目录下可以找到  
  3. #include "sys.h"  
  4.   
  5.   
  6. void LED_Init(void){  
  7.       
  8.     RCC->AHB1ENR |= 1<<5;  //使能GPIO端口的F时钟  
  9.     
  10.     GPIO_Set(GPIOF,PIN9|PIN10,GPIO_MODE_OUT,GPIO_OTYPE_PP,GPIO_SPEED_25M,GPIO_PUPD_PU);  
  11.     PFout(9) = 1;  
  12.     PFout(10) = 1;  
  13.       
  14. }  


[cpp]  view plain  copy
  1. /************************led.h***********************/  
  2. #ifndef _LED_H  
  3. #define _LED_H  
  4.   
  5.   
  6. void LED_Init(void);  
  7.   
  8.   
  9. #endif  


[cpp]  view plain  copy
  1. /************************beep.c***********************/  
  2. #include "sys.h"  
  3. #include "beep.h"  
  4.   
  5.   
  6. void Beep_Init(void){  
  7.     RCC->AHB1ENR |= 1<<5;          //使能PORTE时钟   
  8.     GPIO_Set(GPIOF,PIN8,GPIO_MODE_OUT,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PD); //PF8设置,下拉  
  9.     PFout(8) =0;                        //关闭蜂鸣器   
  10. }  


[cpp]  view plain  copy
  1. /************************beep.h***********************/  
  2. #include "sys.h"  
  3.   
  4.   
  5. #ifndef _BEEP_H  
  6. #define _BEEP_H  
  7.   
  8.   
  9. void Beep_Init(void);  
  10.   
  11.   
  12. #endif  



[cpp]  view plain  copy
  1. /************************timer.c***********************/  
  2. #include "sys.h"  
  3. #include "stm32f4xx.h"  
  4.   
  5.   
  6. /* 
  7. 本示例的作用就是, 
  8. 让蜂鸣器每隔一秒响一次; 
  9. */  
  10.   
  11.   
  12. /* 
  13. 关于定时器的操作可参考寄存器版的步骤 
  14. */  
  15.   
  16.   
  17. /* 
  18. 中断初始化函数: 
  19. 主要是关于寄存器的相关配置 
  20. */  
  21. void TIM3_Init(void){  
  22.       
  23.     /************************* 
  24.     定时器中断的设置:    
  25.     *************************/  
  26.       
  27.     //使能TIM3时钟;  
  28.     RCC->APB1ENR |= 1 << 1;    
  29.       
  30.     /*预分频器*/  
  31.     TIM3->PSC = 8400 - 1;  
  32.       
  33.     /*ARR为自动重载寄存器*/  
  34.     TIM3->ARR = 10000 -1 ;  
  35.       
  36.     //使能更新中断  
  37.     TIM3->DIER |= 1;  
  38.       
  39.     //使能计数器  
  40.     TIM3->CR1 |= 1;  
  41.       
  42.       
  43.     /*TIM中断分组设置*/  
  44.       
  45.     //SCB和NVIC,可参考STM32F3与STM32F4系列Cortex M4内核编程手册.pdf  
  46.     SCB->AIRCR |= 0x5 << 8; //设置分组  
  47.     NVIC->IP[29] |= 0; //设置优先级,具体可分析MY_NVIC_Init()函数;  
  48.       
  49.     //若不使能,则中断不会发生  
  50.     NVIC->ISER[0] |= 1 << 29;  //使能中断;  
  51.       
  52. }  
  53.   
  54.   
  55.   
  56. /*每1000ms产生一次中断*/  
  57. void TIM3_IRQHandler(void){  
  58.       
  59.     /* 
  60.     中断处理函数: 
  61.     */  
  62.       
  63.   
  64.     if(TIM3->SR&0X0001){  
  65.         PFout(8) = !PFout(8);  
  66.     }  
  67.       
  68.       
  69.     /* 
  70.     在中断里边最后记得清中断标志: 
  71.     */  
  72.     TIM3->SR &= ~(1 << 0);  
  73. }   



[cpp]  view plain  copy
  1. /*************************timer.h**********************/  
  2. #ifndef _TIMER_H  
  3. #define _TIMER_H  
  4.   
  5.   
  6. void TIM3_Init(void);  
  7.   
  8.   
  9. #endif  


[cpp]  view plain  copy
  1. /*************************test.c**********************/  
  2. #include "sys.h"  
  3. #include "delay.h"  
  4. #include "beep.h"  
  5. #include "timer.h"  
  6. #include "led.h"  
  7.   
  8.   
  9. int main(void){  
  10.       
  11.     Stm32_Clock_Init(336,8,2,7);//设置时钟,168Mhz  //思考一下为啥要设置整个时钟(到时可参考时钟那章节);  
  12.     delay_init(168);        //初始化延时函数  
  13.     LED_Init();  
  14.     Beep_Init();  
  15.     TIM3_Init();  
  16.       
  17.     while(1){  
  18.         PFout(9) = 0;  
  19.         delay_ms(1000);  
  20.         PFout(9) = 1;  
  21.         delay_ms(1000);  
  22.     }  
  23.       
  24. }  



实验分析:

我们主要分析一下定时器中断中,关于寄存器的设置问题

1. RCC->APB1ENR |= 1 << 1;  

这条语句的作用就是:使能TIM3时钟;

外设这么多,我咋知道,我们应该在哪条总线上去使能响应的外设?

第一种办法就是查看RCC寄存器,看其每个寄存器的每位的解释;

第二种办法就是看对应芯片的整个框图,如:

STM32学习笔记之定时器的配置

由上述视图,我们可知:TIM3是挂在APB1总线上的,所以我们只需使能APB1ENR寄存器上的响应的位就可以达到我们使能响应的外设的时钟的效果;

查看APB1ENR寄存器,我们可知:

STM32学习笔记之定时器的配置

STM32学习笔记之定时器的配置

STM32学习笔记之定时器的配置

STM32学习笔记之定时器的配置

STM32学习笔记之定时器的配置

所以在这里就写成了RCC->APB1ENR |= 1 << 1


2. TIM3->PSC = 8400 - 1;

3. TIM3->ARR = 10000 -1 ;

第2条语句和第3条语句的作用就是设置预分频器和自动重载寄存器;

但是在设置这两个寄存器之前,我们必须要搞清楚一点就是:计数器的时钟频率从哪里来?

查看定时器的框图:

STM32学习笔记之定时器的配置

STM32学习笔记之定时器的配置

STM32学习笔记之定时器的配置

注:由上述的定时器框图,我们可以得知:CK_INT也就是来自RCC的TIMxCLK


再结合第5条语句,其作用就是将CEN位写入1,所以,这样,我们就将预分频器的时钟CK_PSC设置为了内部时钟CK_INT;

此时,我们需要搞清楚CK_INT的时钟多少,在test.c中,我们看到:Stm32_Clock_Init()这个函数,其函数体如下:

[cpp]  view plain  copy
  1. void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq)  
  2. {    
  3.     RCC->CR|=0x00000001;     //设置HISON,开启内部高速RC振荡  
  4.     RCC->CFGR=0x00000000;        //CFGR清零   
  5.     RCC->CR&=0xFEF6FFFF;     //HSEON,CSSON,PLLON清零   
  6.     RCC->PLLCFGR=0x24003010; //PLLCFGR恢复复位值   
  7.     RCC->CR&=~(1<<18);         //HSEBYP清零,外部晶振不旁路  
  8.     RCC->CIR=0x00000000;     //禁止RCC时钟中断   
  9.     Sys_Clock_Set(plln,pllm,pllp,pllq);//设置时钟   
  10.     //配置向量表                 
  11. #ifdef  VECT_TAB_RAM  
  12.     MY_NVIC_SetVectorTable(1<<29,0x0);  
  13. #else     
  14.     MY_NVIC_SetVectorTable(0,0x0);  
  15. #endif   
  16. }             

接着再追踪Sys_Clock_Set()函数,进到其函数体内,我们可以看到:

[cpp]  view plain  copy
  1. //时钟设置函数  
  2. //Fvco=Fs*(plln/pllm);  
  3. //Fsys=Fvco/pllp=Fs*(plln/(pllm*pllp));  
  4. //Fusb=Fvco/pllq=Fs*(plln/(pllm*pllq));  
  5.   
  6. //Fvco:VCO频率  
  7. //Fsys:系统时钟频率  
  8. //Fusb:USB,SDIO,RNG等的时钟频率  
  9. //Fs:PLL输入时钟频率,可以是HSI,HSE等.   
  10. //plln:主PLL倍频系数(PLL倍频),取值范围:64~432.  
  11. //pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.  
  12. //pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)  
  13. //pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.  
  14.   
  15. //外部晶振为8M的时候,推荐值:plln=336,pllm=8,pllp=2,pllq=7.  
  16. //得到:Fvco=8*(336/8)=336Mhz  
  17. //     Fsys=336/2=168Mhz  
  18. //     Fusb=336/7=48Mhz  
  19. //返回值:0,成功;1,失败。  
  20. u8 Sys_Clock_Set(u32 plln,u32 pllm,u32 pllp,u32 pllq)  
  21. {   
  22.     u16 retry=0;  
  23.     u8 status=0;  
  24.     RCC->CR|=1<<16;                //HSE 开启   
  25.     while(((RCC->CR&(1<<17))==0)&&(retry<0X1FFF))retry++;//等待HSE RDY  
  26.     if(retry==0X1FFF)status=1;  //HSE无法就绪  
  27.     else     
  28.     {  
  29.         RCC->APB1ENR|=1<<28;   //电源接口时钟使能  
  30.         PWR->CR|=3<<14;        //高性能模式,时钟可到168Mhz  
  31.         RCC->CFGR|=(0<<4)|(5<<10)|(4<<13);//HCLK 不分频;APB1 4分频;APB2 2分频.   
  32.         RCC->CR&=~(1<<24); //关闭主PLL  
  33.         RCC->PLLCFGR=pllm|(plln<<6)|(((pllp>>1)-1)<<16)|(pllq<<24)|(1<<22);//配置主PLL,PLL时钟源来自HSE  
  34.         RCC->CR|=1<<24;            //打开主PLL  
  35.         while((RCC->CR&(1<<25))==0);//等待PLL准备好   
  36.         FLASH->ACR|=1<<8;      //指令预取使能.  
  37.         FLASH->ACR|=1<<9;      //指令cache使能.  
  38.         FLASH->ACR|=1<<10;     //数据cache使能.  
  39.         FLASH->ACR|=5<<0;      //5个CPU等待周期.   
  40.         RCC->CFGR&=~(3<<0);        //清零  
  41.         RCC->CFGR|=2<<0;       //选择主PLL作为系统时钟     
  42.         while((RCC->CFGR&(3<<2))!=(2<<2));//等待主PLL作为系统时钟成功.   
  43.     }   
  44.     return status;  
  45. }    
在看上述函数时,我们需要结合以下一幅图:

STM32学习笔记之定时器的配置

从芯片手册中,我们还可以找到一句话:

STM32学习笔记之定时器的配置

而在Sys_Clock_Set()函数中,我们可以对照寄存器的设置,得知:

AHB不分频,APB1 4分频,APB2  2分频,且可得知:AHB的频率为168MHZ;

而定时器3又是挂在APB1总线上的,所以由于APB1预分频器为4,并不为1,所以,

定时器的时钟频率等于APB1的频率的2倍,而APB1的时钟频率等于AHB频率的1/4,

所以可知:定时器的时钟频率为84MHZ;而定时器时钟也就是RCC的TIM3CLK的时钟,

所以,内部时钟CK_INT的时钟频率为84MHZ;由此,我们设置预分频器和计数器才有意义;


注:在这里再唠叨一句的是:在这里将预分频器减一的原因是:查看TIMxPSC寄存器,其下方的

解释可以得知:计数器时钟频率CK_CNT等于CK_PSC的频率/(PSC[15:0] + 1),所以在这里就将

预分频器减一;而将自动装载的值减一的原因是:若我们设置自动装载的值为5000,那么计数器

从0开始计数,然后计数到5000,然后又从5000跳到0,当跳到0时,其中断才会触发,所以,

此时,计数器实际上是计了5001次,所以,我们需要将其减一;

STM32学习笔记之定时器的配置



4. TIM3->DIER |= 1;

其作用就是使能更新中断


5. TIM3->CR1 |= 1;

其作用就是使能计数器


6. SCB->AIRCR |= 0x5 << 8;

7. NVIC->IP[29] |= 0;

8. NVIC->ISER[0] |= 1 << 29;

第6,7,8条语句的解释,可参考STM32的外部中断那篇博客中对应部分的解释;



注意事项:

1.在上述中断函数中,若没有加if(TIM3->SR&0X0001)这条语句,
那么蜂鸣器始终不会响,若加了if(TIM3->SR&0X0001)这条语句,
那么蜂鸣器才会如程序中所示,正常工作;理论上,不加这条if语句,
蜂鸣器也会隔一段时间响一次的,但看到的 事实是蜂鸣器根本不会响;

至于其原因,暂时还没有找到合理的解释,现在这里载一笔;