0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【产品应用】AWorksLP 样例详解(MR6450)—— HWTimer

AGk5_ZLG_zhiyua 来源:未知 2023-02-01 21:55 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

AWorksLP 对外设进行了高度抽象化,为同一类外设提供了相同的接口,应用程序可以轻松跨平台。本文以MR6450(点击了解详情)平台为例,介绍AWorksLP HWTimer 外设基本用法。


简介
在AWorksLP中将硬件定时器分为了4类,即延时型、计数型、周期型和输入捕获型硬件定时器。
  • 延时型硬件定时器:

    由硬件定时器外设提供的延时功能。

  • 计数型硬件定时器:

    提供较精确的类似时间戳的功能。

  • 周期型硬件定时器:

    可设置中断频率的计数器,不仅能提供计数器的功能,也能根据中断频率提供更精确的定时。

  • 输入捕获定时器:
    可测量脉冲宽度或者测量频率。

接口介绍
延时型硬件定时器:
函数原型
简要描述
aw_err_t aw_hwtimer_delay (int fd, struct aw_timespec *p_tv);
延时
aw_err_t aw_hwtimer_delay_cancel (int fd);
取消延时
数型硬件定时器:
函数原型
简要描述
aw_err_t aw_hwtimer_count_start (int fd);
启动一个计数型硬件定时器
aw_err_t aw_hwtimer_count_stop (int fd);
停止一个计数型硬件定时器
aw_err_t aw_hwtimer_count_get (int fd, uint64_t *p_count);
读取计数值
aw_err_t aw_hwtimer_count_rate_get (int fd, uint32_t *p_rate);
获取计数时钟频率
aw_err_t aw_hwtimer_count_rate_set (int fd, uint32_t rate);
设置计数时钟频率
aw_err_t aw_hwtimer_count_rate_set_accurate (int fd, uint32_t rate_numerator, uint32_t rate_denominator);
以精确化的方式设置计数时钟频率
aw_err_t aw_hwtimer_count_rate_get_accurate (int fd, uint32_t *p_rate_numerator, uint32_t *p_rate_denominator);
获取计数时钟频率的精确描述
周期型硬件定时器:
函数原型
简要描述
aw_err_t aw_hwtimer_period_wait (int fd, uint32_t wait_ms);
等待定时器周期中断
aw_err_t aw_hwtimer_period_intr (int fd);
打断周期型定时器的等待操作
aw_err_t aw_hwtimer_period_start (int fd);
启动定时器
aw_err_t aw_hwtimer_period_stop (int fd);
停止定时器
aw_err_t aw_hwtimer_period_count_get (int fd, uint64_t *p_count);
读取计数值
aw_err_t aw_hwtimer_period_count_freq_get (int fd, uint32_t *p_rate);
获取周期型定时器的硬件计数频率(不是中断频率)
aw_err_t aw_hwtimer_period_count_freq_get_frac (int fd, aw_hwtimer_rate_t *p_rate);
以更精确的分数形式获取周期型定时器的硬件计数频率(不是中断频率)
aw_err_t aw_hwtimer_period_intr_freq_set (int fd, uint32_t intr_freq);
设置中断频率
aw_err_t aw_hwtimer_period_intr_freq_get (int fd, uint32_t *p_intr_freq);
获取中断频率
aw_err_t aw_hwtimer_period_intr_freq_set_frac (int fd, aw_const aw_hwtimer_rate_t *p_intr_freq);
设置中断频率(以更精确的分数形式)
aw_err_t aw_hwtimer_period_intr_freq_get_frac (int fd, aw_hwtimer_rate_t *p_intr_freq);
获取中断频率(以更精确的分数形式)
输入捕获型硬件定时器:
函数原型
简要描述
aw_err_t aw_hwtimer_cap_start (int fd);
启动输入捕获型硬件定时器
aw_err_t aw_hwtimer_cap_stop (int fd);
停止输入捕获型硬件定时器
aw_err_t aw_hwtimer_cap_read (int fd, uint64_t *p_cap_val, uint32_t timeout_ms);
读取一个捕获到的事件的计数值
aw_err_t aw_hwtimer_cap_intr (int fd);
打断阻塞read读操作
aw_err_t aw_hwtimer_cap_config_set (int fd, aw_const aw_hwtimer_cap_config_t *p_config);
配置输入捕获型硬件定时器
aw_err_t aw_hwtimer_cap_config_get (int fd, aw_hwtimer_cap_config_t *p_config);
获取输入捕获型硬件定时器的配置

使用样例
AWorksLP SDK相关使用请参考《AWorksLP SDK快速入门(MR6450)——开箱体验》一文,本文不在赘述。
1. 周期型定时器
{SDK}demosperipheralhwtimer路径下为硬件定时器例程,默认运行的是demo_hwtimer.c 周期型定时器的代码,例程关键代码如下:
/**
* rief 硬件定时器中断服务函数。
* param[in] p_arg : 任务参数
*/
static void mytimer_isr (void *p_arg)
{
aw_gpio_toggle((int)p_arg);
aw_kprintf("enter isr ");
}


/**
* rief hwtimer 测试函数
*/
aw_local void* __task_handle (void *arg)
{
int fd;
aw_err_t ret;
uint32_t count = 5;
aw_hwtimer_rate_t p_intr_freq;


p_intr_freq.rate_denominator = 5;
p_intr_freq.rate_numerator = 1;


fd = aw_open(CONFIG_DEMO_HWTIMER_PEROID_DEV_NAME, AW_O_RDWR, 0);
if (fd < 0) {
aw_kprintf("hwtimer open failed:%d ", fd);
while(1);
}


ret = aw_hwtimer_period_intr_freq_set_frac(fd, &p_intr_freq);
while (count) {
aw_hwtimer_period_wait(fd, 500);
mytimer_isr(arg);
count --;
}


// 配置每秒中断2次
ret = aw_hwtimer_period_intr_freq_set(fd, 2);


ret = aw_hwtimer_period_start(fd);
if (ret != AW_OK) {
aw_kprintf("Timer allocation fail! ");
}


ret = aw_hwtimer_period_wait(fd, AW_WAIT_FOREVER);


while (1) {
aw_hwtimer_period_wait(fd, AW_WAIT_FOREVER);
mytimer_isr(arg);
}


for (;;) {
aw_mdelay(1000);
}
aw_close(fd);


return 0;
}
在代码中先使用了aw_hwtimer_period_intr_freq_set_frac 接口,以分数的形式设置中断频率,使用aw_hwtimer_period_start接口启动定时器。在循环中使用aw_hwtimer_period_wait 接口阻塞等待中断的产生、中断产生后继续执行mytimer_isr函数使LED 灯状态翻转,由于设置的频率为五分之一,所以5秒LED 灯的状态翻转一次;循环一定次数后用aw_hwtimer_period_intr_freq_set 接口设置中断频率为2HZ,循环等待中断、翻转LED。
实验现象为LED灯先以5s的频率闪烁,同时串口打印同时信息。闪烁一定次数后以0.5s的频率LED闪烁,同时串口打印信息。

下表为使用硬件周期型定时器,在中断中进行引脚翻转,通过逻辑分析仪所测量出的实际数据,在使用设计时可作为部分参考依据。

定时时间(s
实际时间(s
0.010000000
0.010000115
0.020000000
0.019999940
0.030000000
0.029999980
0.040000000
0.040000035
0.050000000
0.049999830
0.100000000
0.100000075
0.200000000
0.200000020
0.500000000
0.500000070
1.000000000
1.000000760
2.000000000
1.999999340
3.000000000
3.000002760
4.000000000
4.000001980
5.000000000
5.000004310
10.000000000
10.000008300
2.计数型定时器
在config配置脚本中选择hwtimer count计数型定时器测试如图1所示。
图1 计数型定时器例程
保存后重新Build工程,编译好后运行的是demo_hwtimer_count.c的代码,例程关键代码如下:
aw_local void* __task_handle (void *arg)
{
uint32_t count = 0;
int fd, led_fd;
int ret;
uint32_t start_count;

fd = aw_open(CONFIG_DEMO_HWTIMER_PEROID_DEV_NAME, AW_O_RDWR, 0);
if (fd < 0) {
aw_kprintf("hwtimer open fail! :%d ",fd);
return;
}
/* 打开设备会点亮LED */
led_fd = aw_open("/dev/led_run", AW_O_RDWR, 0);
if (led_fd < 0) {
aw_kprintf("led open fail! :%d ", led_fd);
aw_close(fd);
return;
}
ret = aw_hwtimer_count_rate_get(fd, &start_count);
if (ret != AW_OK) {
aw_kprintf("Timer count rate get fail! ");
aw_close(fd);
aw_close(led_fd);
return;
}
// 设置时钟频率
ret = aw_hwtimer_count_rate_set(fd, start_count/2);
if (ret != AW_OK) {
aw_kprintf("Timer count rate set fail! ");
aw_close(fd);
aw_close(led_fd);
return;
}
ret = aw_hwtimer_count_start(fd);
if (ret != AW_OK) {
aw_kprintf("Timer start fail! ");
aw_close(fd);
aw_close(led_fd);
return;
}
for (;;) {
aw_led_toggle(led_fd);
aw_mdelay(500);
aw_led_toggle(led_fd);
aw_hwtimer_count_get(fd, &count);
aw_kprintf("Count is %d ", count);
}
aw_close(fd);
aw_close(led_fd);
return 0;
}
在上述代码中使用了aw_hwtimer_count_rate_get接口获取改定时器时钟频率,可以在调试模式下查看获取到的参数,为100M 如图2所示。
图2查看参数
使用aw_hwtimer_count_rate_set接口设置定时器时钟的频率为50M,使用aw_hwtimer_count_start接口开启定时器,使用aw_hwtimer_count_get接口在循环中每延时500ms获取一次计数值,并在串口中打印,打印结果如图3所示。

图3串口打印计数值
打印出的计数值中,相邻两个计数值之差为25M,是由于设置定时器频率为50M,每延时500ms计数值增加25M。
3.延时型定时器
在config配置脚本中选择hwtimer delay延时型定时器测试如图4所示。
图4计数型定时器例程
保存后重新Build工程,编译好后运行的是demo_hwtimer_count.c的代码,例程关键代码如下:
aw_local void* __task_handle (void *arg)
{
int i;
int fd;
aw_err_t ret;
aw_timespec_t timespec;
aw_timestamp_t start_timestamp, stop_timestamp;
aw_timestamp_freq_t timestamp_freq;
uint64_t delay_ns, diff;
uint32_t ns_numerator = 1000000000;


timestamp_freq = aw_timestamp_freq_get();
while (0 == (timestamp_freq % 10)) {
timestamp_freq /= 10;
ns_numerator /= 10;
}


fd = aw_open(CONFIG_DEMO_HWTIMER_DELAY_DEV_NAME, AW_O_RDWR, 0);
if (fd < 0) {
aw_kprintf("hwtimer open failed:%d ", fd);
while(1);
}


delay_ns = 2001000;
for (i = 0; i < 100; i++) {
timespec.tv_sec = delay_ns / 1000000000u;
timespec.tv_nsec = (uint32_t)(delay_ns % 1000000000u);


start_timestamp = aw_timestamp_get();
ret = aw_hwtimer_delay(fd, ×pec);
if (ret !=AW_OK) {
aw_kprintf("hwtimer delay failed:%d ", ret);
}
aw_barrier();
stop_timestamp = aw_timestamp_get();


stop_timestamp -= start_timestamp;
diff = stop_timestamp;
diff *= ns_numerator;
diff /= timestamp_freq;


diff = diff - delay_ns;
aw_kprintf(
"hwtimer_delay delay = %u,diff = %u ns ",
(uint32_t)delay_ns,
(uint32_t)diff);
delay_ns += 100000;
}
aw_close(fd);
return 0;
}
上述代码中在延时开始前使用aw_timestamp_get接口记录时间戳,使用aw_hwtimer_delay接口进行延时,延时结束后记录结束时间戳,用两个时间戳的差值通过换算,用于对比延时不同时间下与timestamp相比的误差,并在串口中打印,打印后增加延时时间,再次循环,串口打印结果如下图所示。
图5串口打印结果
因外设接口调用时代码执行需要时间以及晶振等硬件会导致误差,分析例程打印数据可得,延时性定时器的软件开销在同一硬件以及接口下,其误差基本是一致的。
4. 捕获型定时器

{SDK}demosperipheralcap路径下为捕获型定时器例程,例程关键代码如下:

/* 单边沿触发*/
static void test_cap_single_edge(
int fd,
int gpio_cap,
uint32_t ms,
aw_hwtimer_cap_config_t *p_config,
int is_rising)
{
uint64_t cap_val1, cap_val2;
aw_err_t ret;


// 制造两次上升沿
mk_edge(gpio_cap, 5);
aw_task_delay(ms);
mk_edge(gpio_cap, 5);


// 此时应该产生了两次捕获事件
// 把它们读出来
ret = aw_hwtimer_cap_read(fd, &cap_val1, AW_WAIT_FOREVER);
if (AW_OK != ret) {
aw_kprintf("cap read cap_val1 failed ");
return;
}
ret = aw_hwtimer_cap_read(fd, &cap_val2, AW_WAIT_FOREVER);
if (AW_OK != ret) {
aw_kprintf("cap read cap_val2 failed ");
return;
}


cap_val2 -= cap_val1;
cap_val2 *= 1000000;
cap_val2 /= p_config->sample_rate;


if (is_rising) {
aw_kprintf("two rising edge between %u ms ", ms + 5);
}
else {
aw_kprintf("two falling edge between %u ms ", ms + 5);
}
aw_kprintf("two capture events between %llu us ", cap_val2);
}


static void demo_cap_base(int gpio_cap)
{
int fd;
aw_err_t ret;
aw_hwtimer_cap_config_t config;


// 使得测试GPIO输出为0
aw_gpio_set(gpio_cap, 0);


fd = aw_open(CONFIG_DEMO_HWTIMER_CAP_DEV_NAME, AW_O_RDWR, 0);
if (fd < 0) {
aw_kprintf("cap open failed! ");
return;
}


// 获取捕获定时器的配置
ret = aw_hwtimer_cap_config_get(fd, &config);
if (ret != AW_OK) {
aw_kprintf("cap config get failed... ");
aw_close(fd);
return ;
}


#if CONFIG_SINGLE_EDGE
int is_rising;
// 配置为上升沿触发捕获
config.cap_edge_flags = AW_CAPTURE_RISING_EDGE;
is_rising = 1;
ret = aw_hwtimer_cap_config_set(fd, &config);
if (ret != AW_OK) {
aw_kprintf("cap config set failed... ");
aw_close(fd);
return ;
}


ret = aw_hwtimer_cap_start(fd);
if (ret != AW_OK) {
aw_kprintf("cap start failed... ");
aw_close(fd);
return ;
}
test_cap_single_edge(fd, gpio_cap, 20, &config, is_rising);
#endif


aw_close(fd);
}
在CAP 例程中默认使用的是timer5_chan0,这个通道对应的引脚是PF08,可以通过查看工程下timer5_chan0对应的.h文件得知所使用的引脚的编号为168,通过查看hpm_pin.h头文件可知编号168对应的引脚为PF08 如下图所示。
图6默认通道
图7对应引脚编号

图8对应引脚
本实验中还用到了PF09 这个引脚,用于产生捕获事件,PF09 和 PF08 这两个引脚在开发板上并没有引出来,不利于这次实验,需要修改这两个引脚。
图9捕获产生引脚
参考{SDK} platformsplatform-hpm-aworks-lpoardsEPC6450-AWIdts 下的pins.dts 引脚描述文件,找到timer4_chan1 如图10所示,timer4_chan1 使用的引脚是PE25, 对应着开发板排针 UTX1 丝印的位置。
图10捕获产生引脚
打开配置界面将timer5_chan0修改为timer4_chan1 如图11所示,修改后点击保存,重新build工程。
图11配置界面
将CAP_GPIO 对应的引脚改为PIN_PE24,对应着开发板排针 URX1 丝印的位置,如图12所示。

图12CAP引脚
将 PE25 , PE24 这两个引脚,也就是排针上 URX1 和 UTX1 短接。

图13引脚位置
上诉代码中使用aw_hwtimer_cap_config_get接口获取捕获定时器的配置信息,配置AW_CAPTURE_RISING_EDGE单通道模式后使用aw_hwtimer_cap_config_set接口配置捕获定时器。使用aw_hwtimer_cap_start接口启动定时器。在test_cap_single_edge函数中调用mk_edge函数制造两次上升沿,使用aw_hwtimer_cap_read接口读取这两次事件捕获到的计数值,计算出差值后在串口上显示。
在test_cap_single_edge函数中使用mk_edge函数中控制CAP_GPIO引脚输出高电平后延时5ms再输出低电平。延时20ms后再次调用mk_edge函数,因此两次上升沿事件间隔应为25ms。串口打印结果如下图所示。
图14串口打印结果

至此,所有类型的硬件定时器样例均已展示完毕,在软件应用设计中可根据实际需求选取不同类型的定时器进行使用。更多其他类型外设的用法介绍,请关注后续同系列推文~


原文标题:【产品应用】AWorksLP 样例详解(MR6450)—— HWTimer

文章出处:【微信公众号:ZLG致远电子】欢迎添加关注!文章转载请注明出处。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 致远电子
    +关注

    关注

    13

    文章

    423

    浏览量

    32649

原文标题:【产品应用】AWorksLP 样例详解(MR6450)—— HWTimer

文章出处:【微信号:ZLG_zhiyuan,微信公众号:ZLG致远电子】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    FAN6756MR:高效节能的电源控制解决方案

    : FEBFAN6756MR_T03U065A.pdf FAN6756MR概述 产品描述 FAN6756是一款采用创新mWSaver™技术的下一代绿色模式PWM控制器
    的头像 发表于 04-11 09:05 226次阅读

    Atmel ATmega325/3250/645/6450微控制器:性能与特性深度解析

    Atmel ATmega325/3250/645/6450微控制器:性能与特性深度解析 在嵌入式系统设计领域,选择一款合适的微控制器至关重要。Atmel ATmega325/3250/645
    的头像 发表于 04-06 13:05 646次阅读

    固件烧录速度实测:JTAG比UART快6.8倍

    在批量生产场景中,固件烧录效率直接影响产线节拍与交付能力。如何选择最优烧录方式?本文以ZLG致远电子MR6450系列核心板为,通过实测数据为您解析。MR6450核心板MR6450是Z
    的头像 发表于 01-27 11:34 438次阅读
    固件烧录速度实测:JTAG比UART快6.8倍

    瑞萨RL78/F22 MCU基于e2studio开发环境创建触摸应用工程

    瑞萨RL78/F22 MCU基于e2studio开发环境创建触摸应用工程
    的头像 发表于 01-14 15:31 7986次阅读
    瑞萨RL78/F22 MCU基于e2studio开发环境创建触摸应用<b class='flag-5'>样</b><b class='flag-5'>例</b>工程

    瑞萨RL78/F25 MCU基于IAR开发环境创建触摸应用工程

    瑞萨RL78/F25 MCU基于IAR开发环境创建触摸应用工程
    的头像 发表于 01-05 14:59 8971次阅读
    瑞萨RL78/F25 MCU基于IAR开发环境创建触摸应用<b class='flag-5'>样</b><b class='flag-5'>例</b>工程

    MR30分布式I/O助力物流分拣系统智能化升级

    本文以MR30分布式I/O在某大型电商物流中心分拣系统的应用为,详细阐述其在合流、分拣、分流、分运全流程中的模块化应用方案。
    的头像 发表于 10-31 10:58 1150次阅读
    <b class='flag-5'>MR</b>30分布式I/O助力物流分拣系统智能化升级

    基于开源鸿蒙的图片编辑开发(2)

    本期内容介绍基于开源鸿蒙在RK3568上开发图片编辑的全流程,分为上篇和下篇,本篇为下篇,主要介绍标记、保存图片功能。
    的头像 发表于 10-31 09:19 3362次阅读
    基于开源鸿蒙的图片编辑开发<b class='flag-5'>样</b><b class='flag-5'>例</b>(2)

    基于开源鸿蒙的图片编辑开发(1)

    本期内容介绍基于开源鸿蒙在RK3568上开发图片编辑的全流程,分为上篇和下篇,本篇为上篇,主要介绍添加相册图片、编译图片功能。
    的头像 发表于 10-31 09:16 3215次阅读
    基于开源鸿蒙的图片编辑开发<b class='flag-5'>样</b><b class='flag-5'>例</b>(1)

    新洁能NCE65TF099:高频开关性能如何赋能不同产品应用

    在当今快速发展的电子技术领域,高频开关性能已成为衡量功率器件性能的关键指标之一。新洁能凭借其卓越的高频开关性能,正在为各种产品应用带来前所未有的赋能与变革。本文将深入探讨新洁能NCE65TF099
    的头像 发表于 10-20 16:21 981次阅读
    新洁能NCE65TF099:高频开关性能如何赋能不同<b class='flag-5'>产品应用</b>?

    MR30分布式I/O模块在化工行业的应用

    氧化铝等化工行业实现稳定生产。本期案例使用的MR30系列分布式I/O产品MR30-FBC-PN、MR30-16DI、MR30-16DO、
    的头像 发表于 09-05 11:30 784次阅读

    MR30分布式I/O模块在涂装产线中的应用

    某装备制造企业主要从事自动化焊割、涂装生产线的设计与制造,其中涂装生产线涉及喷砂、喷漆、热处理、废气处理等工艺。明达技术的MR30系列分布式I/O,性能稳定,易上手,方便接线,助力企业提升产品
    的头像 发表于 08-27 15:38 725次阅读
    <b class='flag-5'>MR</b>30分布式I/O模块在涂装产线中的应用

    基于开源鸿蒙的AVPlayer视频播控开发

    在开源鸿蒙生态建设中,多媒体能力是构建丰富用户体验的核心要素。本开发基于AVPlayer实现,AvPlayer支持流媒体和本地资源解析、媒体资源解封装、视频解码和渲染功能,适用于对媒体资源进行端
    的头像 发表于 08-21 10:22 3173次阅读
    基于开源鸿蒙的AVPlayer视频播控开发<b class='flag-5'>样</b><b class='flag-5'>例</b>

    RISC-V和ARM有何区别?

    在微处理器架构领域,ARM与RISC-V是两个备受关注的体系。ZLG致远电子在推出ARM核心版后,又推出了基于RISC-V的MR6450核心版,这引发了人们对这两种架构差异的深入探讨。ARM
    的头像 发表于 06-24 11:38 2230次阅读
    RISC-V和ARM有何区别?

    基于瑞萨电子RA8T2 sensorless方案的工程 可对电流环进行TCM化设置

    基于瑞萨电子RA8T2 sensorless方案的工程 可对电流环进行TCM化设置
    的头像 发表于 06-04 18:29 1245次阅读

    湖南银河电气有限公司产品应用案例

    湖南银河电气有限公司产品应用案例
    发表于 04-23 11:21 0次下载