返回列表 发新帖我要提问本帖赏金: 30.00元(功能说明)

[技术讨论] 关于单片机延时的实现讨论

[复制链接]
1009|0
 楼主| 王小琪 发表于 2023-3-30 21:48 | 显示全部楼层 |阅读模式
一、单片机延时分类
1软件延时:在程序中使用循环等语句来消耗一定的时间,从而实现延时,这种方式简单易用,但会占用CPU资源,影响程序的响应性能。
2硬件延时:通过外部电路或者定时器等硬件模块来实现延时,这种方式可以不占用CPU资源,但需要占用额外的硬件资源,增加系统成本和复杂度。
3系统延时:一些单片机芯片提供了硬件延时和软件延时的组合方式,例如使用定时器和中断相结合的方式来实现精确的延时,这种方式既能够满足延时的精度要求,又能够减少CPU的占用率,提高系统的响应性能。
4外部时钟:通过外部时钟来实现精确的延时,这种方式需要使用外部时钟电路,并配置好单片机的时钟源,可以实现高精度的延时,但同时也增加了系统的复杂度和成本。
二、软件延时举例
下面是一个简单的单片机软件延时的演示代码,以STM32为例,使用SysTick中断来实现延时:
  1. #include "stm32f10x.h"
  2. void SysTick_Handler(void)
  3. {
  4.     static uint32_t ticks = 0;
  5.     ticks++;    // 计数器加1
  6. }
  7. void delay_ms(uint32_t ms)
  8. {
  9.     uint32_t ticks = 0;
  10.     ticks = ms * SystemCoreClock / 1000;    // 计算延时的时钟周期数
  11.     SysTick->CTRL = 0;    // 关闭SysTick定时器
  12.     SysTick->LOAD = ticks - 1;    // 设置定时器的重装载值
  13.     SysTick->VAL = 0;    // 清空定时器的计数器
  14.     SysTick->CTRL = 0x00000005;    // 开启SysTick定时器,使用CPU时钟源
  15.     while(!(SysTick->CTRL & 0x00010000));    // 等待延时结束
  16. }
  17. int main(void)
  18. {
  19.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);    // 使能GPIOC时钟
  20.     GPIO_InitTypeDef GPIO_InitStructure;
  21.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
  22.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  23.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  24.     GPIO_Init(GPIOC, &GPIO_InitStructure);    // 初始化PC13引脚为推挽输出模式
  25.    
  26.     while(1)
  27.     {
  28.         GPIO_SetBits(GPIOC, GPIO_Pin_13);    // PC13输出高电平
  29.         delay_ms(1000);    // 延时1秒
  30.         GPIO_ResetBits(GPIOC, GPIO_Pin_13);    // PC13输出低电平
  31.         delay_ms(1000);    // 延时1秒
  32.     }
  33. }

上述代码中,delay_ms()函数使用SysTick定时器实现了精确的延时,可以实现LED灯的闪烁效果。具体来说,delay_ms()函数首先计算出延时的时钟周期数,然后关闭SysTick定时器,设置定时器的重装载值,清空计数器,最后开启定时器,等待延时结束。使用SysTick定时器可以实现精确的延时,同时不会占用CPU的资源。
三、硬件延时的举例
下面是一个单片机系统硬件延时的举例,以STM32为例,使用定时器来实现延时:
  1. #include "stm32f10x.h"
  2. void delay_us(uint32_t us)
  3. {
  4.     TIM_Cmd(TIM3, DISABLE);    // 关闭TIM3定时器
  5.     TIM_SetCounter(TIM3, 0);    // 清空计数器
  6.     TIM_SetAutoreload(TIM3, us - 1);    // 设置重装载值
  7.     TIM_Cmd(TIM3, ENABLE);    // 开启TIM3定时器
  8.     while(TIM_GetFlagStatus(TIM3, TIM_FLAG_Update) == RESET);    // 等待计数器溢出
  9.     TIM_ClearFlag(TIM3, TIM_FLAG_Update);    // 清除计数器溢出标志
  10. }
  11. int main(void)
  12. {
  13.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);    // 使能TIM3时钟
  14.     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  15.     TIM_TimeBaseStructure.TIM_Period = 65535;    // 设置定时器周期为65535
  16.     TIM_TimeBaseStructure.TIM_Prescaler = 71;    // 设置预分频系数为72-1
  17.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;    // 设置时钟分割为1
  18.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;    // 设置计数器为向上计数模式
  19.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);    // 初始化TIM3定时器
  20.    
  21.     GPIO_InitTypeDef GPIO_InitStructure;
  22.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);    // 使能GPIOC时钟
  23.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
  24.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  25.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  26.     GPIO_Init(GPIOC, &GPIO_InitStructure);    // 初始化PC13引脚为推挽输出模式
  27.    
  28.     while(1)
  29.     {
  30.         GPIO_SetBits(GPIOC, GPIO_Pin_13);    // PC13输出高电平
  31.         delay_us(1000000);    // 延时1秒
  32.         GPIO_ResetBits(GPIOC, GPIO_Pin_13);    // PC13输出低电平
  33.         delay_us(1000000);    // 延时1秒
  34.     }
  35. }

上述代码中,delay_us()函数使用TIM3定时器实现了硬件延时,可以实现LED灯的闪烁效果。具体来说,delay_us()函数首先关闭TIM3定时器,然后清空计数器,设置重装载值,开启定时器,等待计数器溢出,最后清除计数器溢出标志。使用定时器可以实现高精度的延时,同时不会占用CPU的资源。
四、阻塞式延时和非阻塞式延时的区别
阻塞式延时和非阻塞式延时是两种不同的延时方式,主要区别在于是否会阻塞程序的执行。
阻塞式延时是指程序在执行延时函数时会一直等待,直到延时结束后才会继续执行下一条指令。在延时期间,程序无法执行其他任务。例如,以下代码是一个阻塞式延时的例子:
  1. void delay_ms(uint32_t ms)
  2. {
  3.     uint32_t i, j;
  4.     for(i = 0; i < ms; i++)
  5.     {
  6.         for(j = 0; j < 7200; j++);    // 延时1毫秒
  7.     }
  8. }
在上述代码中,程序在执行延时函数时会一直等待,直到延时结束后才会继续执行下一条指令。如果需要延时1秒,程序会占用1秒的时间,无法执行其他任务。
非阻塞式延时是指程序在执行延时函数时可以继续执行其他任务,不会一直等待。在延时期间,程序可以执行其他任务。例如,以下代码是一个非阻塞式延时的例子:
  1. void delay_ms(uint32_t ms)
  2. {
  3.     uint32_t ticks = ms * SystemCoreClock / 1000;
  4.     uint32_t start = SysTick->VAL;
  5.     while((SysTick->VAL - start) < ticks);
  6. }
在上述代码中,程序在执行延时函数时可以继续执行其他任务,不会一直等待。如果需要延时1秒,程序可以在延时期间执行其他任务,只有在延时结束后才会继续执行延时函数中的下一条指令。
总的来说,阻塞式延时会占用CPU的资源,无法执行其他任务,而非阻塞式延时可以让程序在执行延时函数时继续执行其他任务,提高了程序的效率。但是非阻塞式延时需要使用定时器或者其他硬件资源来实现,实现起来相对复杂一些。

本帖子中包含更多资源

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

×

打赏榜单

21ic小管家 打赏了 30.00 元 2023-04-17

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

本版积分规则

232

主题

585

帖子

7

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