Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RT-Thread一般讨论
设备驱动注册流程(YOC)
发布于 2023-08-17 17:21:07 浏览:394
订阅该版
[tocm] ## 设备注册流程 #### 以IIC设备注册流程为例 1. ##### 封装设备注册函数 ```c void rvm_iic_drv_register(int idx) { /* 设备ops 配置 设备ID */ rvm_driver_register(&iic_driver.drv, NULL, idx); } ``` ###### 设备ops: ```c static iic_driver_t iic_driver = { .drv = { .name = "iic", .init = iic_csky_init, .uninit = iic_csky_uninit, .open = iic_csky_open, .close = iic_csky_close, .clk_en = iic_csky_clock }, .config = iic_csky_config, .master_send = iic_csky_master_send, .master_recv = iic_csky_master_recv, .slave_send = iic_csky_slave_send, .slave_recv = iic_csky_slave_recv, .mem_write = iic_csky_mem_write, .mem_read = iic_csky_mem_read, .trans_dma_enable = iic_csky_trans_dma_enable }; ``` 到这就可以通过YOC的设备框架调用设备的驱动。但是到这具体怎么个框架貌似还不清除。 ##### 设备注册过程 ```c int rvm_driver_register(driver_t *drv, void *config, int idx) { aos_check_param(drv && drv->init); /* name exist check */ if (rvm_hal_device_find(drv->name, idx) != NULL) // 查找设备名称 设备存在则不重复注册 return 0; .... ``` 这里会查找设备是否已经注册,防止重复初始化。 >
rvm_hal_device_find(drv->name, idx)
第一个参数:设备名称:例如 i2c 第二个参数:设备ID:例如i2c1,则idx为1 ```c rvm_dev_t *rvm_hal_device_find(const char *name, int id) { rvm_dev_t *node; LIST_LOCK(); slist_for_each_entry(&device_list, node, rvm_dev_t, node) { driver_t *drv = (driver_t *)node->drv; aos_assert(drv); if (strcmp(drv->name, name) == 0 && node->id == id) break; } LIST_UNLOCK(); return node; } ``` 这个参数的关键在于中间的查询部分。
device_list
,是一个全局链表用于挂载设备。在文件的开始处定义:
static slist_t device_list;
```c typedef struct slist_s { struct slist_s *next; } slist_t; static slist_t device_list; ``` 上述函数将节点的地址传入。 ```scheme #define slist_for_each_entry(queue, node, type, member) \ for (node = (queue)->next? aos_container_of((queue)->next, type, member) : NULL; \ node; \ node = node->member.next ? aos_container_of(node->member.next, type, member) : NULL) ```
rvm_hal_device_find
函数的关键部分就是上述代码。 第一个参数:是设备的初始节点,其他设备依次挂载在该节点上。 第二个参数:临时节点,用于缓存设备节点数据 第三个参数:包含设备节点数据结构。 第四个参数:设备成员。
slist_for_each_entry
的关键部分是
aos_container_of
,其实到这就一部分就很熟悉了,进去分析一下该函数; ```scss #define aos_container_of(ptr, type, member) \ ((type *) ((char *) (ptr) - aos_offsetof(type, member))) ``` ```scala #define aos_offsetof(type, member) ((size_t)&(((type *)0)->member)) ``` 将上述的代码合并一下: ```apl #define aos_container_of(ptr, type, member) \ ((type *) ((char *) (ptr) - ((size_t)&(((type *)0)->member)) ``` 这部分代码的含义是; 用待查询的成员的地址-将0地址转换为type类型指向其成员,得出包含查询成员的数据类型的指针。、 值得注意的是指针的特殊之处在于其处于一些特殊的位置,可以进行一些”不会强调的工作“。比如 node成员处于结构体的首位,在此处的成员必定”大有可为“. 回过头来在看一下一开始要分析的函数: ```c rvm_dev_t *rvm_hal_device_find(const char *name, int id) { rvm_dev_t *node; LIST_LOCK(); slist_for_each_entry(&device_list, node, rvm_dev_t, node) { driver_t *drv = (driver_t *)node->drv; aos_assert(drv); if (strcmp(drv->name, name) == 0 && node->id == id) break; } LIST_UNLOCK(); return node; } ``` 在继续分析该函数,必须先介绍一下
rvm_dev_t
这个数据结构: ```c typedef struct _dev_obj { slist_t node; uint8_t id; uint8_t busy; uint16_t ref; aos_mutex_t mutex; driver_t *drv; void *config; #if defined(AOS_COMP_DEVFS) && AOS_COMP_DEVFS devfs_node_t devfs_node; #endif } rvm_dev_t; ``` node成员位于结构体首位; ```scheme rvm_dev_t *node; ... (driver_t *)node->drv; ``` node本身就是rvm_dev_t类型,指向其drv成员并将其强制转化为driver_t *类型,就可以找到每个设备的设备句柄. 设备句柄包含像设备驱动框架层传递的控制函数以及设备本身的一些信息. ```scheme aos_assert(drv); if (strcmp(drv->name, name) == 0 && node->id == id) break; ``` 上述函数的释义是: 比较传递进来的设备名称与查找到的设备名称是否一致,设备的id号是否一致.若一致则将该设备的设备地址返回. ```c int rvm_driver_register(driver_t *drv, void *config, int idx) { aos_check_param(drv && drv->init); /* name exist check */ if (rvm_hal_device_find(drv->name, idx) != NULL) // 查找设备名称 设备存在则不重复注册 return 0; .... ``` 回到故事的开头,这里查找到该设备是存在的,就直接退出,不再次初始化.(这里只做查找工作,具体初始化工作在下文) 继续分析. ```c rvm_dev_t *dev = drv->init(drv, config, idx); // 设备初始化 ``` 实际执行下述函数; ```c static rvm_dev_t *iic_csky_init(driver_t *drv, void *config, int id) { iic_dev_t *iic = (iic_dev_t *)rvm_hal_device_new(drv, sizeof(iic_dev_t), id); return (rvm_dev_t *)iic; } ``` 继续分析 ```c iic_dev_t *iic = (iic_dev_t *)rvm_hal_device_new(drv, sizeof(iic_dev_t), id); ... rvm_dev_t *rvm_hal_device_new(driver_t *drv, int size, int id) { rvm_dev_t *dev = (rvm_dev_t*)aos_zalloc(size);/* 分配内存 */ if (dev) { dev->id = id; /* 初始化设备ID */ dev->drv = drv;/* 初始化设备驱动 */ if (aos_mutex_new(&dev->mutex) != 0) { aos_free(dev); return NULL; } } return dev; } ``` 执行设备初始化函数,至此设备驱动层(不包含硬件 相当于ops层 )已经初始化. ```scheme if (dev) { dev->id = idx;// 初始化设备ID ((driver_t *)(dev->drv))->device_id = alloc_device_id(); LIST_LOCK();// 获取互斥锁 slist_add_tail(&dev->node, &device_list); LIST_UNLOCK();// 释放 return 0; } ``` ```c dev->id = idx;// 初始化设备ID ``` 此处的ID就是硬件ID. ```scheme ((driver_t *)(dev->drv))->device_id = alloc_device_id(); ``` 分配设备ID. ```c alloc_device_id(); static int alloc_device_id() { uint32_t index = rand() & 0xFFFF; LIST_LOCK(); while (1) { index++; int found = 0; rvm_dev_t *node; slist_for_each_entry(&device_list, node, rvm_dev_t, node) { driver_t *drv = (driver_t *)node->drv; aos_check_param(drv); if (drv->device_id == index << 16) { found = 1; break; } } if (found == 0) break; } LIST_UNLOCK(); return index << 16; } ``` 分析上述函数: ```c slist_for_each_entry(&device_list, node, rvm_dev_t, node) { driver_t *drv = (driver_t *)node->drv; aos_check_param(drv); if (drv->device_id == index << 16) { found = 1; break; } } ``` ##### 这部分的作用是分配一个具体的设备ID.不必深究. 一下是特别关键的一步. ```scheme slist_add_tail(&dev->node, &device_list); ``` ```c void slist_add_tail(slist_t *node, slist_t *head) { while (head->next) { head = head->next; } slist_add(node, head); } ``` ```c static inline void slist_add(slist_t *node, slist_t *head) { node->next = head->next; head->next = node; } ``` ##### 设备打开过程 ```scss rvm_hal_iic_open(filename); ``` 该函数用于打开一个设备 ,filename为具体的设备名称.s ```scss #define rvm_hal_iic_open(name) rvm_hal_device_open(name) ``` ```c rvm_dev_t *rvm_hal_device_open(const char *name) { int id = 0; char *n = dev_name_parse(name, &id); /* 获取设备id */ if (n != NULL) { rvm_dev_t *dev = device_open_id(n, id); aos_free(n); return dev; } return NULL; } ``` 当获取到设备ID后 ```c# device_open_id(n, id); ``` ```c rvm_dev_t *device_open_id(const char *name, int id) { rvm_dev_t *dev = rvm_hal_device_find(name, id); if (dev) { device_lock(dev); if (dev->ref == 0) { if (DRIVER(dev)->open(dev) != 0) { device_unlock(dev); return NULL; } } dev->ref++; device_unlock(dev); } return dev; } ``` 执行下述代码 ```c DRIVER(dev)->open(dev) ``` ```c static int iic_csky_open(rvm_dev_t *dev) { csi_error_t ret = csi_iic_init(&IIC(dev)->handle, dev->id); if (ret != CSI_OK) { LOGE(TAG, "csi_iic_init error"); return -1; } aos_event_new(&IIC(dev)->event, 0); IIC(dev)->dma_en = 0; return 0; } ``` 进行IIC设备初始化,创建事件. 至此设备的注册与初始化就彻底完成.就可以调用其提供的设备接口. 例如: ```c int rvm_hal_iic_master_send(rvm_dev_t *dev, uint16_t dev_addr, const void *data, uint32_t size, uint32_t timeout) { if (size == 0 || NULL == data) { return -EINVAL; } int ret; IIC_VAILD(dev); device_lock(dev); ret = IIC_DRIVER(dev)->master_send(dev, dev_addr, data, size, timeout); device_unlock(dev); return ret; } ``` 分析这个函数,必须要看一个宏; ```scss #define IIC_DRIVER(dev) ((iic_driver_t*)(dev->drv)) ``` 这个宏将设备强制转换为 iic_driver_t* 类型.驱动在注册阶段已经分配内存并初始化. ```c static int iic_csky_master_send(rvm_dev_t *dev, uint16_t dev_addr, const void *data, uint32_t size, uint32_t timeout) { unsigned int flags = 0; csi_error_t ret = csi_iic_master_send_async(&IIC(dev)->handle, dev_addr, data, size); if (ret < 0) { LOGE(TAG, "iic send fail"); return -1; } aos_event_get(&IIC(dev)->event, EVT_SEND_DONE, AOS_EVENT_OR_CLEAR, &flags, timeout); if (flags & EVT_SEND_DONE) { return 0; } else { return -1; } return -ETIMEDOUT; } ``` 最终调用这个函数进行发送. 归纳; 注册方法: 执行设备注册与打开后,我们就可以使用具体设备的函数,比如IIC设备,使用其具体的发送,接收,配置参数等API. 当具体到实际某一个具有某设备接口的传感器时,在设置其结构体时将IIC设备的句柄带上就可找到具体的IIC设备了.
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
药RV
调网络不抓包,调I2C等时序不上逻辑分析仪,就像电工不用万用表!多用整理的好的文字,比截图更省流量,还能在整理过程中思考。
文章
12
回答
14
被采纳
0
关注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
机器人操作系统 (ROS2) 和 RT-Thread 通信
4
国产MCU移植系列教程汇总,欢迎查看!
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
串口
LWIP
Env
SPI
Bootloader
AT
ART-Pi
Hardfault
CAN总线
FinSH
USB
文件系统
DMA
RT-Thread
SCons
线程
RT-Thread Nano
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
ota在线升级
WIZnet_W5500
I2C_IIC
UART
flash
cubemx
packages_软件包
freemodbus
潘多拉开发板_Pandora
PWM
定时器
ADC
BSP
中断
编译报错
socket
flashDB
keil_MDK
GD32
Debug
MicroPython
msh
ulog
SFUD
SDIO总线
rt_mq_消息队列_msg_queue
本月问答贡献
出出啊
1506
个答案
339
次被采纳
小小李sunny
1423
个答案
281
次被采纳
张世争
740
个答案
161
次被采纳
crystal266
539
个答案
157
次被采纳
whj467467222
1218
个答案
146
次被采纳
本月文章贡献
出出啊
1
篇文章
5
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
3
篇文章
1
次点赞
crystal266
2
篇文章
1
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部