0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

嵌入式里通用微秒计时函数框架设计与实现

Q4MP_gh_c472c21 来源:痞子衡嵌入式 作者:痞子衡嵌入式 2022-10-14 12:41 次阅读

今天给大家分享的是嵌入式里通用微秒(microseconds)计时函数框架设计与实现

嵌入式软件开发里,计时可以说是非常基础的功能模块了,其应用也非常广泛,比如可以辅助计算信号脉冲宽度时间,也可以直接用于常规延时等。相信很多人初次领略 MCU 的神奇,都是从计时功能相关小程序开始的。

在 MCU 里要想实现精确计时,往往都是利用其内部硬件定时器。不同厂商的 MCU,其定时器设计与使用都不太一样。即使是同一 MCU 内,通常也会有好几种不同类型的定时器共存。

基于此,今天分享一种非常简单实用的通用计时函数框架。这个框架的目的是统一计时函数接口,并且在实现上将通用部分和硬件相关部分剥离开。这样你的嵌入式项目在使用这个框架时,可以无缝快捷地切换底层定时器。

注:本框架主要适合定时器时钟源不小于 1MHz 的 MCU,因为函数接口里延时最小单元是 1us。对于一些定时器时钟源低于 1MHz 的 MCU,可将本框架简单改成毫秒(milliseconds)计时函数。

一、微秒(microseconds)计时函数库设计

1、函数接口定义

首先是设计通用计时函数框架头文件:microseconds.h ,这个头文件里直接定义如下 7 个接口函数原型。涵盖必备的初始化流程init()、shutdown(),最核心的计时功能get_ticks()、convert_to_microseconds(),常用的延时功能delay()、set_delay()、is_timeout()。

//!@brief初始化计时
voidmicroseconds_init(void);
//!@brief关闭计时
voidmicroseconds_shutdown(void);
//!@brief获取系统累计计数值
uint64_tmicroseconds_get_ticks(void);
//!@brief将计数值转换为时间值(微秒)
uint32_tmicroseconds_convert_to_microseconds(uint64_tticks);
//!@brief阻塞型延时(微秒级)
voidmicroseconds_delay(uint32_tus);
//!@brief设置超时时间(用于非阻塞型延时)
voidmicroseconds_set_delay(uint32_tus);
//!@brief判断是否超时(用于非阻塞型延时)
boolmicroseconds_is_timeout(void);

2、通用函数实现

然后是设计通用计时函数框架共用源文件:microseconds_common.c,这个文件里涉及三个静态全局变量定义,四个私有函数声明,以及除了 get_ticks() 之外的 6 个接口函数实现。

其中 s_tickPerMicrosecond 变量存的是每微秒对应计数值,其实这个变量不是一定要定义的,可以在函数需要时实时计算,但为了小小提升框架性能,就在 init() 里将这个值先算出来了,方便其他函数直接使用。

s_highCounter 变量存的是定时器中断次数,即高位计数器,因为框架 get_ticks() 接口返回的是 64bit 的计数值,对于有些宽度小于 32bit 的定时器,我们常常需要开启定时器中断,否则无法保证系统长时间运行线性计时的正确性(比如 100MHz 时钟源的 32bit 定时器,最长约 43 秒就会清零翻转一次,需要 s_highCounter 变量记录翻转次数)。

当然,如果 MCU 里能级连出 64bit 的定时器,就可以不用开启中断(清零翻转的时间特别长,可近似认为是永久),s_highCounter 此时就不需要了。

关于延时函数接口,delay() 用于阻塞型延时,即调用这个函数后一定是死等指定时间后才退出,系统会被强制挂起;set_delay()/is_timeout()用于非阻塞型延时,系统可以继续干其他任务,在需要的时侯来查看一下超时时间是否到了即可。两种延时各有各的用途。

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

//! @brief 打开硬件定时器
extern void microseconds_timer_init(void);
//! @brief 关闭硬件定时器
extern void microseconds_timer_deinit(void);
//! @brief 获取定时器时钟源数值
extern uint32_t microseconds_get_clock(void);
//! @brief 将时间值(微秒)转换为计数值
static uint64_t microseconds_convert_to_ticks(uint32_t microseconds);

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

void microseconds_shutdown(void)
{
    // 关闭硬件定时器
    microseconds_timer_deinit();
}

uint32_t microseconds_convert_to_microseconds(uint64_t ticks)
{
    return (ticks / s_tickPerMicrosecond);
}

uint64_t microseconds_convert_to_ticks(uint32_t microseconds)
{
    return ((uint64_t)microseconds * s_tickPerMicrosecond);
}

void microseconds_delay(uint32_t us)
{
    // 获取系统当前计数值
    uint64_t currentTicks = microseconds_get_ticks();
    // 计算超时时间点系统计数值
    uint64_t ticksNeeded = ((uint64_t)us * s_tickPerMicrosecond) + currentTicks;
    // 等待系统计数值到达超时时间点系统计数值
    while (microseconds_get_ticks() < ticksNeeded);
}

void microseconds_set_delay(uint32_t us)
{
    // 计算超时时间等效计数值
    uint64_t ticks = microseconds_convert_to_ticks(us);
    // 设置超时时间点系统计数值
    s_timeoutTicks = microseconds_get_ticks() + ticks;
}

bool microseconds_is_timeout(void)
{
    // 获取系统当前计数值
    uint64_t currentTicks = microseconds_get_ticks();
    // 判断系统计数值是否大于超时时间点系统计数值
    return (currentTicks < s_timeoutTicks) ? false : true;
}

二、微秒(microseconds)计时函数库实现

1、定时器相关实现(基于Cortex-M内核的SysTick)

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

SysTick 是 24bit 递减定时器,时钟源有两种配置:一是内核主频,二是外部时钟(看厂商实现),最常用的时钟源配置就是与内核同频。

之前我们说过,用 SysTick 这类宽度小于 32bit 的定时器,是需要开启定时器中断的,所以 s_highCounter 会生效。get_ticks()是整个计时函数框架里最基础也最核心的功能接口,这里面的实现有一个需要特别注意的地方,就是取系统当前计数值可能会有数值回退的风险,需要使用代码中 do {} while();方式来确保正确性。

//!< 高位计数器,仅当使能定时器超时中断时有效,用于记录中断累计次数
extern volatile uint32_t s_highCounter;

void microseconds_timer_init(void)
{
    // 调用 core_cmx.h 头文件里的初始化函数
    // SysTick时钟源为内核时钟,开启中断,重装值为 0xFFFFFF
    SysTick_Config(SysTick_LOAD_RELOAD_Msk + 1);
}

void microseconds_timer_deinit(void)
{
    SysTick->CTRL&=~(SysTick_CTRL_CLKSOURCE_Msk|
SysTick_CTRL_TICKINT_Msk|
SysTick_CTRL_ENABLE_Msk);
SysTick->VAL=0;
}

uint32_tmicroseconds_get_clock(void)
{
returnSystemCoreClock;
}

uint64_tmicroseconds_get_ticks(void)
{
uint32_thigh;
uint32_tlow;
//这里的实现要注意确保中断发生时获取系统累计计数值的正确性
do
{
//先缓存高位计数器
high=s_highCounter;
//再读定时器实际计数值
low=~SysTick->VAL&SysTick_LOAD_RELOAD_Msk;
}while(high!=s_highCounter);//保证缓存高位值与读实际低位值间隙中没有发生中断

return((uint64_t)high<< 24) + low;
}

void SysTick_Handler(void)
{
    s_highCounter++;
}

当然还有很多具体 MCU 平台的各种定时器实现,因此这个项目会不断更新,也欢迎大家来参与贡献。

至此,嵌入式里通用微秒(microseconds)计时函数框架设计与实现便介绍完毕了。

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • mcu
    mcu
    +关注

    关注

    146

    文章

    16977

    浏览量

    350215
  • 嵌入式
    +关注

    关注

    5068

    文章

    19008

    浏览量

    302965
  • 定时器
    +关注

    关注

    23

    文章

    3237

    浏览量

    114432
  • 函数
    +关注

    关注

    3

    文章

    4303

    浏览量

    62411

原文标题:MCU通用微秒计时函数框架设计

文章出处:【微信号:gh_c472c2199c88,微信公众号:嵌入式微处理器】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    嵌入式计时器以及simulink设计实现

    嵌入式计时器是一种在嵌入式系统中用于计时、计数和测量时间间隔的设备。
    发表于 07-21 17:20 4935次阅读
    <b class='flag-5'>嵌入式</b><b class='flag-5'>计时</b>器以及simulink设计<b class='flag-5'>实现</b>

    什么叫嵌入式 嵌入式应用

    防、智能农业工控类:机械手臂、医疗用具3、嵌入式开发框架:Linux开发:c语言——>编译文件(二进制)——>下载到操作系统——>运行4、c语言框架头文件——声明接口函数
    发表于 08-12 07:51

    嵌入式框架

    1.overview 图1-1 嵌入式框架嵌入式系统分为硬件以及软件两大部分,大多数人参与的是嵌入式软件设计,更多的是接触的是上层软件系统部分,可以分为两大类型
    发表于 10-27 08:26

    如何去设计一个linux嵌入式UI框架

    看了“自己动手设计并实现一个linux嵌入式UI框架”显然没有尽兴,因为还没有看到庐山真面目,那我今天继续,先来说说,我用到了哪些知识背景。如:C语言基础知识,尤其是指针、函数指针、内
    发表于 11-08 07:22

    嵌入式通用微秒计时函数框架的设计资料分享

      大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是嵌入式通用微秒(microseconds)计时
    发表于 12-08 07:35

    嵌入式低功耗电源管理框架的设计资料分享

    概述目标本篇的目标是介绍低功耗电源管理框架设计的概念理解低功耗管理的重要性理解如何开发适合的低功耗管理框架理解如何去管理产品的功耗形势大多数的嵌入式产品非常注重低功耗设计,尤其近年来,硬件不断
    发表于 12-21 07:39

    基于Chirp函数的Nios Ⅱ嵌入式实现

    分析Chirp函数在频域上的一般特性,提出利用FPGA的嵌入式软核NiosⅡ处理器在嵌入式操作系统μC/OS-Ⅱ上实现Chirp的方法
    发表于 06-15 11:02 1147次阅读
    基于Chirp<b class='flag-5'>函数</b>的Nios Ⅱ<b class='flag-5'>嵌入式</b><b class='flag-5'>实现</b>

    嵌入式应用框架EAF详解

    EAF是Embedded Application Framework 的缩写,即嵌入式应用框架嵌入式应用框架是 Application framework的一种, 是在
    发表于 12-02 11:30 2798次阅读

    关于嵌入式应用框架(EAF)的探索

    EAF是Embedded Application Framework 的缩写,即嵌入式应用框架
    发表于 12-10 14:28 848次阅读

    关于嵌入式应用框架(EAF)的分析

    EAF是Embedded Application Framework 的缩写,即嵌入式应用框架嵌入式应用框架是 Application framework的一种, 是在
    发表于 01-01 09:50 1486次阅读

    嵌入式堆栈的原理介绍和如何实现纯C实现

    栈这种结构在嵌入式其实是非常常用的,比如函数调用与返回就是典型的栈应用,虽然很多时候栈都是CPU系统在自动管理,我们只需要在链接文件分配栈大小以及栈存放位置,但稍微了解一下栈的原理
    的头像 发表于 04-06 10:10 4564次阅读
    <b class='flag-5'>嵌入式</b><b class='flag-5'>里</b>堆栈的原理介绍和如何<b class='flag-5'>实现</b>纯C<b class='flag-5'>实现</b>

    嵌入式系统框架----硬件篇

    1.系统框架图对于一个嵌入式系统,最重要的当然是运算以及存储单元,基本的嵌入式系统可以简化成如下系统框架图:
    发表于 10-20 11:51 3次下载
    <b class='flag-5'>嵌入式</b>系统<b class='flag-5'>框架</b>----硬件篇

    自己动手设计并实现一个linux嵌入式UI框架(设计)

    看了“自己动手设计并实现一个linux嵌入式UI框架”显然没有尽兴,因为还没有看到庐山真面目,那我今天继续,先来说说,我用到了哪些知识背景。如:C语言基础知识,尤其是指针、函数指针、内
    发表于 11-03 16:51 12次下载
    自己动手设计并<b class='flag-5'>实现</b>一个linux<b class='flag-5'>嵌入式</b>UI<b class='flag-5'>框架</b>(设计)

    痞子衡嵌入式嵌入式通用微秒(microseconds)计时函数框架设计与实现

      大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是嵌入式通用微秒(microseconds)计时
    发表于 11-25 09:21 16次下载
    痞子衡<b class='flag-5'>嵌入式</b>:<b class='flag-5'>嵌入式</b><b class='flag-5'>里</b><b class='flag-5'>通用</b><b class='flag-5'>微秒</b>(microseconds)<b class='flag-5'>计时</b><b class='flag-5'>函数</b><b class='flag-5'>框架设</b>计与<b class='flag-5'>实现</b>

    深度详解嵌入式系统专用轻量级框架设

    MR 框架是专为嵌入式系统设计的轻量级框架。充分考虑了嵌入式系统在资源和性能方面的需求。通过提供标准化的设备管理接口,极大简化了嵌入式应用开
    发表于 04-27 02:32 937次阅读
    深度详解<b class='flag-5'>嵌入式</b>系统专用轻量级<b class='flag-5'>框架设</b>计