Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RT-Thread一般讨论
M2354
【NuMaker-M2354试用】ECAP测试分享
发布于 2021-12-12 21:20:29 浏览:912
订阅该版
#1、ECAP详解 ECAP既外部脉冲捕获功能,M2354提供了两个输入捕获计时器单元有三个输入捕获通道,可以用来测量脉冲宽度或者测量频率。根据M2354的数据手册说明可以实现上升沿、下降沿或者双边沿的检测。 #2、硬件 采用M2354的PC.10既ECAP1的通道0进行脉冲检测.  脉冲输出采用AB32VG1(上次参加测试获得板子)的PA2进行周期为5ms,PWM脉冲宽度值为2.5ms、3ms以及周期为7ms,PWM脉冲宽度值为3ms的脉冲输出  #3、工程裁剪及修改 这里请参考我的上一篇文章[【NuMaker-M2354试用】ADC测试分享](https://club.rt-thread.org/ask/article/3197.html),在这个基础上使用ENV输入如下指令: menuconfig 开启操作界面后,在Hardware Drivers Config下的On-chip Peripheral Drivers中开启ECAP功能的ECAP1,并选择通道号为1(这里的1代表通道0)。   最后输入scons --target=mdk5(MDK5用户)或者scons --target=IAR(IAR用户)重新构建工程。 注意,到这里后,还需要手动修改一下引脚配置,这里选用的是PC.10作为ecap1l0,需要在nutool_pincfg.c中的nutool_pincfg_init()函数中添加如下代码 ``` /* Set PC.10 for ECAP1_IC0*/ SYS->GPC_MFPH &= ~SYS_GPC_MFPH_PC10MFP_Msk; SYS->GPC_MFPH |= SYS_GPC_MFPH_PC10MFP_ECAP1_IC0; ``` #4、软件 ##4.1 AB32VG1的脉冲输出程序 这里就不过多介绍了,只贴代码 ``` #include
#include
#define PWM_DEV_NAME "lpwm2" /* PWM设备名称 */ #define PWM_DEV_CHANNEL 3 /* PWM通道 */ struct rt_device_pwm *pwm_dev; /* PWM设备句柄 */ static int Pwm_Normal_Run(void) { rt_uint32_t period, pulse, dir; period = 5000000; /* 周期为5ms,单位为纳秒
ns
*/ dir = 1; /* PWM脉冲宽度值的增减方向 */ pulse = 2500000; /* PWM脉冲宽度值,单位为纳秒
ns
*/ /* 查找设备 */ pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME); if (pwm_dev == RT_NULL) { rt_kprintf("
pwm
sample run failed! can't find %s device!\n",PWM_DEV_NAME); return RT_ERROR; } /* 设置PWM周期和脉冲宽度默认值 */ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); /* 使能设备 */ rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL); } /* 导出到
msh
命令列表中 */ INIT_DEVICE_EXPORT(Pwm_Normal_Run); static int PWM_TEST1(int argc, char *argv[]) { rt_uint32_t period, pulse, dir; period = 5000000; /* 周期为5ms,单位为纳秒ns */ dir = 1; /* PWM脉冲宽度值的增减方向 */ pulse = 3000000; /* PWM脉冲宽度值,单位为纳秒ns */ /* 设置PWM周期和脉冲宽度 */ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(PWM_TEST1, PWM_TEST1); static int PWM_TEST2(int argc, char *argv[]) { rt_uint32_t period, pulse, dir; period = 7000000; /* 周期为7ms,单位为纳秒ns */ dir = 1; /* PWM脉冲宽度值的增减方向 */ pulse = 3000000; /* PWM脉冲宽度值,单位为纳秒ns */ /* 设置PWM周期和脉冲宽度 */ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(PWM_TEST2, PWM_TEST2); ``` AB32VG1的PA2上电就输出5ms周期,宽度为2.5ms的PWM波,在控制台输入PWM_TEST1输出5ms周期,宽度为3ms的PWM波,输入PWM_TEST2输出7ms周期,宽度为3ms的PWM波。这里就不过多介绍了,不然该跑题了。 ##4.2 ECAP设备 官网上没有关于ECAP的设备介绍,我在这里仿照官网稍微写一写。 ###4.2.1 访问ECAP设备 应用程序通过RT-Thread提供的ECAP设备管理接口来访问ECAP设备硬件,相关接口如下所示: 函数 | 描述 -----|------- rt_device_find() | 根据ECAP设备名称查找设备获取设备句柄 rt_device_init() | 初始化设备 rt_device_open() | 开启设备 rt_device_close() | 关闭设备 rt_device_control() | 控制设备 rt_device_set_rx_indicate | 设置接收回调函数 rt_device_read() | 读取数据 ###4.2.2 查找ECAP设备 应用程序根据ECAP设备名称获取设备句柄,进而可以操作ECAP设备,查找设备函数如下所示, ``` rt_device_t rt_device_find(const char* name); ``` 参数 | 描述 -----|------- name | 设备名称 返回 | —— 设备句柄 | 查找到对应设备将返回相应的设备句柄 RT_NULL | 没有找到相应的设备对象 注册到系统的ECAP设备名称为 ecap0i0,ecap0i1,ecap0i2,ecap1i0,ecap1i1,ecap1i2,使用示例如下所示: ``` #define ECAP_DEV_NAME "ecap1i0" /* ECAP设备名称 */ static rt_device_t ecap_dev; /* ECAP设备句柄 */ /* 查找ECAP设备 */ ecap_dev = rt_device_find(ECAP_DEV_NAME); ``` ###4.2.3 初始化ECAP设备 通过设备句柄,应用程序初始化设备,初始化设备时,会检测设备是否初始化成功。通过如下函数初始化ECAP设备: ``` rt_err_t rt_device_init(rt_device_t dev); ``` 参数 | 描述 -----|------- name | 设备名称 返回 | —— 设备句柄 | 查找到对应设备将返回相应的设备句柄 RT_EOK | 设备初始化成功 RT_ERROR| 设备初始化失败 ###4.2.4 打开ECAP设备 通过设备句柄,应用程序可以打开和关闭设备,打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。通过如下函数打开设备: ``` rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag); ``` 参数 | 描述 -----|------- dev | 设备句柄 oflags | 设备模式标志 返回 | —— RT_EOK | 设备打开成功 -RT_EBUSY | 如果设备注册时指定的参数中包括 RT_DEVICE_FLAG_STANDALONE 参数,此设备将不允许重复打开 其他错误码| 设备打开失败 oflags ECAP未实现: ###4.2.5 关闭ECAP设备 当应用程序完成ECAP操作后,可以关闭ECAP设备,通过如下函数完成: ``` rt_err_t rt_device_close(rt_device_t dev); ``` 参数 |描述 -----|------- dev| 设备句柄 返回| —— RT_EOK |关闭设备成功 -RT_ERROR| 设备已经完全关闭,不能重复关闭设备 其他错误码|关闭设备失败 关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。 ###4.2.6 控制ECAP设备 通过控制接口,应用程序可以对ECAP设备进行配置,如设定脉冲捕获的缓存区大小修改以及清空缓存区。控制函数如下所示: ``` rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg); ``` 参数| 描述 -----|------- dev| 设备句柄 cmd| 命令控制字,可取值:INPUTCAPTURE_CMD_CLEAR_BUF、INPUTCAPTURE_CMD_SET_WATERMARK arg| 控制的参数,可取类型: rt_size_t * 返回| —— RT_EOK| 函数执行成功 -RT_ENOSYS| 执行失败,dev 为空 其他错误码| 执行失败 实际使用ECAP的示例程序如下: ``` #define ECAP_DEV_NAME "ecap1i0" /* ECAP设备名称 */ static rt_device_t ecap_dev; /* ECAP设备句柄 */ static struct rt_semaphore ecap_sem; rt_size_t SIZE = 1; /* 查找ECAP设备 */ ecap_dev = rt_device_find(ECAP_DEV_NAME); rt_device_init(ecap_dev); /* 控制input设备*/ rt_device_control(ecap_dev, INPUTCAPTURE_CMD_SET_WATERMARK, &SIZE); /* 打开inputcapture设备 */ rt_device_open(ecap_dev,0); ``` ###4.2.7 设置接收回调函数 可以通过如下函数来设置数据接收指示,当ECAP捕获到数据时,通知上层应用线程有数据到达 : ``` 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| 回调函数指针 dev |设备句柄(回调函数参数) size |缓冲区数据大小(回调函数参数) 返回| —— RT_EOK |设置成功 该函数的回调函数由调用者提供。若ECAP以中断接收模式打开,当ECAP捕获到一个脉冲边沿中断时,就会调用回调函数,并且会把此时缓冲区的数据大小放在 size 参数里,把ECAP设备句柄放在 dev 参数里供调用者获取。 一般情况下接收回调函数可以发送一个信号量或者事件通知捕获数据处理线程有数据到达。使用示例如下所示: ``` #define ECAP_DEV_NAME "ecap1i0" /* ECAP设备名称 */ static rt_device_t ecap_dev; /* ECAP设备句柄 */ static struct rt_semaphore ecap_sem; /* 查找ECAP设备 */ ecap_dev = rt_device_find(ECAP_DEV_NAME); /* 接收数据回调函数 */ static rt_err_t input_capture(rt_device_t dev, rt_size_t size) { rt_sem_release(&ecap_sem); return RT_EOK; } static int ECAP_sample(int argc, char *argv[]) { rt_err_t ret = RT_EOK; unsigned char SIZE = 1; /* 查找设备 */ ecap_dev = rt_device_find(ECAP_DEV_NAME); ret = rt_device_init(ecap_dev); if(ret == RT_EOK) { /* 控制input设备*/ rt_device_control(ecap_dev, INPUTCAPTURE_CMD_SET_WATERMARK, &SIZE); /* 初始化信号量 */ rt_sem_init(&ecap_sem, "ecap_sem", 0, RT_IPC_FLAG_FIFO); /* 打开inputcapture设备 */ rt_device_open(ecap_dev,0); /* 设置回调函数 */ rt_device_set_rx_indicate(ecap_dev,input_capture); } } ``` ###4.2.8 接收数据 可调用如下函数读取ECAP采集到的脉冲数据: ``` rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size); ``` 参数 |描述 -----|------- dev | 设备句柄 pos | 读取数据偏移量,此参数串口设备未使用 buffer | 缓冲区指针,读取的数据将会被保存在缓冲区中 size |读取数据的大小 返回 | —— 读到数据的实际大小 | 如果是字符设备,返回大小以字节为单位 0 | 需要读取当前线程的 errno 来判断错误状态 读取数据偏移量 pos 针对ECAP设备无效。 ECAP使用中断接收模式并配合接收回调函数的使用示例如下所示: ``` #define ECAP_DEV_NAME "ecap1i0" /* ECAP 设备名称 */ /* 用于接收消息的信号量 */ static struct rt_semaphore ECAP_sem; static rt_device_t ecap_dev; static void ecap_thread_entry(void *parameter) { struct rt_inputcapture_data ch; while (1) { /* 从ecap读取1个采集信息,没有读取到则等待接收信号量 */ while (rt_device_read(ecap_dev, -1, &ch, 2) != 1) { /* 清空缓存区 */ rt_device_control(ecap_dev, INPUTCAPTURE_CMD_CLEAR_BUF, 0); /* 阻塞等待接收信号量,等到信号量后再次读取数据 */ rt_sem_take(&ECAP_sem, RT_WAITING_FOREVER); } /* 读取到的数据输出 */ rt_kprintf("%d,%d\n",ch.pulsewidth_us,ch.is_high); } } ``` 这里需要注意rt_inputcapture_data结构体定义 ``` struct rt_inputcapture_data { rt_uint32_t pulsewidth_us; rt_bool_t is_high; }; ``` 这里包含两个信息,pulsewidth_us为采集到的脉冲宽度,is_high代表高低脉冲信息。 ##4.3 使用实例 ``` #include
#include
#define ECAP_DEV_NAME "ecap1i0" /* ECAP 设备名称 */ /* 用于接收消息的信号量 */ static struct rt_semaphore ECAP_sem; static rt_device_t ecap_dev; static rt_err_t input_capture(rt_device_t dev, rt_size_t size) { rt_sem_release(&ECAP_sem); return RT_EOK; } static void ecap_thread_entry(void *parameter) { struct rt_inputcapture_data ch; while (1) { /* 从ecap读取1个采集信息,没有读取到则等待接收信号量 */ while (rt_device_read(ecap_dev, -1, &ch, 2) != 1) { /* 清空缓存区 */ rt_device_control(ecap_dev, INPUTCAPTURE_CMD_CLEAR_BUF, 0); /* 阻塞等待接收信号量,等到信号量后再次读取数据 */ rt_sem_take(&ECAP_sem, RT_WAITING_FOREVER); } /* 读取到的数据输出 */ rt_kprintf("%d,%d\n",ch.pulsewidth_us,ch.is_high); } } static int ECAP_sample(int argc, char *argv[]) { rt_err_t ret = RT_EOK; rt_size_t SIZE = 1; /* 查找设备 */ ecap_dev = rt_device_find(ECAP_DEV_NAME); if (ecap_dev == RT_NULL) { rt_kprintf("ecap sample run failed! can't find %s device!\n", ECAP_DEV_NAME); return RT_ERROR; } else { rt_kprintf("find %s device success!\n", ECAP_DEV_NAME); } ret = rt_device_init(ecap_dev); if(ret == RT_EOK) { /* 控制input设备*/ rt_device_control(ecap_dev, INPUTCAPTURE_CMD_SET_WATERMARK, &SIZE); /* 初始化信号量 */ rt_sem_init(&ECAP_sem, "ECAP_sem", 0, RT_IPC_FLAG_FIFO); /* 打开inputcapture设备 */ rt_device_open(ecap_dev,0); /* 设置回调函数 */ rt_device_set_rx_indicate(ecap_dev,input_capture); } else { rt_kprintf("ecap sample init failed!\n"); return RT_ERROR; } /* 创建 serial 线程 */ rt_thread_t thread = rt_thread_create("ecap", ecap_thread_entry, RT_NULL, 512, 25, 10); /* 创建成功则启动线程 */ if (thread != RT_NULL) { rt_thread_startup(thread); } else { ret = RT_ERROR; } return ret; } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(ECAP_sample, ecap sample); ``` ##4.4 效果演示 AB32VG1发送周期为5ms,PWM脉冲宽度为2.5ms的情况下,M2354的监测情况  可以看到M2354采集到的脉冲信息为2500,1;2500,0。采集正确。 AB32VG1发送周期为5ms,PWM脉冲宽度为3ms的情况下,M2354的监测情况  可以看到M2354采集到的脉冲信息为3000,1;2000,0。采集正确。 AB32VG1发送周期为7ms,PWM脉冲宽度为3ms的情况下,M2354的监测情况  可以看到M2354采集到的脉冲信息为3000,1;4000,0。采集正确。 #5、心得体会 本次测试内容为M2354的ECAP设备使用,因为设备驱动层以及设备硬件框架均由官方实现了,所以就不需要自己花费时间去查看手册动手去操作了,再加上ENV的项目构建功能,大大减少了开发时间。总的来说M2354+RT-Thread+env的开发模式相对简单、很容易上手。唯一的不足是对于这款开发板了解太少,我的代码仅能仿真运行,脱离仿真就跑不动,一直没有找到原因,还有就是rt_hw_us_delay不准,没有找到解决方案希望有大佬能够帮忙解决下。 因本人水平有限,最近太忙,没有过多的研究此款芯片,文章内容及代码中可能会出现错误,还请谅解。 #6、代码地址 [gitee代码仓](https://gitee.com/werper/nu-maker-m2354_-test)
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
werper
叫我点灯大师
文章
10
回答
19
被采纳
1
关注TA
发私信
相关文章
1
有关动态模块加载的一篇论文
2
最近的调程序总结
3
晕掉了,这么久都不见layer2的踪影啊
4
继续K9ii的历程
5
[GUI相关] FreeType 2
6
[GUI相关]嵌入式系统中文输入法的设计
7
20081101 RT-Thread开发者聚会总结
8
嵌入式系统基础
9
linux2.4.19在at91rm9200 上的寄存器设置
10
[转]基于嵌入式Linux的通用触摸屏校准程序
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
rt-smart
FAL
I2C_IIC
UART
ESP8266
cubemx
WIZnet_W5500
ota在线升级
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
GD32
ADC
flashDB
编译报错
socket
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
ulog
SFUD
msh
C++_cpp
MicroPython
本月问答贡献
出出啊
1520
个答案
343
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
813
个答案
178
次被采纳
crystal266
553
个答案
162
次被采纳
whj467467222
1222
个答案
149
次被采纳
本月文章贡献
出出啊
1
篇文章
5
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
1
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部