RT-Thread HWTIMER设备(学习)

news2024/9/20 1:08:43

定时器简介

硬件定时器一般有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);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1081013.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c语言:通讯录管理系统(文件版本)

前言&#xff1a;在大多数高校内&#xff0c;都是通过设计一个通讯录管理系统来作为c语言课程设计&#xff0c;通过一个具体的系统设计将我们学习过的结构体和函数等知识糅合起来&#xff0c;可以很好的锻炼学生的编程思维&#xff0c;本文旨在为通讯录管理系统的设计提供思路和…

智能电表线路单回路双回路的区别

随着科技的发展和能源管理的需求&#xff0c;智能电表已经成为电力系统中不可或缺的一部分。智能电表可以通过数据通信网络将用电信息实时传输到电力公司&#xff0c;为电力公司提供更精确、实时的用电数据&#xff0c;同时也可以为用户提供更加智能化的用电服务。 在智能电表…

局域网上IP多播与IP单播关于MAC地址的区别

IP单播进行到局域网上的时候&#xff1a; 网际层使用IP地址进行寻址&#xff0c;各路由器收到IP数据报后&#xff0c;根据其首部中的目的IP地址的网络号部分&#xff0c;基于路由表进行查表转发。 查表转发的结果可指明IP数据报的下一跳路由器的IP地址&#xff0c;但无法指明…

pid-limit参数实验

fork炸弹命令 :(){ :|:& };: 可以看到&#xff0c;如果docker没有限制&#xff0c;会遭到fork炸弹恶意 参考 https://www.cyberciti.biz/faq/understanding-bash-fork-bomb/

146616-66-2,胺反应性染料BDP FL NHS Ester,聚乙二醇单烯丙基醚

产品概览&#xff1a;Amine reactive bright&#xff0c;一种光稳定性佳的绿色荧光染料&#xff0c;以发挥独特的分子发光性能。此乃改进型的FAM&#xff08;荧光素&#xff09;荧光染料接班人——BDP FL NHS ester&#xff0c;在488nm通道中绽放色彩。这种胺反应性染料&#x…

信息系统项目管理师第四版学习笔记——项目绩效域

干系人绩效域 干系人绩效域涉及与干系人相关的活动和职能。在项目整个生命周期过程中&#xff0c;有效执行本绩效域可以实现的预期目标主要包含&#xff1a;①与干系人建立高效的工作关系&#xff1b;②干系人认同项目目标&#xff1b;③支持项目的干系人提高了满意度&#xf…

在原有的vue(react)项目中引入electron打包成桌面应用(全网最新!)

基于原有的项目中使用electron打包成桌面应用 首先了解electron是什么? 软件简介 Electron 是一个使用 JavaScript、HTML 和 CSS 构建跨平台的桌面应用程序。它基于 Node.js 和 Chromium,被 Atom 编辑器和许多其他应用程序使用。 Electron 兼容 Mac、Windows 和 Linux,可以…

SpringMVC源码分析(二)启动过程之RequestMappingHandlerMapping分析

a、http请求中的url是如何与对应Handler的即Controller对应method映射的&#xff1f; 在上篇中提到在SpringMVC中&#xff0c;初始化9大内置组件的时候其中有一个组件就是HandlerMapping&#xff0c;在初始化HandlerMapping的时候会加载代码中所有标注了Controller和RequestMap…

视频目标分割数据集分享

MOSE: A New Dataset for Video Object Segmentation in Complex Scenes MOSE 是一个新的视频目标分割数据集&#xff0c;旨在解决复杂环境下的目标跟踪和分割。MOSE 包含 2,149 个视频片段和来自 36 个类别的 5,200 个物体&#xff0c;以及 431,725 个高质量物体分割掩码。MOS…

ubuntu安装依赖包时显示需要先安装其所需要的各种安装包)apt-get源有问题

最近在崭新的ubuntu上安装g以及一些其他的依赖与工具时遇到以下报错情况&#xff1a; 依赖环环相扣&#xff0c;手动无法解决。 总结&#xff1a; 出现需要很多依赖项的情况是因为软件源中没有可用的依赖项或者依赖项版本不正确。 其实在Ubuntu系统中&#xff0c;使用sudo…

Java程序设计实验4 | 面向对象(下)

*本文是博主对Java各种实验的再整理与详解&#xff0c;除了代码部分和解析部分&#xff0c;一些题目还增加了拓展部分&#xff08;⭐&#xff09;。拓展部分不是实验报告中原有的内容&#xff0c;而是博主本人自己的补充&#xff0c;以方便大家额外学习、参考。 &#xff08;没…

AI如何帮助Salesforce从业者找工作?

在当今竞争激烈的就业市场中&#xff0c;找到满意的工作是一项艰巨的任务。成千上万的候选人竞争一个岗位&#xff0c;你需要利用一切优势从求职大军中脱颖而出。 这就是AI的用武之地&#xff0c;特别是像ChatGPT这样的人工智能工具&#xff0c;可以成为你的秘密武器。本篇文章…

2.1、如何在FlinkSQL中读取写出到Kafka

目录 1、环境设置 方式1&#xff1a;在Maven工程中添加pom依赖 方式2&#xff1a;在 sql-client.sh 中添加 jar包依赖 2、读取Kafka 2.1 创建 kafka表 2.2 读取 kafka消息体&#xff08;Value&#xff09; 使用 format json 解析json格式的消息 使用 format csv 解析…

力扣第98题 验证二叉搜索树 c++ 与上一篇文章相似

题目 98. 验证二叉搜索树 中等 相关标签 树 深度优先搜索 二叉搜索树 二叉树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当…

淘宝天猫商品历史价格API接口

获取淘宝商品历史价格接口的步骤如下&#xff1a; 注册淘宝开放平台&#xff1a;首先在淘宝开放平台上注册一个账号&#xff0c;并进行登录。创建应用&#xff1a;在淘宝开放平台上创建一个应用&#xff0c;并获取该应用的App Key和App Secret&#xff0c;用于后续的接口调用。…

Python“梦寻”淘宝天猫店铺所有数据接口,淘宝店铺所有商品数据API接口,淘宝API接口申请指南(含代码示例)

获取淘宝店铺所有商品数据的接口可以通过淘宝开放平台获取。 具体操作步骤如下&#xff1a; 在淘宝开放平台注册成为开发者&#xff0c;并创建一个应用&#xff0c;获取到所需的 App Key 和 App Secret 等信息。使用获取到的 App Key 和 App Secret&#xff0c;进行签名和认证…

Android组件通信——Intent(二十三)

1. 认识Intent 1.1 知识点 &#xff08;1&#xff09;了解Intent的主要作用&#xff1b; &#xff08;2&#xff09;掌握Activity程序对Intent操作的支持&#xff1b; &#xff08;3&#xff09;可以使用Intent完成Activity程序间的跳转&#xff0c;也可以通过Intent接收返…

QT基础 QChart绘制折线

目录 1.简单折线 2.数学折线 3.可滑动折线 1.简单折线 //![1] //! 折现段坐标QLineSeries *series new QLineSeries(); //![1]//![2] //! 添加点series->append(0, 6);series->append(2, 4);series->append(3, 8);series->append(7, 4);series->append(10, 5)…

YOLOv7改进: CFP:即插即用的多尺度融合模块,EVC助力小目标检测| 顶刊TIP 2023

💡💡💡本文独家改进:即插即用的多尺度融合模块,EVC助力小目标检测 EVC | 亲测在多个数据集实现暴力涨点,强烈推荐,独家首发; 收录: YOLOv7高阶自研专栏介绍:http://t.csdnimg.cn/tYI0c ✨✨✨前沿最新计算机顶会复现 🚀🚀🚀YOLOv7自研创新结合,轻松搞…

MS31804四通道低边驱动器可pin对pin兼容DRV8804

MS31804TE 是一个具有过流保护功能的四通道低边驱动器。MS31804TE 内置钳位二极管&#xff0c;用来钳制由电感负载续流产生的电压。MS31804TE 可以驱动单极步进电机、直流电机、继电器、螺线管或者其它负载。 散热良好的情况下&#xff0c;MS31804TE 可以提供每个通道最高 2A 的…