电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
《rt-thread驱动框架分析》-pin驱动
分 享
扫描二维码分享
《rt-thread驱动框架分析》-pin驱动
RTThread
PIN驱动
驱动开发
Rice嵌入式开发
关注
发布时间: 2020-11-02
丨
阅读: 2564
## 简要 - 接下来做一个专辑《rt-thread驱动框架分析》,我会按照自己的理解来描述每一个驱动。有不对的欢迎随时来怼我。 - rt-thread的版本分为两大类,一个是完整版本,一个是nano版本。而驱动框架是相对于完整版本的。所以要了解驱动框架,只能在完整版上了解。 - rt-thread提供了很多驱动框架,比如常见的外设驱动:I2C, SPI等。还有网络相关的WLAN驱动等。 - 驱动框架分析,主要以STM32来分析。 ## 驱动分析 #### API简要说明 - rt-thread的pin驱动为上层应用提供两套不同的API,一套是对接设备驱动框架。一套是封装好的API,用户层可以直接使用。接下来我们来分析一下这两套API的使用,以及实现。 #### pin框架层次 1. 用户访问的方式的接口不同,访问的层次是不一样的。 2. 层次结构如下: ![](https://rice_chen_1.gitee.io/picture/rt-thread专辑/pin框架/1.png) 3. 从上面的图可以看出,对于不同芯片,用户层的接口是统一的,而对于驱动层来说,只需要对接好相应的回调函数。 4. 通过统一的接口,应用开发人不需要知道底层驱动,也减少造轮子的时间。 #### GPIO驱动层 1. 驱动层的任务主要有:①对接底层硬件,②对芯片的GPIO统一编号,③注册下面描述的6个回调函数。 2. 驱动层中,我们特别关注一个结构体rt_pin_ops,如下: ``` C /@@* pin.h */ struct rt_pin_ops { void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_base_t mode); void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_base_t value); int (*pin_read)(struct rt_device *device, rt_base_t pin); /@@* TODO: add GPIO interrupt */ rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_int32_t pin, rt_uint32_t mode, void (*hdr)(void *args), void *args); rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_int32_t pin); rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled); }; ``` 4. 我们只需要对接好这几个6个回调函数就好了,描述如下: | API | 描述 | |-----|------| | pin_mode | 设置引脚模式 | | pin_write | 设置引脚电平 | | pin_read | 读取引脚电平 | | pin_attach_irq | 绑定引脚中断回调函数 | | pin_irq_enable | 使能引脚中断 | | pin_detach_irq | 脱离引脚中断回调函数 | 5. stm32为例,对接相应的OPS结构体,如下: ``` C const static struct rt_pin_ops _stm32_pin_ops = { stm32_pin_mode, stm32_pin_write, stm32_pin_read, stm32_pin_attach_irq, stm32_pin_dettach_irq, stm32_pin_irq_enable, }; ``` 6. 注册pin相应的回调函数,如下: ``` C int rt_hw_pin_init(void) { ...... return rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL); } ``` #### PIN设备驱动层 1. 这一层主要起到承上启下的,为上层应用提供统一的API,为下层驱动,提供注册函数。 2. 其中注册函数如下: ``` C int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data) { _hw_pin.parent.type = RT_Device_Class_Miscellaneous; _hw_pin.parent.rx_indicate = RT_NULL; _hw_pin.parent.tx_complete = RT_NULL; #ifdef RT_USING_DEVICE_OPS _hw_pin.parent.ops = &pin_ops; #else _hw_pin.parent.init = RT_NULL; _hw_pin.parent.open = RT_NULL; _hw_pin.parent.close = RT_NULL; _hw_pin.parent.read = _pin_read; _hw_pin.parent.write = _pin_write; _hw_pin.parent.control = _pin_control; #endif _hw_pin.ops = ops; _hw_pin.parent.user_data = user_data; /@@* register a character device */ rt_device_register(&_hw_pin.parent, name, RT_DEVICE_FLAG_RDWR); return 0; } ``` 3. 前面我们谈到RT-Thread的pin设备提供了两套API,但是最终调用的接口都是执行注册的回调函数。 4. 简单的理解就是两套API,只是不同的访问方式。 #### RTT 为上层应用封装好的API描述 1. 从官方的文档中心中,有详细的描述这套API的使用方法。这套API是直接暴露给用户层调用的。 2. RT-Thread提供的pin设备管理接口来访问GPIO,接口如下: | API | 描述 | |-----|------| | rt_pin_mode() | 设置引脚模式 | | rt_pin_write() | 设置引脚电平 | | rt_pin_read() | 读取引脚电平 | | rt_pin_attach_irq() | 绑定引脚中断回调函数 | | rt_pin_irq_enable() | 使能引脚中断 | | rt_pin_detach_irq() | 脱离引脚中断回调函数 | 3. 该接口访问的层次如下: ![](https://rice_chen_1.gitee.io/picture/rt-thread专辑/pin框架/2.png) 4. 应用,通过点亮一颗灯来描述: ``` C #define LED0_PIN GET_PIN(I, 13) int main(void) { int count = 1; /@@* set LED0 pin mode to output */ rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT); while (count++) { rt_pin_write(LED0_PIN, PIN_HIGH); rt_thread_mdelay(500); rt_pin_write(LED0_PIN, PIN_LOW); rt_thread_mdelay(500); } return RT_EOK; } ``` #### 通过设备驱动框架访问pin设备描述 1. RTT设备驱动框架提供了统一的API: | API | 说明 | |-----|------| | rt_err_t rt_device_init (rt_device_t dev) | 设备初始化 | | rt_err_t rt_device_open (rt_device_t dev, rt_uint16_t oflag) | 打开设备 | | rt_err_t rt_device_close(rt_device_t dev) | 关闭设备 | | rt_size_t rt_device_read (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) | 读设备 | | rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) | 写设备 | | rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg) | 控制设备 | 2. 如果你学习过Linux,你是否听过一句话,一切设备皆文件。在Linux中对设备的访问有如下接口open,read,write,close等,其实RTT提供的设备驱动API也是如此。 3. 该接口访问的层次如下: ![](https://rice_chen_1.gitee.io/picture/rt-thread专辑/pin框架/4.png) 4. 如上图所示,_pin_control()应该包含:GPIO的模式设置,中断关联,中断使能,中断分离。但是从实际的上_pin_control()中,只实现了GPIO的模式设置,如果要使用这组API需要自己增加相关实现: ``` C static rt_err_t _pin_control(rt_device_t dev, int cmd, void *args) { struct rt_device_pin_mode *mode; struct rt_device_pin *pin = (struct rt_device_pin *)dev; /@@* check parameters */ RT_ASSERT(pin != RT_NULL); mode = (struct rt_device_pin_mode *) args; if (mode == RT_NULL) return -RT_ERROR; pin->ops->pin_mode(dev, (rt_base_t)mode->pin, (rt_base_t)mode->mode); return 0; } ``` 5. 应用,通过点亮一颗灯来描述: ``` C #define LED_PIN GET_PIN(I, 13) int main(void) { int count = 1; struct rt_device_pin *pin_dev = RT_NULL; pin_dev = (struct rt_device_pin *)rt_device_find("pin"); rt_device_open((rt_device_t)pin_dev, RT_DEVICE_OFLAG_RDWR); pin_dev->ops->pin_mode(&pin_dev->parent, LED_PIN, PIN_MODE_OUTPUT); while (count++) { pin_dev->ops->pin_write(&pin_dev->parent, LED_PIN, PIN_HIGH); rt_thread_mdelay(1000); pin_dev->ops->pin_write(&pin_dev->parent, LED_PIN, PIN_LOW); rt_thread_mdelay(1000); } return RT_EOK; } ``` ## 总结 - 其实很多人都在讨论说,没有Linux基础,学RTT很痛苦。但是直接学Linux,如果你不去了解内核驱动代码,会少很多乐趣。但是Linux的驱动框架更加复杂,分析更加痛苦。所以作者认为,如果你学了RTT,再去学习Linux,分析驱动框架会更加简单方便。 - 作为RTT的爱好者,我将对RTT驱动框架分析作为一个系列。
关注微信公众号『Rice嵌入式开发技术分享』,后台回复“微信”添加作者微信,备注”入群“,便可邀请进入技术交流群。 ![](https://rice_chen_1.gitee.io/picture/logo/logo_.jpg)
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
Rice嵌入式开发
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交