(十五)零基础开发小安派-Eyes-S1【外设篇】——TIMER

[复制链接]
查看1662 | 回复16 | 2023-12-14 20:37:00 | 显示全部楼层 |阅读模式

零基础开发小安派-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。 微信截图_20231214201424.png

在 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);
        //这个延迟是为了防止在主函数中重复判断导致疯狂打印
    }
}

效果

微信截图_20231214203614.png

本帖被以下淘专辑推荐:

回复

使用道具 举报

AXK123456 | 2023-12-14 20:38:31 | 显示全部楼层
前排围观
回复

使用道具 举报

iiv | 2023-12-14 21:52:58 | 显示全部楼层
泽哥终于又出马了
回复 支持 反对

使用道具 举报

shawnting | 2023-12-14 23:09:12 | 显示全部楼层
围观一下
回复

使用道具 举报

bzhou830 | 2023-12-15 08:33:22 | 显示全部楼层
前排围观
选择去发光,而不是被照亮
回复

使用道具 举报

爱笑 | 2023-12-15 08:37:00 | 显示全部楼层
用心做好保姆工作
回复

使用道具 举报

WT_0213 | 2023-12-15 09:01:31 | 显示全部楼层
学习
回复

使用道具 举报

1084504793 | 2023-12-15 09:02:12 | 显示全部楼层
学到了
回复

使用道具 举报

lazy | 2023-12-15 09:11:43 | 显示全部楼层
学习了
回复

使用道具 举报

干簧管 | 2023-12-15 09:18:11 | 显示全部楼层
泽哥开始更新了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则