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

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

3天内不再提示

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

ZLG致远电子 2023-02-02 11:00 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

AWorksLP 对外设进行了高度抽象化,为同一类外设提供了相同的接口,应用程序可以轻松跨平台。本文以MR6450平台为例,介绍AWorksLP HWTimer 外设基本用法。d2739af6-a278-11ed-ad0d-dac502259ad0.jpg 简介在AWorksLP中将硬件定时器分为了4类,即延时型、计数型、周期型和输入捕获型硬件定时器。

  • 延时型硬件定时器:

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

  • 计数型硬件定时器:

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

  • 周期型硬件定时器:

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

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


d2739af6-a278-11ed-ad0d-dac502259ad0.jpg 接口介绍延时型硬件定时器:

函数原型

简要描述

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);

获取输入捕获型硬件定时器的配置

d2739af6-a278-11ed-ad0d-dac502259ad0.jpg 使用样例AWorksLP SDK相关使用请参考《AWorksLP SDK快速入门(MR6450)——开箱体验》一文,本文不在赘述。1. 周期型定时器{SDK}\demos\peripheral\hwtimer路径下为硬件定时器例程,默认运行的是demo_hwtimer.c 周期型定时器的代码,例程关键代码如下:

/** * \brief 硬件定时器中断服务函数。 * \param[in] p_arg : 任务参数 */static void mytimer_isr (void *p_arg){ aw_gpio_toggle((int)p_arg); aw_kprintf("enter isr \n\r");}
/** * \brief 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 \n\r", 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!\n"); }
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所示。d29dd4b0-a278-11ed-ad0d-dac502259ad0.png图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\n",fd); return; } /* 打开设备会点亮LED */ led_fd = aw_open("/dev/led_run", AW_O_RDWR, 0); if (led_fd < 0) { aw_kprintf("led open fail! :%d\n", 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!\n"); 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!\n"); aw_close(fd); aw_close(led_fd); return; } ret = aw_hwtimer_count_start(fd); if (ret != AW_OK) { aw_kprintf("Timer start fail!\n"); 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\r\n", count); } aw_close(fd); aw_close(led_fd); return 0;}

在上述代码中使用了aw_hwtimer_count_rate_get接口获取改定时器时钟频率,可以在调试模式下查看获取到的参数,为100M 如图2所示。d2bcdc8e-a278-11ed-ad0d-dac502259ad0.png图2查看参数使用aw_hwtimer_count_rate_set接口设置定时器时钟的频率为50M,使用aw_hwtimer_count_start接口开启定时器,使用aw_hwtimer_count_get接口在循环中每延时500ms获取一次计数值,并在串口中打印,打印结果如图3所示。

d2ce07ac-a278-11ed-ad0d-dac502259ad0.png

图3串口打印计数值打印出的计数值中,相邻两个计数值之差为25M,是由于设置定时器频率为50M,每延时500ms计数值增加25M。3.延时型定时器在config配置脚本中选择hwtimer delay延时型定时器测试如图4所示。d2dbe886-a278-11ed-ad0d-dac502259ad0.png图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 \n\r", 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 \n\r", 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\n", (uint32_t)delay_ns, (uint32_t)diff); delay_ns += 100000; } aw_close(fd); return 0;}

上述代码中在延时开始前使用aw_timestamp_get接口记录时间戳,使用aw_hwtimer_delay接口进行延时,延时结束后记录结束时间戳,用两个时间戳的差值通过换算,用于对比延时不同时间下与timestamp相比的误差,并在串口中打印,打印后增加延时时间,再次循环,串口打印结果如下图所示。d2e8114c-a278-11ed-ad0d-dac502259ad0.png图5串口打印结果因外设接口调用时代码执行需要时间以及晶振等硬件会导致误差,分析例程打印数据可得,延时性定时器的软件开销在同一硬件以及接口下,其误差基本是一致的。4. 捕获型定时器

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

/* 单边沿触发*/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 \n"); return; } ret = aw_hwtimer_cap_read(fd, &cap_val2, AW_WAIT_FOREVER); if (AW_OK != ret) { aw_kprintf("cap read cap_val2 failed \n"); 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 \n", ms + 5); } else { aw_kprintf("two falling edge between %u ms \n", ms + 5); } aw_kprintf("two capture events between %llu us \n", 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!\n"); return; }
// 获取捕获定时器的配置 ret = aw_hwtimer_cap_config_get(fd, &config); if (ret != AW_OK) { aw_kprintf("cap config get failed...\r\n"); 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...\r\n"); aw_close(fd); return ; }
ret = aw_hwtimer_cap_start(fd); if (ret != AW_OK) { aw_kprintf("cap start failed...\r\n"); 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 如下图所示。d2f71a48-a278-11ed-ad0d-dac502259ad0.png图6默认通道d314c250-a278-11ed-ad0d-dac502259ad0.png图7对应引脚编号

d325495e-a278-11ed-ad0d-dac502259ad0.png

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

d3735f7c-a278-11ed-ad0d-dac502259ad0.png

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

d38306fc-a278-11ed-ad0d-dac502259ad0.png

图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。串口打印结果如下图所示。d3a7e5c6-a278-11ed-ad0d-dac502259ad0.png图14串口打印结果

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

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

    关注

    33

    文章

    9446

    浏览量

    156145
  • 定时器
    +关注

    关注

    23

    文章

    3361

    浏览量

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

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

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

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

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

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

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

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

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

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

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

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

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

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

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

    在开源鸿蒙生态建设中,多媒体能力是构建丰富用户体验的核心要素。本开发基于AVPlayer实现,AvPlayer支持流媒体和本地资源解析、媒体资源解封装、视频解码和渲染功能,适用于对媒体资源进行端
    的头像 发表于 08-21 10:22 2359次阅读
    基于开源鸿蒙的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 1730次阅读
    RISC-V和ARM有何区别?

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

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

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

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

    RISC-V生态崛起:政策落地与高性能芯片的崛起

    近期,RISC-V生态取得重要进展,包括高性能核心板的推出和在工业自动化、物联网等领域的广泛应用。其中,致远电子MR6450系列RISC-V核心板,凭借高性能和丰富的通信接口,成为工业控制和物联网
    的头像 发表于 03-06 11:35 1165次阅读
    RISC-V生态崛起:政策落地与高性能芯片的崛起

    深度解析 | 基于HPM6450的RISC-V核心板究竟有哪些过人之处?

    本文导读随着AIoT时代的到来,RISC-V作为新兴架构,其精简及开源的特性在物联网的应用领域有很大的优势,为此ZLG致远电子推出MR6450系列是基于先楫HPM6450的全新RISC-V核心板
    的头像 发表于 02-08 13:45 1348次阅读
    深度解析 | 基于HPM<b class='flag-5'>6450</b>的RISC-V核心板究竟有哪些过人之处?

    40mR/650V SiC 碳化硅MOSFET,替代30mR 超结MOSFET或者20-30mR的GaN!

    BASiC基本半导体40mR/650V SiC 碳化硅MOSFET,替代30mR 超结MOSFET或者20-30mR的GaN! BASiC基本半导体40mR/650V SiC 碳化硅M
    发表于 01-22 10:43

    关于cc2541程序代码

    CC2541哪里有cc2541的模数转换模块和蓝牙模块的程序代码呀?初学不懂
    发表于 01-20 07:14

    深蕾半导体开发者中心上线多个AI Demo

    近日深蕾半导体开发者中心更新了VS680 AI演示视频、AI Demo、VS680快速开发手册等相关内容。
    的头像 发表于 01-10 14:48 750次阅读
    深蕾半导体开发者中心上线多个AI Demo<b class='flag-5'>样</b><b class='flag-5'>例</b>