Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
rt-smart
RT-Thread
rt-smart用户态通过IPC通信玩转传感器数据
发布于 2021-01-14 09:48:19 浏览:879
订阅该版
[tocm] ## 简介 - 首先纠正一下上一篇文章中,在我的仓库中,1月11日的代码会出现系统崩溃。原因在于我的驱动中内存物理地址映射到虚拟地址的操作有问题,我已经把这个bug解决了,如果有兴趣,欢迎拉取最新的代码。 - 这一篇来介绍我在rt-smart的第二个应用。这个应用将加入rt-smart与rt-thread区别之处--进程间的通信。 - 功能主要是在用户态读取传感器数据,传感器是100ASK_imx6ull板载的ap3216c,它是采用I2C总线进行通信。 - 为啥这次会先对接I2C呢?因为接下来想把屏幕在rt-smart跑起来,但是屏幕的触摸芯片采用I2C,所以就把他先跑起来。 - 目前屏幕已经在rt-thread上跑起来,但是在rt-smart没有跑起来,目前在研究LCD的缓存是一个什么样一个形式。 - 100ask_imx6ull驱动对接情况: | | rt-thread | rt-smart | |--|-----------|----------| | GPIO | √ | √ | | I2C | √ | √ | | lcd | √ | × | ### 100ask_imx6ull的rtt仓库: - rt-thread的仓库:https://gitee.com/RiceChen0/imx6ull_rt_rthread - rt-smart的厂库:https://gitee.com/RiceChen0/imx6ull_rt_smart ## 环境 1. 100ask_imx6ull开发板。 2. 两条micro USB线。 3. 电源。 4. windows电脑一台。 ## I2C驱动适配 1. 在imx6ull中,我适配的是硬件I2C,imx6ull有4组I2C接口。软件I2C后续不会进行适配,因为在这颗芯片上,软件I2C的必要性不大。 2. 如果你要了解RT-Thread的I2C设备驱动框架,可以看一下我之前的文章[《rt-thread驱动框架分析》-i2c驱动](https://mp.weixin.qq.com/s/GExLjvv-yyfONxsbGFdKhA) 3. 在上一篇文章中[《rt-smart的第一个应用程序,imx6ull用户态点灯》](https://mp.weixin.qq.com/s/vFvxlkPuEa2xQhaho-kKoQ)讲到,rt-smart不能直接使用物理地址访问硬件,而需要采用虚拟地址。所以需要进行地址映射(rtt提供的API:rt_hw_kernel_phys_to_virt)。 4. 首先需要查看imx6ull的芯片手册,需要将I2C相关的物理地址找到。为了不要重复造轮子,定义了一个结构体:struct i2c_addr_config,并把4组I2C相关的地址作为一个表格。如下: ``` C #define I2C1_SCL_MUX_BASE 0x020E00B4U #define I2C2_SCL_MUX_BASE 0x020E00BCU #define I2C3_SCL_MUX_BASE 0x020E00E4U #define I2C4_SCL_MUX_BASE 0x020E00ECU #define I2C1_SCL_CFG_BASE 0x020E0340U #define I2C2_SCL_CFG_BASE 0x020E0348U #define I2C3_SCL_CFG_BASE 0x020E0370U #define I2C4_SCL_CFG_BASE 0x020E0378U #define I2C1_SCL_INPUT_BASE 0x020E05A4U #define I2C2_SCL_INPUT_BASE 0x020E05ACU #define I2C3_SCL_INPUT_BASE 0x020E05B4U #define I2C4_SCL_INPUT_BASE 0x020E05BCU #define I2C1_SDA_MUX_BASE 0x020E00B8U #define I2C2_SDA_MUX_BASE 0x020E00C0U #define I2C3_SDA_MUX_BASE 0x020E00E8U #define I2C4_SDA_MUX_BASE 0x020E00F0U #define I2C1_SDA_CFG_BASE 0x020E0344U #define I2C2_SDA_CFG_BASE 0x020E034CU #define I2C3_SDA_CFG_BASE 0x020E0374U #define I2C4_SDA_CFG_BASE 0x020E037CU #define I2C1_SDA_INPUT_BASE 0x020E05A8U #define I2C2_SDA_INPUT_BASE 0x020E05B0U #define I2C3_SDA_INPUT_BASE 0x020E05B8U #define I2C4_SDA_INPUT_BASE 0x020E05C0U struct i2c_addr_config { I2C_Type *i2c; size_t i2c_scl_mux_base; size_t i2c_scl_config_base; size_t i2c_scl_input_base; size_t i2c_sda_mux_base; size_t i2c_sda_config_base; size_t i2c_sda_input_base }; static struct i2c_addr_config addr_config[] = { {I2C1, I2C1_SCL_MUX_BASE, I2C1_SCL_CFG_BASE, I2C1_SCL_INPUT_BASE, I2C1_SDA_MUX_BASE, I2C1_SDA_CFG_BASE, I2C1_SDA_INPUT_BASE}, {I2C2, I2C2_SCL_MUX_BASE, I2C2_SCL_CFG_BASE, I2C2_SCL_INPUT_BASE, I2C2_SDA_MUX_BASE, I2C2_SDA_CFG_BASE, I2C2_SDA_INPUT_BASE}, {I2C3, I2C3_SCL_MUX_BASE, I2C3_SCL_CFG_BASE, I2C3_SCL_INPUT_BASE, I2C3_SDA_MUX_BASE, I2C3_SDA_CFG_BASE, I2C3_SDA_INPUT_BASE}, {I2C4, I2C4_SCL_MUX_BASE, I2C4_SCL_CFG_BASE, I2C4_SCL_INPUT_BASE, I2C4_SDA_MUX_BASE, I2C4_SDA_CFG_BASE, I2C4_SDA_INPUT_BASE}, }; ``` 5. 将物理地址转为虚拟地址,代码如下: ``` C for(i = 0; i < sizeof(addr_config) / sizeof(addr_config[0]); i++) { addr_config[i].i2c = (I2C_Type *)rt_hw_kernel_phys_to_virt((void*)(addr_config[i].i2c), 0x1000); addr_config[i].i2c_scl_mux_base = (size_t)rt_hw_kernel_phys_to_virt((void*)(addr_config[i].i2c_scl_mux_base), 0x1000); addr_config[i].i2c_scl_config_base = (size_t)rt_hw_kernel_phys_to_virt((void*)(addr_config[i].i2c_scl_config_base), 0x1000); addr_config[i].i2c_scl_input_base = (size_t)rt_hw_kernel_phys_to_virt((void*)(addr_config[i].i2c_scl_input_base), 0x1000); addr_config[i].i2c_sda_mux_base = (size_t)rt_hw_kernel_phys_to_virt((void*)(addr_config[i].i2c_sda_mux_base), 0x1000); addr_config[i].i2c_sda_config_base = (size_t)rt_hw_kernel_phys_to_virt((void*)(addr_config[i].i2c_sda_config_base), 0x1000); addr_config[i].i2c_sda_input_base = (size_t)rt_hw_kernel_phys_to_virt((void*)(addr_config[i].i2c_sda_input_base), 0x1000); } ``` 6. 在imx6ull中,I2C需要的步骤,引脚初始化为I2C,然后I2C总线初始化便可以了。 7. 目前imx6ull上,rt-thread和rt-smart都适配I2C,所以可以先看一下rt-thread的仓库,然后再看rt-smart的仓库,可能更加理解它的区别。 ## I2C的应用: 1. 100ask_imx6ull中,板载有ap3216c传感器,挂载在I2C1总线上。而且RT-Thread中有相应的软件包,对接了RT-Thread的传感器设备框架,这给我验证代码提供便携。不过要在用户态中使用该软件包,还需要做一点操作,需要注册该传感器设备。 ``` C int ap3216c_test() { struct rt_sensor_config cfg; cfg.intf.dev_name = "i2c1"; cfg.mode = RT_SENSOR_MODE_POLLING; rt_hw_ap3216c_init("ap", &cfg); return RT_EOK; } INIT_DEVICE_EXPORT(ap3216c_test); ``` 2. 然后编译烧录,通过list_device就可以看到相对应的设备(pr_ap和li_ap),如下: ![](https://oss-club.rt-thread.org/uploads/20220714/91ee4644eac30c8f7990c389c8b22ab773943890.png) 3. RT_Thread的传感器框架很贴心,提供了测试命令(sensor_polling li_ap),这样就可以初步验证传感器是否正常工作,通过验证,传感器和I2C适配都能正常工作: ![](https://oss-club.rt-thread.org/uploads/20220714/5fe73feff68c5747d2871d8c38ff4c09ab9e17ba.png) 4. 上面的验证都是在内核态中测试的,而这篇文章的目的是要在用户态中读取传感器数据,为了进一步了解rt-smart和RT-Thread的区别,我这个应用采用进程通信(IPC)做了例子,该例子将上一篇文章例子结合起来: - 有两个进程, 进程1和进程2 - 进程1,通过接收等待进程2读取的传感器数据是否超标的状态,来进行闪灯。 - 进程2,通过读取ap3216c传感器光强度数据,判断是否超过50lux,如果超过则通知进程1进行闪灯提示。 5. IPC通信,详情可以查看官网:https://www.rt-thread.org/document/site/rt-smart/architecture/architecture/。 6. 进程1,等待接收通道发来的"warning"信息,然后进行闪灯操作: ``` C int main(int argc, char **argv) { struct rt_device_pin_mode pin_mode; struct rt_device_pin_status pin_status; int server_ch; int shmid; struct rt_channel_msg msg_text; char *str; printf("RiceChen rt-smart first app\n"); /* create the IPC channel for 'server' */ server_ch = rt_channel_open("server", O_CREAT); if (server_ch == -1) { printf("Error: rt_channel_open: fail to create the IPC channel for server!\n"); return -1; } printf("\nserver: wait on the IPC channel: %d\n", server_ch); pin_dev = rt_device_find("pin"); if(pin_dev == RT_NULL) { printf("not find pin device\n"); return RT_ERROR; } rt_device_open(pin_dev, RT_DEVICE_OFLAG_RDWR); pin_mode.pin = LED_PIN; pin_mode.mode = 0; //OUTPUT rt_device_control(pin_dev, 0, (void *)&pin_mode); pin_status.pin = LED_PIN; while(1) { rt_channel_recv(server_ch, &msg_text); //接收通道信息 shmid = (int)msg_text.u.d; if (shmid < 0 || !(str = (char *)lwp_shmat(shmid, NULL))) { msg_text.u.d = (void *)-1; printf("server: receive an invalid shared-memory page.\n"); rt_channel_reply(server_ch, &msg_text); /* send back -1 */ continue; } if(strcmp(str, "warning") == 0) //判断是否接收到"warning"信息 { printf("light warning.\n"); pin_status.status = 1; rt_device_write(pin_dev, 0, (void *)&pin_status, sizeof(pin_status)); rt_thread_mdelay(200); pin_status.status = 0; rt_device_write(pin_dev, 0, (void *)&pin_status, sizeof(pin_status)); rt_thread_mdelay(200); pin_status.status = 1; rt_device_write(pin_dev, 0, (void *)&pin_status, sizeof(pin_status)); rt_thread_mdelay(200); pin_status.status = 0; rt_device_write(pin_dev, 0, (void *)&pin_status, sizeof(pin_status)); rt_thread_mdelay(200); } lwp_shmdt(str); msg_text.type = RT_CHANNEL_RAW; msg_text.u.d = (void *)1; rt_channel_reply(server_ch, &msg_text); } return 0; } ``` 7. 进程2,间隔两面读取一次传感器光强度数据,然后判断是否操作50lux,超过则通过通道通知进程1进行闪灯: ``` C int main(int argc, char **argv) { rt_device_t ap3216c_dev; struct rt_sensor_data sensor_data; int res; int server_ch; char warning_msg[256] = { 0 }; size_t len = 0; /* channel messages to send and return back */ struct rt_channel_msg ch_msg, ch_msg_ret; printf("RiceChen rt-smart second app\n"); /* open the IPC channel created by 'pong' */ server_ch = rt_channel_open("server", 0); if (server_ch == -1) { printf("Error: rt_channel_open: could not find the \'server\' channel!\n"); return -1; } ap3216c_dev = rt_device_find(SENSOR_NAME); if (ap3216c_dev == RT_NULL) { rt_kprintf("Can't find device:%s", SENSOR_NAME); return -1; } if (rt_device_open(ap3216c_dev, RT_DEVICE_FLAG_RDWR) != RT_EOK) { rt_kprintf("open device failed!"); return -1; } rt_device_control(ap3216c_dev, RT_SENSOR_CTRL_SET_ODR, (void *)100); while(1) { res = rt_device_read(ap3216c_dev, 0, &sensor_data, 1); //读取传感器数值 if (res != 1) { rt_kprintf("read data failed!size is %d\n", res); } else { rt_kprintf("light:%5d lux, timestamp:%5d\n", sensor_data.light, sensor_data.timestamp); } if(sensor_data.light > 50) //判断阈值 { ch_msg.type = RT_CHANNEL_RAW; snprintf(warning_msg, 255, "%s", "warning"); len = strlen(warning_msg) + 1; warning_msg[len] = '\0'; int shmid = prepare_data(warning_msg, len); if (shmid < 0) { printf("clent: fail to prepare the clent message.\n"); continue; } ch_msg.u.d = (void *)shmid; rt_channel_send_recv(server_ch, &ch_msg, &ch_msg_ret); //发送警报信息 lwp_shmrm(shmid); } rt_thread_mdelay(2000); } rt_device_close(ap3216c_dev); rt_channel_close(server_ch); return 0; } ``` ### 演示 - 演示请看如下链接:https://mp.weixin.qq.com/s/nmO_0LH2pztHW4MK3ZLK6A
关注微信公众号『Rice嵌入式开发技术分享』,后台回复“微信”添加作者微信,备注”入群“,便可邀请进入技术交流群。 ![](https://oss-club.rt-thread.org/uploads/20220714/42b25f77e3631ff00e2d436b621507a6f6a58d3a.jpg)
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
RiceChen
内核/软件架构/开源
文章
4
回答
27
被采纳
4
关注TA
发私信
相关文章
1
RT-THREAD在STM32H747平台上移植lwip
2
正点原子miniSTM32开发板读写sdcard
3
反馈rtt串口驱动对低功耗串口lpuart1不兼容的问题
4
Keil MDK 移植 RT-Thread Nano
5
RT1061/1052 带 RTT + LWIP和LPSPI,有什么坑要注意吗?
6
RT thread HID 如何收发数据
7
求一份基于RTT系统封装好的STM32F1系列的FLASH操作程序
8
RT-Thread修改项目名称之后不能下载
9
rt-studio编译c++
10
有木有移植rt-thread(nano)到riscv 32位MCU上
推荐文章
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
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
rt_mq_消息队列_msg_queue
keil_MDK
ulog
MicroPython
C++_cpp
本月问答贡献
a1012112796
20
个答案
3
次被采纳
张世争
11
个答案
3
次被采纳
踩姑娘的小蘑菇
7
个答案
3
次被采纳
rv666
9
个答案
2
次被采纳
用户名由3_15位
13
个答案
1
次被采纳
本月文章贡献
程序员阿伟
9
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
RTT_逍遥
1
篇文章
6
次点赞
大龄码农
1
篇文章
5
次点赞
ThinkCode
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部