发帖
1 0 0

小安派-Eyes-S1 - TIMER

st780206
论坛元老

12

主题

25

回帖

7786

积分

论坛元老

积分
7786
本帖最后由 st780206 于 2026-1-12 11:02 编辑

参考 零基础开发小安派-Eyes-S1【外设篇】——TIMER
TIMER也就是定时器,定时器故名思意就是用来定时的东西,可以根据时钟源来分配计时的时间周期,实现准确的计时,一般软件的定时会出现误差,一些特殊情况需要精准的定时,那就需要使用到硬件定时器,如定时5分钟执行某些特殊任务。定时器可以搭配中断来使用,利用好时间间隔而满足我们的需求。
一、了解小安派-Eyes-S1的TIMER
芯片内置了两个32-Bit定时器,这两个定时器在LHAL库里对应timer0和timer1。
这两组TIMER有以下特征:
• 多种时钟来源,最高可支持 80M 时钟
• 8-bit 时钟分频器,分频系数为 1-256
• 两个 32-bit 定时器:channel 0 和 channel 1
• 定时器包含三组报警值设定,可设定报警值溢出时报警
• 支持 Free Run 模式和 Pre_load 模式
• 一个 16-bit 看门狗定时器
• 支持写入密码保护,防止误设定造成系统异常
• 支持中断或复位两种看门狗溢出方式
• 支持测量外部 GPIO 的脉冲宽度
定时器的时钟来源有以下五种选择:
• BCLK--总线时钟
• 32K--32K 时钟
• 1K--1K 时钟(32K 的分频)
• XTAL--外部晶振
• GPIO--外部 GPIO
#define TIMER_CLKSRC_BCLK 0
#define TIMER_CLKSRC_32K  1
#define TIMER_CLKSRC_1K   2
#define TIMER_CLKSRC_XTAL 3
#define TIMER_CLKSRC_GPIO 4
#define TIMER_CLKSRC_NO   5​
计数模式有以下两种:
定时器计数模式分为两种: freerun(向上计数模式)、preload(重装载模式)。
#define TIMER_COUNTER_MODE_PROLOAD 0
#define TIMER_COUNTER_MODE_UP      1​
定时器一共三个 compare id, 用于设置不同的定时时间,可以当三个定时器使用。
#define TIMER_COMP_ID_0 0
#define TIMER_COMP_ID_1 1
#define TIMER_COMP_ID_2 2​
二、结构体与函数接口struct bflb_timer_config_s
说明:Timer初始化配置结构体。
struct bflb_timer_config_s {
uint8_t counter_mode;
uint8_t clock_source;
uint8_t clock_div;
uint8_t trigger_comp_id;
uint32_t comp0_val;
uint32_t comp1_val;
uint32_t comp2_val;
uint32_t preload_val;
};​


parameter
description
counter_mode
计数模式
clock_source
时钟源选择
clock_div
分频值
trigger_comp_id
选择触发的最高 comp id(选择越高,则能够定时的个数越多)
comp0_val
comp0 比较值
comp1_val
comp1 比较值(需要大于 comp0_val)
comp2_val
comp2 比较值(需要大于 comp1_val)
preload_val
重装载值
bflb_timer_init
说明: 初始化 timer。使用之前需要开启 timer ip 时钟。
void bflb_timer_init(struct bflb_device_s *dev, const struct bflb_timer_config_s *config);​
parameter
description
dev
设备句柄
config
配置项
bflb_timer_deinit
说明: 反初始化 timer。
void bflb_timer_deinit(struct bflb_device_s *dev);
parameter
description
dev
设备句柄
bflb_timer_start
说明: 启动 timer 。
void bflb_timer_start(struct bflb_device_s *dev);​
parameter
description
dev
设备句柄
bflb_timer_stop
说明: 停止 timer。
void bflb_timer_stop(struct bflb_device_s *dev);
parameter
description
dev
设备句柄
bflb_timer_set_compvalue
说明: 设置 timer comp id 比较值。
void bflb_timer_set_compvalue(struct bflb_device_s *dev, uint8_t cmp_no, uint32_t val);
parameter
description
dev
设备句柄
cmp_no
comp id
val
比较值
bflb_timer_get_compvalue
说明: 获取 comp id 比较值。
uint32_t bflb_timer_get_compvalue(struct bflb_device_s *dev, uint8_t cmp_no);​
parameter
description
dev
设备句柄
cmp_no
comp id
val
比较值
bflb_timer_get_countervalue
说明: 获取 timer 计数值。
uint32_t bflb_timer_get_countervalue(struct bflb_device_s *dev);​
parameter
description
dev
设备句柄
return
计数值
bflb_timer_compint_mask
说明: timer comp 中断屏蔽开关。
void bflb_timer_compint_mask(struct bflb_device_s *dev, uint8_t cmp_no, bool mask);​
parameter
description
dev
设备句柄
cmp_no
comp id
mask
是否屏蔽中断
bflb_timer_get_compint_status
说明: 获取 timer comp id 中断匹配标志。
bool bflb_timer_get_compint_status(struct bflb_device_s *dev, uint8_t cmp_no);​
parameter
description
dev
设备句柄
cmp_no
comp id
return
为 true 表示匹配
bflb_timer_compint_clear
说明: 清除 timer comp id 中断标志。
void bflb_timer_compint_clear(struct bflb_device_s *dev, uint8_t cmp_no);
parameter
description
dev
设备句柄
cmp_no
comp id
三、定时器的两种计数方式以及中断触发一、定时器时钟源的选择以及分频
以选择TIMER_CLKSRC_XTAL这个外部晶振的时钟源来举例,频率为40MHz,而分频系数,也就是结构体中的clock_div,这里系数可选0~255,选择39,时钟计数=时钟频率/(分频系数+1)。也就是40Mhz/(39+1),也就是1Mhz,而周期与频率互为倒数,也就是1us一个计数。这样分频的话就是一微秒计数+1。
二、计数模式
TIMER有两种计数模式,分别是freerun(向上计数模式)、preload(重装载模式)
FreeRun模式下,计数器的初始值为0,定时器开始后,累加计数,当达到计数最大值后,然后从 0 再次开始计数。而最大值的数量估计是comp0的数据类型最大值,也就是32位数据。
相比之下,PreLoad模式就好用多了,计数器的初始值是 PreLoad 寄存器的值,然后向上累加计数,当满足 PreLoad 条件时,计数器的值被置为 PreLoad 寄存器的值,然后计数器再次开始向上累加计数。
三、中断
结构体有trigger_comp_id选择几个比较ID,如果选择三个ID的情况下,在定时器的计数器计数过程中,一旦计数器的值与三个比较器中的某比较值一致,该比较器的比较标志就会置位,并可以产生相应的比较中断。在所有的ID中断调节都达到后,会回到PreLoad的值,也就是preload_val重新开始计时。有如下一个示例的时序图,若预加载寄存器的值为 10,比较器 0 的值为 13,比较器 1 的值为 16,比较器 2 的值为 19。

                               
登录/注册后可看大图
在 FreeRun 模式下,定时器工作时序与 PreLoad 基本相同,只是计数器会从 0 开始累计到最大值,期间产生的比较标志和比较中断的机制与 FreeRun 模式相同。
四、简单示例——定时器分频一秒进入一次中断,在中断修改全局变量在主函数中打印Main
#include "bflb_mtimer.h"
#include "bflb_timer.h"
#include "board.h"
struct bflb_device_s *timer0;
volatile static uint16_t MyTime_s = 0;
//定义一个全局变量,在中断中修改,这里注意要用volatile关键字防止变量被优化
void timer0_isr(int irq, void *arg){
    bool status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_0);
   if (status) {
        bflb_timer_compint_clear(timer0, TIMER_COMP_ID_0);
        if (MyTime_s==60)
        {
            MyTime_s = 0;
        }
        MyTime_s++;
         printf("time is %d\r\n",MyTime_s);
     }
}
//中断服务函数,每进入一次变量自增1,到达60也就是1分钟置为0
int main(void){
    board_init();
    printf("Timer basic test\n");
    /* timer clk = XCLK/(div + 1 )*/
    struct bflb_timer_config_s cfg0;
    cfg0.counter_mode = TIMER_COUNTER_MODE_PROLOAD;
/* 选择重装载模式 */
    cfg0.clock_source = TIMER_CLKSRC_XTAL;
//选择外部时钟晶振,40MHz   
cfg0.clock_div = 39;
/* for bl616/bl808/bl606p is 39, for bl702 is 31 */
    cfg0.trigger_comp_id = TIMER_COMP_ID_0;
//选择比较ID的个数,这里选择一个ID,也就是只会到达下面的ID1   
cfg0.comp0_val = 1000000;
/* 比较值ID1,当计数达到1000000时,根据前面的分频一微秒一个计数,也就是总共1秒  */
    cfg0.comp1_val = 2500000;
/* 比较值ID2,需要大于ID1,由于前面只设置了一个ID,所以这里不会触发 */
    cfg0.comp2_val = 3500000;
/* 比较值ID2,需要大于ID2,由于前面只设置了一个ID,所以这里不会触发 */
    cfg0.preload_val = 0;   
/* 重装载值,开始的值,以及比较完所有ID个数后重启的值 */
    timer0 = bflb_device_get_by_name("timer0");
    /* Timer init with default configuration */
    bflb_timer_init(timer0, &cfg0);
    bflb_irq_attach(timer0->irq_num, timer0_isr, NULL);
    bflb_irq_enable(timer0->irq_num);
    /* Enable timer */
    bflb_timer_start(timer0);
//开启定时器
    printf("case success.\r\n");
    while (1) {
          switch (MyTime_s)
        {
        case 10:
            printf("10 seconds have passed\r\n");
            break;
        case 20:
            printf("20 seconds have passed\r\n");
            break;
        case 30:
            printf("30 seconds have passed\r\n");
            break;
        case 40:
            printf("40 seconds have passed\r\n");
            break;
        case 50:
            printf("50 seconds have passed\r\n");
            break;
        case 60:
            printf("One minute has already passed\r\n");
            break;
        default:
            break;
        }
        //对全局变量进行判断,通过switch语句分别打印
        bflb_mtimer_delay_ms(900);
        //这个延迟是为了防止在主函数中重复判断导致疯狂打印
    }
}​
效果

                               
登录/注册后可看大图



──── 0人觉得很赞 ────

使用道具 举报

好详细~
您需要登录后才可以回帖 立即登录
高级模式
返回
统计信息
  • 会员数: 30497 个
  • 话题数: 44667 篇