STM32延时函数的几种方法

[复制链接]
1325|0
 楼主| 王小琪 发表于 2023-1-9 18:57 | 显示全部楼层 |阅读模式
本帖最后由 王小琪 于 2023-1-9 18:59 编辑

延时函数是基本上在所有的项目中都会遇到的一个功能、和软件工程师的HELLO WORLD!一样,几乎每个玩单片机的上手第一个项目就是点灯,因此许多人自称“点灯工程师”,点灯常用的闪烁基本逻辑就是亮0.5s,灭0.5s,重复动作就是闪烁的效果。上面就用到了延时函数,由此可见一斑,延时函数是多么的重要,于是我在这里抛砖引玉,介绍几种延时函数的方式,下面用野火核心板进行演示,演示的功能都是一样的,间隔一段时间LED状态进行翻转,同时进行串口打印。
一、软件延时
  1. //  软件延时函数,使用不同的系统时钟,延时不一样
  2. void Delay(__IO uint32_t nCount)        
  3. {
  4.         for(; nCount != 0; nCount--);
  5. }

  6. int main(void)
  7. {        
  8.         HSE_SetSysClock(RCC_PLLMul_9);//SYSCLK  8*9=72M
  9.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);          //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  10.         uart_init(115200);         //串口初始化为115200
  11.         LED_Init();
  12.            while(1)
  13.         {
  14.                 LED0=!LED0;
  15.                 printf("\r\n今天是个好日子~");
  16.                 Delay(5000000);
  17.         }         
  18. }

通过下面的对比可以看到,通过改变系统时钟SYSCLK,时间间隔改变了,因为单片机执行一步指令大概在几ns,频率越快,执行的动作越快,相对应的时间就越短。
还有一个问题就是Delay(5000000)差不多是0.8s,如果我希望延时刚好1s,很难找到这个临界值,导致延时不准。
总结:
优点:移植方便,无论你用51,还是PIC,还是STM32基本都可以用这个延时函数,移植性强。
缺点:不精准,对不同的单片机,延时时间也是不一样的,只能大概知道延时时间。


二、滴答定时器
下面展示的是main.cdelay.c部分函数
  1. int main(void)
  2. {        
  3.         HSE_SetSysClock(RCC_PLLMul_9);//SYSCLK  8*5=40M
  4.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);          //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  5.         uart_init(115200);         //串口初始化为115200
  6.         LED_Init();
  7.         delay_init();
  8.            while(1)
  9.         {
  10.                 LED0=!LED0;
  11.                 printf("\r\n今天是个好日子~");
  12.                 delay_ms(1000);
  13.         }         
  14. }
  1. //SYSTICK的时钟固定为HCLK时钟的1/8
  2. //SYSCLK:系统时钟
  3. void delay_init()
  4. {
  5.         SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);        //选择外部时钟  HCLK/8
  6.         fac_us=9;//该处的值为SYSCLK/8M,对应关系为72M--9;64M--8;56M--7…………
  7.         fac_ms=(u16)fac_us*1000;//代表每个ms需要的systick时钟数   
  8. }                                                                    
  9. //延时nus,nus为要延时的us数.                                                                                       
  10. void delay_us(u32 nus)
  11. {               
  12.         u32 temp;                     
  13.         SysTick->LOAD=nus*fac_us;                                         //时间加载                           
  14.         SysTick->VAL=0x00;                                                //清空计数器
  15.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数         
  16.         do
  17.         {
  18.                 temp=SysTick->CTRL;
  19.         }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
  20.         SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
  21.         SysTick->VAL =0X00;                                               //清空计数器         
  22. }
  23. //延时nms
  24. //注意nms的范围
  25. //SysTick->LOAD为24位寄存器,所以,最大延时为:
  26. //nms<=0xffffff*8*1000/SYSCLK
  27. //SYSCLK单位为Hz,nms单位为ms
  28. //对72M条件下,nms<=1864
  29. void delay_ms(u16 nms)
  30. {                                    
  31.         u32 temp;                  
  32.         SysTick->LOAD=(u32)nms*fac_ms;                                //时间加载(SysTick->LOAD为24bit)
  33.         SysTick->VAL =0x00;                                                        //清空计数器
  34.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数  
  35.         do
  36.         {
  37.                 temp=SysTick->CTRL;
  38.         }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
  39.         SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
  40.         SysTick->VAL =0X00;                                               //清空计数器                     
  41. }




可以看到滴答定时器延时比较准,delay_ms(1000)就刚好是1s,有一点需要注意的是,修改了SYSCLK之后,在delay.c文件里面的系数也要修改。
总结:
优点:延时精准。
缺点:不适用其他型号单片机,不方便移植。




三、使用定时器进行定时,不使用中断

  1. int main(void)
  2. {        
  3.         HSE_SetSysClock(RCC_PLLMul_9);//SYSCLK  8*9=72M
  4.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  5.         uart_init(115200);         //串口初始化为115200
  6.         LED_Init();
  7.         TIM3_Init();
  8.            while(1)
  9.         {
  10.                 LED0=!LED0;
  11.                 printf("\r\n今天是个好日子~");
  12.                 TIM3_Delayms(1000);
  13.         }         
  14. }
  1. void TIM3_Init(void)
  2. {
  3.         TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  4.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  ///使能TIM3时钟
  5.         TIM_TimeBaseInitStructure.TIM_Period    = 50000-1;         //自动重装载值
  6.         TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;     //定时器分频
  7.         TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
  8.         TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
  9.         TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化TIM3
  10. }

  11. //微秒级延时
  12. void TIM3_Delayus(u16 xus)
  13. {
  14.         TIM_Cmd(TIM3,ENABLE); //启动定时器
  15.         while(TIM3->CNT < xus);
  16.         TIM3->CNT = 0;
  17.         TIM_Cmd(TIM3,DISABLE); //关闭定时器
  18. }

  19. //毫秒级延时
  20. void TIM3_Delayms(u16 xms)
  21. {
  22.         int i;
  23.         for(i=0;i<xms;i++)
  24.         {
  25.                 TIM3_Delayus(1000);
  26.         }
  27. }







可以看到延时也比较准,TIM3_Delayms(1000)就刚好是1s,有一点需要注意的是,修改了SYSCLK之后,在time.c文件里面的预分频系数需要改。
总结:
优点:延时精准。
缺点:不适用其他型号单片机,不方便移植。



当然延时函数的方法有很多种,也欢迎其他小伙伴一起谈论。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
您需要登录后才可以回帖 登录 | 注册

本版积分规则

232

主题

585

帖子

7

粉丝
快速回复 在线客服 返回列表 返回顶部