【嵌入式】延时函数及其原理

LuckiBit

延时函数(Delay Function)在编程中是一种常见的功能,广泛用于控制程序的执行时间,尤其是在嵌入式系统和实时系统中。延时函数的实现和使用可以根据不同的硬件和软件环境有所不同。本文将详细讲解延时函数的原理、实现方法、应用场景以及相关注意事项。

1. 什么是延时函数

1.1. 定义

延时函数是一种用于在程序中引入人为延迟的函数,其主要作用是让程序在指定的时间段内暂停执行。延时函数在许多应用场景中非常重要,如LED闪烁、按键去抖、通信协议中的时序控制等。

1.2. 分类

延时函数可以根据不同的实现方式分为以下几类:

  • 基于软件的延时函数:通过循环计数等方法在代码中引入延时。
  • 基于硬件定时器的延时函数:利用硬件定时器产生精确的延时。
  • 基于操作系统的延时函数:在多任务操作系统中,通过系统调用实现延时。

2. 延时函数的原理

2.1. 软件延时原理

软件延时通常通过空循环来实现。其基本原理是利用CPU执行空循环所需的时间来引入延迟。以下是一个简单的C语言软件延时函数示例:

void delay(volatile unsigned int count) {
    while (count--) {
        // 空循环,什么都不做
    }
}

这个函数的延时长度取决于循环次数和每次循环执行所需的时间。虽然实现简单,但精度较低,并且会占用CPU资源。

2.2. 硬件定时器延时原理

硬件定时器是现代微控制器中的标准外设,能够产生精确的时间间隔。利用硬件定时器可以实现高精度的延时。以下是一个基于STM32微控制器的硬件定时器延时示例:

void delay_ms(uint32_t ms) {
    // 配置定时器
    TIM_HandleTypeDef htim;
    __HAL_TIM_SET_COUNTER(&htim, 0);
    __HAL_TIM_SET_AUTORELOAD(&htim, ms * (SystemCoreClock / 1000));
    HAL_TIM_Base_Start(&htim);

    // 等待定时器溢出
    while (__HAL_TIM_GET_FLAG(&htim, TIM_FLAG_UPDATE) == RESET) {}
    HAL_TIM_Base_Stop(&htim);
}

这种方法能够提供精确的延时,但需要对定时器进行配置和管理。

2.3. 操作系统延时原理

在多任务操作系统中,如RTOS或Linux,可以通过系统调用来实现延时。操作系统提供的延时函数能够让任务进入睡眠状态,从而不占用CPU资源。以下是一个FreeRTOS中任务延时的示例:

void vTaskFunction(void *pvParameters) {
    for (;;) {
        // 执行任务
        // ...

        // 延时1秒
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

在这个示例中,vTaskDelay函数会将任务挂起指定的时间,其他任务可以在此期间运行。

3. 延时函数的实现方法

3.1. 基于循环计数的延时

这是最简单的延时方法,但不推荐在需要精确延时的场合使用。实现方式如前面提到的空循环。

3.2. 基于硬件定时器的延时

硬件定时器可以产生精确的时间间隔,适用于高精度延时。需要配置定时器的相关寄存器,并处理定时器中断。以下是一个基于AVR微控制器的延时函数示例:

void delay_ms(uint16_t ms) {
    TCCR1B |= (1 << WGM12); // 配置定时器为CTC模式
    OCR1A = (F_CPU / 1000) * ms; // 设置比较值
    TCCR1B |= (1 << CS10) | (1 << CS12); // 启动定时器,预分频器为1024

    // 等待定时器溢出
    while (!(TIFR1 & (1 << OCF1A)));
    TCCR1B = 0; // 停止定时器
    TIFR1 |= (1 << OCF1A); // 清除溢出标志
}

3.3. 基于操作系统的延时

在RTOS中,任务可以通过系统调用实现延时。常见的RTOS,如FreeRTOS、RTX、ThreadX等,都提供了任务延时函数。以下是一个基于FreeRTOS的延时示例:

void vTaskFunction(void *pvParameters) {
    for (;;) {
        // 执行任务
        // ...

        // 延时1秒
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

在Linux中,可以使用usleepnanosleep等系统调用实现延时:

#include <unistd.h>

int main() {
    // 延时1秒
    usleep(1000000);
    return 0;
}

4. 延时函数的应用场景

4.1. LED闪烁

在嵌入式系统中,延时函数常用于控制LED的闪烁频率。以下是一个简单的例子:

void blink_led(void) {
    while (1) {
        // 打开LED
        LED_ON();
        // 延时500毫秒
        delay_ms(500);

        // 关闭LED
        LED_OFF();
        // 延时500毫秒
        delay_ms(500);
    }
}

4.2. 按键去抖

机械按键在按下和释放时会产生抖动现象,需要延时函数进行去抖处理。以下是一个按键去抖的示例:

bool read_button(void) {
    if (BUTTON_PRESSED()) {
        // 延时去抖
        delay_ms(50);
        // 再次检查按键状态
        if (BUTTON_PRESSED()) {
            return true;
        }
    }
    return false;
}

4.3. 通信时序控制

在串行通信中,延时函数常用于控制时序。例如,在I2C通信中,延时函数可以用于生成时钟信号:

void i2c_start(void) {
    SDA_HIGH();
    SCL_HIGH();
    delay_us(5);
    SDA_LOW();
    delay_us(5);
    SCL_LOW();
}

5. 延时函数的注意事项

5.1. 精度和误差

软件延时精度较低,容易受到编译器优化和CPU频率变化的影响。硬件定时器延时精度高,但需要额外的硬件资源。操作系统延时函数精度取决于系统时钟和任务调度的粒度。

5.2. CPU占用

软件延时会占用CPU资源,导致系统性能下降。硬件定时器和操作系统延时函数能够让CPU在延时期间执行其他任务,提高系统效率。

5.3. 能耗

在低功耗应用中,使用软件延时会导致CPU持续运行,增加能耗。硬件定时器和操作系统延时函数能够降低能耗,使系统在延时期间进入低功耗模式。

6. 示例代码和应用

6.1. 基于STM32的延时函数

以下是一个基于STM32微控制器的延时函数示例,使用了硬件定时器:

void delay_ms(uint32_t ms) {
    // 配置定时器
    TIM_HandleTypeDef htim;
    __HAL_TIM_SET_COUNTER(&htim, 0);
    __HAL_TIM_SET_AUTORELOAD(&htim, ms * (SystemCoreClock / 1000));
    HAL_TIM_Base_Start(&htim);

    // 等待定时器溢出
    while (__HAL_TIM_GET_FLAG(&htim, TIM_FLAG_UPDATE) == RESET) {}
    HAL_TIM_Base_Stop(&htim);
}

6.2. 基于FreeRTOS的延时函数

以下是一个基于FreeRTOS的任务延时示例:

void vTaskFunction(void *pvParameters) {
    for (;;) {
        // 执行任务
        // ...

        // 延时1秒
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

6.3. 基于Linux的延时函数

以下是一个基于Linux的延时函数示例,使用了usleep系统调用:

#include <unistd.h>

int main() {
    // 延时1秒
    usleep(1000000);
    return 0;
}

7. 延时函数的高级应用

7.1. 精确延时的校准

在一些对时间精度要求非常高的应用中,可能需要对延时函数进行校准。可以通过测量实际的延时时间,并调整延时函数中的参数来实现。例如,在软件延时中,可以通过实验确定空循环的执行时间,从而调整循环次数。

void delay_calibrated(unsigned int count) {
    unsigned int calibrated_count = count * CALIBRATION_FACTOR;
    while (calibrated_count--) {
        // 空循环
    }
}

7.2. 非阻塞延时

非阻塞延时允许在延时期间执行其他任务,常用于提高系统响应能力。可以使用定时器中断或操作系统的定时器服务来实现非阻塞延时。以下是一个使用定时器中断实现的非阻塞延时示例:

volatile bool delay_elapsed = false;

void TIM_IRQHandler(void) {
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
        delay_elapsed = true;
    }
}

void delay_non_blocking(uint32_t ms) {
    TIM_SetCounter(TIM2, 0);
    TIM_SetAutoreload(TIM2, ms * (SystemCoreClock / 1000));
    TIM_Cmd(TIM2, ENABLE);
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    while (!delay_elapsed) {
        // 执行其他任务
    }

    TIM_Cmd(TIM2, DISABLE);
    TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);
    delay_elapsed = false;
}

7.3. 精确延时和任务调度

在实时操作系统中,精确的任务调度依赖于延时函数的精度。高精度延时函数能够确保任务在预定的时间点执行,满足实时系统的时序要求。以下是一个FreeRTOS中使用高精度延时函数的示例:

void vHighPrecisionTask(void *pvParameters) {
    TickType_t xLastWakeTime = xTaskGetTickCount();
    const TickType_t xFrequency = pdMS_TO_TICKS(100); // 100毫秒

    for (;;) {
        // 执行任务
        // ...

        // 高精度延时
        vTaskDelayUntil(&xLastWakeTime, xFrequency);
    }
}

8. 延时函数在嵌入式系统中的实际应用

8.1. 温度传感器数据采集

在嵌入式系统中,常常需要定时采集传感器数据。以下是一个使用延时函数定时采集温度传感器数据的示例:

void read_temperature(void) {
    while (1) {
        // 读取温度传感器数据
        float temperature = get_temperature();
        // 打印温度值
        printf("Temperature: %.2f\n", temperature);
        // 延时1秒
        delay_ms(1000);
    }
}

8.2. 串行通信协议的实现

在串行通信协议中,时序控制非常重要。延时函数常用于生成通信时序,例如在I2C协议的实现中:

void i2c_start(void) {
    SDA_HIGH();
    SCL_HIGH();
    delay_us(5);
    SDA_LOW();
    delay_us(5);
    SCL_LOW();
}

void i2c_stop(void) {
    SDA_LOW();
    SCL_HIGH();
    delay_us(5);
    SDA_HIGH();
    delay_us(5);
}

8.3. 电子时钟的实现

延时函数可以用于实现电子时钟,通过定时更新显示来实现计时功能。以下是一个简单的电子时钟示例:

void display_time(uint8_t hours, uint8_t minutes, uint8_t seconds) {
    printf("%02d:%02d:%02d\n", hours, minutes, seconds);
}

void run_clock(void) {
    uint8_t hours = 0, minutes = 0, seconds = 0;

    while (1) {
        // 更新显示
        display_time(hours, minutes, seconds);

        // 延时1秒
        delay_ms(1000);

        // 更新时间
        seconds++;
        if (seconds >= 60) {
            seconds = 0;
            minutes++;
            if (minutes >= 60) {
                minutes = 0;
                hours++;
                if (hours >= 24) {
                    hours = 0;
                }
            }
        }
    }
}

9. 延时函数在不同平台上的实现

9.1. AVR平台

在AVR平台上,延时函数可以通过空循环或硬件定时器实现。以下是一个基于AVR平台的延时函数示例:

void delay_ms(uint16_t ms) {
    TCCR1B |= (1 << WGM12); // 配置定时器为CTC模式
    OCR1A = (F_CPU / 1000) * ms; // 设置比较值
    TCCR1B |= (1 << CS10) | (1 << CS12); // 启动定时器,预分频器为1024

    // 等待定时器溢出
    while (!(TIFR1 & (1 << OCF1A)));
    TCCR1B = 0; // 停止定时器
    TIFR1 |= (1 << OCF1A); // 清除溢出标志
}

9.2. ARM Cortex-M平台

在ARM Cortex-M平台上,延时函数可以利用SysTick定时器或其他硬件定时器实现。以下是一个基于STM32的延时函数示例:

void delay_ms(uint32_t ms) {
    // 配置SysTick定时器
    SysTick->LOAD = (SystemCoreClock / 1000) * ms;
    SysTick->VAL = 0;
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;

    // 等待定时器溢出
    while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
    SysTick->CTRL = 0; // 停止定时器
}

9.3. Linux平台

在Linux平台上,延时函数可以使用usleepnanosleep或其他系统调用实现。以下是一个使用nanosleep的延时函数示例:

#include <time.h>

void delay_ms(long ms) {
    struct timespec ts;
    ts.tv_sec = ms / 1000;
    ts.tv_nsec = (ms % 1000) * 1000000;
    nanosleep(&ts, NULL);
}

10. 总结

延时函数在嵌入式系统和实时系统中具有重要作用。通过不同的方法可以实现各种精度和功能的延时函数,包括软件延时、硬件定时器延时和操作系统延时。不同平台和应用场景下,延时函数的实现和使用有所不同。理解和掌握延时函数的原理和实现方法,有助于开发高效和可靠的嵌入式系统应用。

11. 结束语

  1. 本节内容已经全部介绍完毕,希望通过这篇文章,大家对延时函数有了更深入的理解和认识。
  2. 感谢各位的阅读和支持,如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和评论,这对我们非常重要。再次感谢大家的关注和支持点我关注❤️

相关文章:

### 关于嵌入式蓝桥杯比赛中的延时函数嵌入式系统编程中,尤其是针对像蓝桥杯这样的竞赛环境,实现精确的延时功能是非常重要的。对于基于ARM Cortex-M系列微控制器的比赛平台来说,可以利用系统定时器(SysTick)来创建可靠的延时机制。 #### 使用SysTick实现延时函数 下面是一个典型的使用SysTick定时器编写的延时函数的例子: ```c #include "stm32f1xx_hal.h" // 初始化 SysTick 定时器用于产生固定频率的时间间隔 void delay_init(void){ HAL_SYSTICK_Config(SystemCoreClock / 1000); } // 实现 ms 级别的软件延迟 void delay_ms(uint32_t nms){ uint32_t ticks; uint32_t startTicks; ticks = (nms * (SystemCoreClock / 1000)) / 1000; // 计算需要等待的滴答次数 startTicks = HAL_GetTick(); while((HAL_GetTick() - startTicks) < ticks){} // 循环直到经过指定时间 } ``` 这段代码首先初始化了SysTick定时器以每毫秒触发一次中断的方式工作[^2]。接着定义了一个`delay_ms()`函数用来提供毫秒级精度的阻塞型延时操作,在此期间CPU会持续执行空循环直至达到设定的时间长度为止。 需要注意的是上述方法适用于STM32F1系列MCU及其对应的HAL库版本;不同型号的单片机可能具有不同的API接口名称或参数设置方式,请参照具体器件的手册文档调整相应部分。 此外,还可以考虑采用硬件定时器配合中断服务程序的方式来完成更复杂的定时任务处理逻辑,但这超出了当前讨论范围内的简单延时需求。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LuckiBit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值