打印
[APM32F0]

将两个16位定时器级联成32位

[复制链接]
1822|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Peixu|  楼主 | 2023-11-13 15:22 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Peixu 于 2023-11-20 18:12 编辑

#申请原创# @21小跑堂

在嵌入式系统开发中,时间管理是至关重要的。定时器在这方面扮演着关键角色,用于测量时间间隔、生成脉冲、控制任务执行周期等。
某些系列不具有32位计时器,自带的定时器均为16位。所以常常使用16位定时器来完成这些任务。然而,在某些情况下,当你需要进行某些同时需要精度与时长的计时内容时,16位计时器并不够用。
在这种情况下,将两个16位定时器级联成一个32位定时器就显得尤为重要。
目前使用的是一颗ARM Cortex-M0+内核的APM32F003是一款广泛应用于嵌入式系统中的低功耗、高性能的微控制器核心。
包含 2 个16bit高级控制定时器(TMR1 和 TMR1A)、1 个16bit通用定时器(TMR2)、1 个8bit基本型定时器(TMR4)、2 个看门狗定时器、1 个系统滴答定时器和 1 个自动唤醒定时器。
接下来将介绍如何使用APM32F003的两个16位定时器,将它们级联起来,实现一个功能强大的32位定时器。
1. 选择和初始化定时器
一般微控制器通常具有多个定时器,我们需要选择两个可用的16位定时器并对它们进行初始化。这中间会涉及到设置时钟源、计数模式、中断使能等。请参考微控制器手册以获取特定寄存器的详细信息。
2. 配置级联模式
在级联模式下,一个定时器被设置为主定时器(例如定时器2),另一个被设置为从定时器(例如定时器1)。主定时器的计数器溢出会触发一个中断,在该中断服务程序中,更新从定时器的计数器。
3. 中断服务程序设计
设计主定时器的中断服务程序,以处理主定时器计数器溢出。在这个中断服务程序中,更新从定时器的计数器,可以是递增、清零或其他需求的操作。
4. 计算32位定时器的值
在主程序中,可以在循环中读取定时器1和定时器2的值。通过位操作(如位移和逻辑或操作),将这两个16位计数器的值合并成一个32位的计时值。
temp = TMR1_ReadCounter(TMR1);
temp <<= 16;
temp |= TMR2_ReadCounter();
5. 测试和验证
对32位定时器进行全面的测试,包括检查溢出情况、正确性、稳定性等。确保它按照预期工作,并满足你的应用需求。

APM32F003这个主频可以跑48Mhz 以我们的48M主频不分频直接计数为例:
那么计数单位时间为:

16bit 的定时器是2的16次方 = 65535 ,那么最大计时时间为:

16bit = 1.36ms
32bit = 89.4s
对于某些毫秒级高精度计时来说,使用32位定时器还是很有必要的。
使用TMR1 和TMR2级联。
原理:
设置TMR2为主定时器,TMR1从为定时器。

在定时器互连中可以:
将一个定时器作为另一个寄存器的预分频器
用一个定时器的使能信号启动另一个寄存器
用一个定时器的更新事件启动另一个寄存器
用一个定时器的使能选通另一个定时器
用一个外部触发同步两个定时器
将TMR2级联到TMR1 的后面,当TMR2出现 “溢出 更新事件” 的时候,TMR1就自动计数一次 软读出。
void TMR2Init(void)
{
     /**  Divider = 0x00, counter = 0xFFFF  */
    TMR2_ConfigTimerBase(0x00, 0XFFFF);
    /**  Master mode Configuration  */
    TMR2_ConfigMasterMode(TMR2_MASTER_MODE_UPDATE);
     /**  Enable TMR2 Master mode  */
    TMR2_EnableMasterMode();
     /**  Enable TMR2  */
    TMR2_Enable();
}
void TMR1Init(void)
{
    TMR1_TimeBaseConfig_T timeBaseConfig;
    /** Up-counter */
    timeBaseConfig.cntMode = TMR1_CNT_MODE_UP;
    /** Set divider */
    timeBaseConfig.divider = 0;
    /** Repetition counter = 0x0 */
    timeBaseConfig.repetitionCount = 0X0;
    /** specifies the counter */
    timeBaseConfig.count = 0xFFFF;
    TMR1_ConfigTimerBase(TMR1, &timeBaseConfig);
    /** The update event is selected as trigger Input */
    TMR1_ConfigInputTrigger(TMR1,TMR1_ITC_TMR2);
    /**  Set TMR1 Slave mode  */
    TMR1_ConfigSlaveMode(TMR1,TMR1_SLAVE_MODE_EXTERNAL1);
    /**  Enable TMR1  */
    TMR1_Enable(TMR1);
}

需要注意TMR2初始化要在TMR1前面, 因为TMR2是主定时器。
int main(void)
{
    USARTInit();  
    TMR2Init();    // TMR2 is Master and needs to be before TMR1
    TMR1Init();
    uint32_t temp = 0;
    while(1)
    {
        temp = TMR1_ReadCounter(TMR1);
        temp <<= 16;
        temp |= TMR2_ReadCounter();
        printf("Count Value: 0x%8x\r\n",temp);
    }
}

通过以上步骤,已经成功地使用了两个16位定时器,将它们级联成一个32位定时器。




APM32F003_timer (cascade mode) 32bit.zip

1.31 MB

使用特权

评论回复
沙发
呐咯密密| | 2023-11-13 16:47 | 只看该作者
定时器的级联真的香,可以做太多事了

使用特权

评论回复
板凳
yangxiaor520| | 2023-11-14 08:16 | 只看该作者
简单来说就是两个计数器想加。

使用特权

评论回复
地板
mcu5i51| | 2023-11-14 10:24 | 只看该作者
溢出时用中断处理高位,不是省了一个计数器了么,而且可以更多位

使用特权

评论回复
5
Peixu|  楼主 | 2023-11-20 18:14 | 只看该作者
#申请原创# @21小跑堂

使用特权

评论回复
6
Peixu|  楼主 | 2023-11-20 18:15 | 只看该作者
@21小跑堂  

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

27

主题

53

帖子

0

粉丝