你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

stm32f103之滴答定时器

[复制链接]
STMCU小助手 发布时间:2023-3-8 13:00
滴答定时器:
        定时器的本质就是计数器。我们设置一个定值,然后计数器开始计数,从我们给的定值开始往下一直数,当数到0时,就做相应的动作(也可以不做什么,当把它用作延时计时的时候)。

滴答定时器systick是一个内核外设(即:内核自带的)

        Systick是一个24bit的系统定时器(stm32F407的寄存器名字与位数都与f103一样,但是有些芯片定时器位数不同),向下计数(从定值开始数到0),当计数到0时,在下一个时钟边沿,会重复计数。所以如果我们用完定时器后,不再继续使用它时,记得要将定时器关掉,否则它就一直在重复计数,增加了功耗。

Systick的作用

1.产生一个精准的定时
2.FreeRTOS时基由Systick提供
      
在说定时器前,我们先来看一看如何计数?
@S}}FXGPMXJCVME%JJGH.png
配置滴答定时器:


2e4242cbd0334a8dbcc333256a6d9fcb.png

        红框中就是滴答定时器的时钟,我们可以看到分频系数为8,所以滴答定时器的频率为:72M/8 = 9Mhz

配置滴答定时器我们需要下面几个寄存器:

fb7ea8d208f14b14975bb8c2c2b8daac.png

注意:第16位,是定时完成标志位,每当计数到0时,该标志位就会置1。

439efd5325344681aa687218ae27eeac.png

注意:由于定时器是24位的,所以给定时器装载的定值是有一个范围的,红框中就是范围,范围为0x000001 ~ 0xFFFFFF   

即:1~     也就是:1 ~ 16,777,215,如果填入的定值不是在0x000001 ~ 0xFFFFFF这个范围,则无法定时计数。

d1b8265cf617451caef6d6a879b01593.png

编程
产生Nms定时编程步骤:
        1》配置时钟源---AHB8分频,并且关闭定时器-----初始化(while(1)之前)
        2》设置计数值---N*9000
        3》清除当前值寄存器----注意:清除当前值寄存器,STK_CTRL寄存器的第16位会自动清0
        4》打开定时器
        5》等待定时器结束
        6》关闭定时器

代码:

  1. uint32_t fu_ms;  // 1ms需要计的次数
  2. uint32_t fu_us;  // 1us需要计的次数


  3. void Systick_Config(uint32_t Sysclk)        //Sysclk就是系统时钟频率72Mhz,但是这里用72表示72Mhz
  4. {
  5.     // 1》配置时钟源---AHB8分频,并且关闭定时器-----初始化(while(1)之前)
  6.     SysTick->CTRL  &=~0x05;        //这里关闭定时器的原因是,有可能之前没有关闭,所以一开始先将其关闭,再进行配置

  7.     fu_us  =Sysclk/8;                // 72/8 = 9,即:表示9Mhz
  8.     fu_ms  =fu_us*1000;        //9*1000 = 9000,由前面知道,在9Mhz下,计数9000次为1ms
  9. }


  10. // 定时Nms程序,ms级延时
  11. void Systick_NmsDelay(uint32_t Nms)
  12. {
  13.     uint32_t temp;
  14.     // 2》设置计数值---N*9000
  15.     SysTick->LOAD =Nms * fu_ms;

  16.     // 3》清除当前值寄存器----因为清除当前值寄存器,STK_CTRL的第16位(定时完成标志位)就会自动清0
  17.     SysTick->VAL =0;        //清除标志位

  18.     // 4》打开定时器
  19.     SysTick->CTRL |=0x01;

  20.     // 5》等待定时器结束
  21.     do{
  22.         temp=SysTick->CTRL;        
  23.     }while(!(temp&(1<<16)));        //判断CTRL寄存器第16位是否为1。如果为1,则说明计数数到0了,此时计数完成

  24.     // 6》关闭定时器
  25.     SysTick->CTRL &=~0x01;
  26. }

复制代码

解析:
1.我们先来看程序,定时器程序为什么被分成  Systick_Config(uint32_t Sysclk)  、Systick_NmsDelay(uint32_t Nms)两部分来写?
        因为,Systick_Config(uint32_t Sysclk) 中的配置,在整个程序中只需要被初始化一次就行了,而Systick_NmsDelay(uint32_t Nms)是延时函数,它会被多次使用。如果将Systick_Config(uint32_t Sysclk)与Systick_NmsDelay(uint32_t Nms)写在一起的话,那么多次调用该函数时,原本只需要被初始化一次的语句,就会被多次执行,浪费CPU资源。

2.为什么程序中的这部分需要用do.....while而不用while()?

8df6add7dbcb4ca6b71a02a7ec75dea9.png

        这里需要用do....while而不用while,是因为需要先执行temp = SysTick->CTRL; 将SysTick->CTRL寄存器中的值赋值给变量temp,然后再对temp进行真假判断,即:!(temp&(1<<16))。temp&(1<<16)为读取SysTick->CTRL中的第16位是1还是0,如果第16位是1,则为真,但是由于非 "!" 的存在,所以 ! (temp & (1<<16))就为假,所以此时do.....while循环不再循环下去。如果第16位是0,则为假,但是由于非 "!" 的存在,所以 ! (temp & (1<<16))为真,所以此时do.....while循环将继续循环下去。等到第16位变为1,也就是定时完成标志位置1时,如上所述,while循环将不再循环下去,此时计数器计数完成。


3.  由上面知滴答定时器的计数器是24位的,其计数范围为 0x000001 ~ 0xFFFFFF ,即:1 ~ 16,777,215,那么如果我需要计的数超过了这个范围呢?我该怎么办?

        由前面知,计数9000下,延时1ms。所以,16,777,215可以延时1864ms,即:  =  1864 。也就是说,在9Mhz频率下,将计数器计满,可以实现延时1864ms。

        的确,如果我想要延时2000ms,那么由于超过计数器的计数范围,那么滴答定时器就不会正常工作了。


所以,我们改下一下程序:
  1. // 定时Nms程序
  2. void Systick_NmsDelay(uint32_t Nms)
  3. {
  4.     uint8_t  systick_flag=0;
  5.     uint32_t temp;


  6.     while(0==systick_flag){


  7.         if(Nms>1864){
  8.             SysTick->LOAD =1864 * fu_ms;
  9.             Nms =Nms -1864;
  10.         }else{
  11.             // 2》设置计数值---N*9000
  12.             SysTick->LOAD =Nms * fu_ms;
  13.             systick_flag=1;
  14.          }


  15.         // 3》清除当前值寄存器----因为清除当前值寄存器,STK_CTRL的第16位会清0
  16.         SysTick->VAL =0;

  17.         // 4》打开定时器
  18.         SysTick->CTRL |=0x01;

  19.         // 5》等待定时器结束
  20.         do{
  21.             temp=SysTick->CTRL;
  22.         }while(!(temp&(1<<16)));

  23.         // 6》关闭定时器
  24.         SysTick->CTRL &=~0x01;

  25.      }


  26. }

复制代码

这样的话,就解决了1864问题了。


4.us级的延时程序是什么?
  1. // 定时Nus程序
  2. void Systick_NusDelay(uint32_t Nus)
  3. {
  4.     uint32_t temp;
  5.     // 2》设置计数值---N*9
  6.     SysTick->LOAD =Nus * fu_us;        //us与ms的程序就只有这里改变了

  7.     // 3》清除当前值寄存器----因为清除当前值寄存器,STK_CTRL的第16位会清0
  8.     SysTick->VAL =0;

  9.     // 4》打开定时器
  10.     SysTick->CTRL |=0x01;

  11.     // 5》等待定时器结束
  12.     do{
  13.         temp=SysTick->CTRL;
  14.     }while(!(temp&(1<<16)));

  15.     // 6》关闭定时器
  16.     SysTick->CTRL &=~0x01;
  17. }

复制代码

注意:
        在FreeRTOS操作系统中,不能使用我们自己设置的Systick来进行延时,因为FreeRTOS时基是由Systick提供(裸机的时基是由晶振提供的)。这是什么意思?

        FreeRTOS时基是由Systick提供,时基是需要保持不变的,所以Systick寄存器中的值必须是固定的,是不允许被修改的。但是,如果我们在FreeRTOS中用我们手动设置的Systick来进行延时,我们必定会去修改Systick寄存器中的值来得到我们需要的延时。所以,此时如果在FreeRTOS中使用我们手动设置的Systick去进行延时,那么就会卡死。解决办法:使用软件延时,delay()函数来代替Systick延时。代码如下:

  1. //初始化延迟函数
  2. //SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
  3. //这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
  4. //SYSCLK:系统时钟频率
  5. void delay_init(u8 SYSCLK)
  6. {
  7.     u32 reload;
  8.      SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
  9.     fac_us=SYSCLK;                            //不论是否使用OS,fac_us都需要使用
  10.     reload=SYSCLK;                            //每秒钟的计数次数 单位为M      
  11.     reload*=1000000/configTICK_RATE_HZ;        //根据configTICK_RATE_HZ设定溢出时间
  12.                                             //reload为24位寄存器,最大值:16777216,在168M下,约合0.0998s左右   
  13.     fac_ms=1000/configTICK_RATE_HZ;            //代表OS可以延时的最少单位      
  14.     SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
  15.     SysTick->LOAD=reload;                     //每1/configTICK_RATE_HZ断一次   
  16.     SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK     
  17. }            

  18.                        

  19. //延时nus
  20. //nus:要延时的us数.   
  21. //nus:0~204522252(最大值即2^32/fac_us@fac_us=168)                                          
  22. void delay_us(u32 nus)
  23. {        
  24.     u32 ticks;
  25.     u32 told,tnow,tcnt=0;
  26.     u32 reload=SysTick->LOAD;                //LOAD的值            
  27.     ticks=nus*fac_us;                         //需要的节拍数
  28.     told=SysTick->VAL;                        //刚进入时的计数器值
  29.     while(1)
  30.     {
  31.         tnow=SysTick->VAL;   
  32.         if(tnow!=told)
  33.         {        
  34.             if(tnow<told)tcnt+=told-tnow;    //这里注意一下SYSTICK是一个递减的计数器就可以了.
  35.             else tcnt+=reload-tnow+told;        
  36.             told=tnow;
  37.             if(tcnt>=ticks)break;            //时间超过/等于要延迟的时间,则退出.
  38.         }  
  39.     };                                            
  40. }  


  41. //延时nms
  42. //nms:要延时的ms数
  43. //nms:0~65535
  44. void delay_ms(u32 nms)
  45. {   
  46.     if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
  47.     {        
  48.         if(nms>=fac_ms)                        //延时的时间大于OS的最少时间周期
  49.         {
  50.                vTaskDelay(nms/fac_ms);             //FreeRTOS延时
  51.         }
  52.         nms%=fac_ms;                        //OS已经无法提供这么小的延时了,采用普通方式延时   
  53.     }
  54.     delay_us((u32)(nms*1000));                //普通方式延时
  55. }

  56. //延时nms,不会引起任务调度
  57. //nms:要延时的ms数
  58. void delay_xms(u32 nms)
  59. {
  60.     u32 i;
  61.     for(i=0;i<nms;i++) delay_us(1000);
  62. }
  63. ————————————————
  64. 版权声明:本文为CSDN博主「无敌小小雷」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
  65. 原文链接:https://blog.csdn.net/qq_39577221/article/details/125153214
复制代码

————————————————
版权声明:无敌小小雷
如有侵权请联系删除

收藏 评论0 发布时间:2023-3-8 13:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版