RT-Thread HWTIMER设备(学习)

定时器简介

硬件定时器一般有2种工作模式,定时器模式和计数器模式。不管是工作在哪一种模式,实质都是通过内部计数器模块对脉冲信号进行计数,下面是定时器的一些重要概念。

  • 计数器模式:对外部输入引脚的外部脉冲信号计数。
  • 定时器模式:对内部脉冲信号计数。定时器常用作定时时钟,以实现定时检测,定时响应,定时控制。

计数器:计数器可以递增计数或者递减计数,16位计数器的最大计数值为65535.

计数频率:定时器模式时,计数器单位时间内的计数次数,由于系统时钟频率是定值,所以根据计数器的计数值计算出定时时间,定时时间=计数值/计数频率。
例如计数频率为1MHz,计数器计数一次的时间为1/1000000,也就是没经过1微妙计数器加一,此时16位计数器的最大定时能力为65535微妙,即65.535毫秒。

本定时器设备框架内部会自动处理硬件定时器超时的问题,例如16位定时器在1MHz的频率下最大只能维持65.535ms。
但是本定时器框架下,用户可以将定时器的溢出时间设置为例如500ms,框架内部会自动处理硬件溢出问题。当时间达到500ms后,框架会调用用户预先设置好的回调函数。

访问硬件定时器设备

在这里插入图片描述

查找定时器设备

应用程序根据硬件定时器设备名称获取设备句柄,进而可以操作硬件定时器设备。

rt_device_t rt_device_find(const char* name);

一般情况下,注册到系统的硬件定时器设备名称为timer0,timer1等。

#define HWTIMER_DEV_NAME "timer0";
rt_device_t hw_dev;
hw_dev = rt_device_find(HWTIMER_DEV_NAME);

打开定时器设备

通过设备句柄,应用程序可以打开设备。
打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。

rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
  • dev:硬件定时器设备句柄。
  • oflags:设备打开模式,一般以读写方式打开,即RT_DEVICE_OFLAG_RDWR

设置超时回调函数

在C语言和C++语言中,将一个函数声明为’static’具有以下含义:

  1. 作用域限制:函数声明为’static’会将其作用域限制在当前文件中,这意味着该函数只能在包含它的源文件中调用,而不能在其他文件中调用。这可以用于隐藏函数的实现细节,避免与其他文件中的同名函数发生冲突。
  2. 链接性:'static’函数具有内部链接性,这意味着它不会被放在全局符号表中,无法被其他文件访问。这有助于减小程序的全局命名空间污染。
rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev, rt_size_t size))
  • dev:设备句柄。
  • rx_ind:超时回调函数,由调用者提供。
#define HWTIMER_DEV_NAME   "timer0"     /* 定时器名称 */
rt_device_t hw_dev;                     /* 定时器设备句柄 */

/* 定时器超时回调函数 */
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
    rt_kprintf("this is hwtimer timeout callback fucntion!\n");
    rt_kprintf("tick is :%d !\n", rt_tick_get());

    return 0;
}

static int hwtimer_sample(int argc, char *argv[])
{
    /* 查找定时器设备 */
    hw_dev = rt_device_find(HWTIMER_DEV_NAME);
    /* 以读写方式打开设备 */
    rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);

    ...

    /* 设置超时回调函数 */
    rt_device_set_rx_indicate(hw_dev, timeout_cb);

    return 0;
}

控制定时器设备

通过命令控制字,应用程序可以对硬件定时器设备进行配置。

rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void *arg);
  • dev:设备句柄
  • cmd:命令控制字
  • arg:控制的参数
  • 返回:RT_EOK:函数执行成功。-RT_ENSYS:执行失败,dev为空。

硬件定时器设备支持的命令控制字如下:

  • HWTIMER_CTRL_FRQ_SET:设置计数频率(若未设置该项,默认为1MHz或支持的最小计数频率)
  • HWTIMER_CTRL_STOP:停止计时器
  • HWTIMER_CTRL_INFO_GET:获取定时器特征信息
  • HWTIMER_CTRL_MODE_SET:设置定时器模式(若未设置,默认是HWTIMER_MODE_ONESHOT)

获取定时器特征信息参数arg为指向结构体struct rt_hwtimer_info的指针,作为一个输出参数保存获取的信息。

设置定时器模式时,参数arg可取:

  • HWTIMER_MODE_ONESHOT:单次定时
  • HWTIMER_MODE_PERIOD:周期性定时
#define HWTIMER_DEV_NAME   "timer0"     /* 定时器名称 */
rt_device_t hw_dev;                     /* 定时器设备句柄 */
rt_hwtimer_mode_t mode;                 /* 定时器模式 */
rt_uint32_t freq = 10000;               /* 计数频率 */

static int hwtimer_sample(int argc, char *argv[])
{
	hw_dev = rt_device_find(HWTIMER_DEV_NAME);
	rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);

	rt_device_control(hw_dev, HWTIMER_CTRL_FREQ_SET, &freq);
	mode = HWTIMER_MODE_PERIOD;
	rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
	return 0;
}

设置定时器超时值

通过如下函数可以设置定时器的超时值,在调用该函数后,定时器更新参数并开启。

rt_size_t rt_device_write(rt_device_t dev, rt_odd_t pos, const void* buffer, rt_size_t size);
  • dev:设备句柄。
  • pos:写入数据偏移量,未使用,可取0值。
  • buffer:指向定时器超时时间结构体的指针。
  • size:超时时间结构体的大小。
  • 返回:写入数据的实际大小,0:失败。

超时时间结构体原型:

typedef struct rt_hwtimerval
{
	rt_int32_t sec;
	rt_int32_t usec;
}

设置定时器超时值的使用示例如下:

#define HWTIMER_DEV_NAME "timer0"
rt_device_t hw_dev; //定时器设备句柄
rt_hwtimerval_t timeout_s; //定时器超时值

static int hwtimer_sample(int argc, char *argv[])
{
	//查找定时器设备
	hw_dev = rt_device_find(HWTIMER_DEV_NAME);
	rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);

	//设置定时器超时值为5s并启动定时器
	timeout_s.sec = 5;
	timeout_s.usec = 0;
	rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s));
	return 0;
	
}

获取定时器当前值

通过如下函数可以获取自定时器开始(rt_device_write)之后的运行时:

rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
  • dev:定时器设备句柄
  • pos:写入数据偏移量,未使用,可取0值
  • buffer:输出参数,指向定时器超时时间结构体的指针
  • size:超时时间结构体的大小
  • return:成功-超时时间结构体的大小,0:失败
rt_hwtimerval_t t;
rt_device_read(hw_dev, 0, &t, sizeof(t));
rt_kprintf("Read: Sec = %d, Usec = %d\n", t.sec, t.usec);

关闭定时器设备

通过如下函数关闭定时器设备:

rt_err_t rt_device_close(rt_device_t dev);
  • dev:定时器设备句柄
  • 返回:RT_EOK-关闭设备成功,-RT_ERROR-设备已经完全关闭,不能重复关闭设备,其它错误码-关闭设备失败。

关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。

注:可能出现定时误差。假设计数器最大值0xFFFF,计数频率1MHz,定时时间1s又1us。
由于定时一次最多只能计时到65535us,对于1000001us的定时要求,可以50000us定时20次完成,此时会出现计算误差1us。

硬件定时器设备完整使用示例

硬件定时器设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:

  1. 首先根据定时器设备名称“timer0”查找设备获取设备句柄。
  2. 以读写方式打开设备“timer0”。
  3. 设置定时器超时回调函数。
  4. 设置定时器模式为周期性定时器,并设置超时时间为5s,此时定时器启动。
  5. 延时3500ms后读取定时器时间,读取到的值会以秒和微妙的形式显示。
//例程导出了hwtimer_sample命令到控制终端
//程序功能:硬件定时器超时回调函数周期性的打印当前tick值,2次tick值之差换算为时间等同于定时时间

#include <rtthread.h>
#include <rtdevice.h>

#define HWTIMER_DEV_NAME "timer0" //定时器名称

//定时器超时回调函数
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
	rt_kprintf("this is hwtimer timeout callback function\n");
	rt_kprintf("tick is : %d \n",rt_tick_get());

	return 0;
}

static int hwtimer_sample(int argc, char *argv[])
{
	rt_err_t ret = RT_EOK;
	rt_hwtimerval_t timeout_s; //定时器超时值
	rt_device_t hw_dev = RT_NULL;
	rt_hwtimer_mode_t mode; //定时器模式
	rt_uint32_t freq = 10000; //计数频率

	hw_dev = rt_device_find(HWTIMER_DEV_NAME);
	if(hw_dev == RT_NULL)
	{
		 rt_kprintf("hwtimer sample run failed! can't find %s device!\n", HWTIMER_DEV_NAME);
		 return RT_ERROR;
	}
	ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
	if(ret != RT_EOK)
	{
		rt_kprintf("open %s device failed\n",HWTIMER_DEV_NAME);
		return ret;
	}
	rt_device_set_rx_indicate(hw_dev, timeout_cb);

	rt_device_control(hw_dev, HWTIMER_CTRL_FREQ_SET, &freq);
	mode = HWTIMER_MODE_PERIOD;
	ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
	
	if (ret != RT_EOK)
    {
        rt_kprintf("set mode failed! ret is :%d\n", ret);
        return ret;
    }
	
	timeout_s.sec = 5;
	timeout_s.usec = 0;
	if(rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s))
	{
		rt_kprintf("set timeout value failed\n");
        return RT_ERROR;
	}
	rt_thread_mdelay(3500);
	
	rt_device_read(hw_dev, 0, &timeout_s, sizeof(timeout_s));
	rt_kprintf("Read: Sec = %d, Usec = %d\n", timeout_s.sec, timeout_s.usec);

    return ret;
}
MSH_CMD_EXPORT(hwtimer_sample, hwtimer sample);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

饼干饼干圆又圆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值