RT-Thread I/O设备模型及驱动框架解析(一)
目录
1. 概述
2. 原理解析
3. 源码解析
3.1. 创建设备
3.2. 注册到驱动框架
3.3. 注册到IO设备管理器
4. 小结
1. 概述
本着由简入繁的原则,分析源码以STM32平台的看门狗源码为例,正好参考官方资料辅助学习下。
硬件平台及软件版本如下:
硬件平台:STM32F407ZG
RT-Thread版本:4.0.4
在分析源码前需要了解的基础知识如下:
自动初始化机制
RT-Thread 文档中心
I/O驱动模型
RT-Thread 文档中心
看门狗驱动框架
RT-Thread 文档中心
2. 原理解析
通过基础知识的准备,切回到我们的主题。那么在I/O设备模型下,使用watchdog驱动框架如何驱动硬件看门狗呢?
看下官方资料给出的流程图:
通过这张框图明确的流程是
1,创建看门狗设备,并实现底层驱动
2,注册看门狗设备到看门狗设备驱动框架
3,注册I/O设备到I/O设备管理器
4,应用程序使用看门狗
3. 源码解析
原则上说,分析源码是要明确框架才能进一步分析,但是为了方便与简化理解,不妨从设备驱动开始,往上层一步一步追踪来研究源码。
几个关键的RT-Thread的代码目录
设备的驱动代码在
libraries/HAL_Drivers
驱动框架
rt-thread/components/drivers
设备模型
rt-thread/src
3.1. 创建设备
按照上面的流程图理解,首先是要创建看门狗设备,这时看驱动文件drv_wdt.c。
看门狗设备的结构体定义如下,该看门狗设备采用静态初始化的方法,定义了看门狗设备对象及看门狗设备的操作方法。
struct stm32_wdt_obj
{//看门狗设备定义rt_watchdog_t watchdog;//看门狗的硬件结构体定义IWDG_HandleTypeDef hiwdg;//是否初始化的标志rt_uint16_t is_start;
};
//看门狗实例
static struct stm32_wdt_obj stm32_wdt;
//看门狗的操作方法
static struct rt_watchdog_ops ops;
其中使用了看门狗驱动框架的看门狗相关的结构体定义,后面在说。硬件结构体,就是stm32官方的定义,这个可以去看官方驱动示例。那么结合结构体的定义,看门狗的初始化如下,主要是配置硬件参数,然后向驱动框架中注册该设备,名字即为“wdt”。
int rt_wdt_init(void)
{//看门狗硬件参数配置
#if defined(SOC_SERIES_STM32H7)stm32_wdt.hiwdg.Instance = IWDG1;
#elsestm32_wdt.hiwdg.Instance = IWDG;
#endifstm32_wdt.hiwdg.Init.Prescaler = IWDG_PRESCALER_256;stm32_wdt.hiwdg.Init.Reload = 0x00000FFF;
#if defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32F7) \|| defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32L0)stm32_wdt.hiwdg.Init.Window = 0x00000FFF;
#endifstm32_wdt.is_start = 0;//操作方法的绑定ops.init = &wdt_init;ops.control = &wdt_control;stm32_wdt.watchdog.ops = &ops;//向驱动框架中注册该设备if (rt_hw_watchdog_register(&stm32_wdt.watchdog, "wdt", RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK){LOG_E("wdt device register failed.");return -RT_ERROR;}LOG_D("wdt device register success.");return RT_EOK;
}
INIT_BOARD_EXPORT(rt_wdt_init);
此处看门狗设备的创建采用自动初始化机制,按照自动初始化机制描述,该初始化是板级初始化。初始化相关宏如下:
接下来再看下,看门狗设备定义的驱动功能,就两个,一个是看门狗的初始化,另一个是看门狗的各种功能控制(喂狗、设置超时时间等)。此处的初始化是一个空函数,看门狗的初始化是通过
static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg)
结合RT_DEVICE_CTRL_WDT_START命令来实现的。
static rt_err_t wdt_init(rt_watchdog_t *wdt)
{return RT_EOK;
}static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg)
{switch (cmd){/* feed the watchdog */case RT_DEVICE_CTRL_WDT_KEEPALIVE:if(HAL_IWDG_Refresh(&stm32_wdt.hiwdg) != HAL_OK){LOG_E("watch dog keepalive fail.");}break;/* set watchdog timeout */case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
#if defined(LSI_VALUE)if(LSI_VALUE){stm32_wdt.hiwdg.Init.Reload = (*((rt_uint32_t*)arg)) * LSI_VALUE / 256 ;}else{LOG_E("Please define the value of LSI_VALUE!");}if(stm32_wdt.hiwdg.Init.Reload > 0xFFF){LOG_E("wdg set timeout parameter too large, please less than %ds",0xFFF * 256 / LSI_VALUE);return -RT_EINVAL;}
#else#error "Please define the value of LSI_VALUE!"
#endifif(stm32_wdt.is_start){if (HAL_IWDG_Init(&stm32_wdt.hiwdg) != HAL_OK){LOG_E("wdg set timeout failed.");return -RT_ERROR;}}break;case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
#if defined(LSI_VALUE)if(LSI_VALUE){(*((rt_uint32_t*)arg)) = stm32_wdt.hiwdg.Init.Reload * 256 / LSI_VALUE;}else{LOG_E("Please define the value of LSI_VALUE!");}
#else#error "Please define the value of LSI_VALUE!"
#endifbreak;case RT_DEVICE_CTRL_WDT_START:if (HAL_IWDG_Init(&stm32_wdt.hiwdg) != HAL_OK){LOG_E("wdt start failed.");return -RT_ERROR;}stm32_wdt.is_start = 1;break;default:LOG_W("This command is not supported.");return -RT_ERROR;}return RT_EOK;
}
那么通过以上的源码就实现了硬件层面的看门狗的创建,或者说参数配置及功能实现。
接下来就是注册看门狗设备到看门狗设备框架了。
3.2. 注册到驱动框架
上一小节的的看门狗初始化中调用了注册函数,该函数即实现了注册到驱动框架的功能。
if (rt_hw_watchdog_register(&stm32_wdt.watchdog, "wdt", RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK)
{LOG_E("wdt device register failed.");return -RT_ERROR;
}
看门狗的驱动框架实现见watchdog.c,注册函数的实现如下:
rt_err_t rt_hw_watchdog_register(struct rt_watchdog_device *wtd,const char *name,rt_uint32_t flag,void *data)
{struct rt_device *device;RT_ASSERT(wtd != RT_NULL);device = &(wtd->parent);device->type = RT_Device_Class_Security;//回调处理device->rx_indicate = RT_NULL;device->tx_complete = RT_NULL;//看门狗设备的操作方法
#ifdef RT_USING_DEVICE_OPSdevice->ops = &wdt_ops;
#elsedevice->init = rt_watchdog_init;device->open = rt_watchdog_open;device->close = rt_watchdog_close;device->read = RT_NULL;device->write = RT_NULL;device->control = rt_watchdog_control;
#endifdevice->user_data = data;/* register a character device */return rt_device_register(device, name, flag);
}
此处,就是把看门狗设备的类型、回调函数、操作方法设置或绑定好,由于看门狗设备操作简单,此处的回调直接为空。
其中RT_USING_DEVICE_OPS宏,个人理解是为了兼容不同版本的问题,除了ram和rom使用大小有区别,其他没有什么影响。
接下来看下看门狗框架中的设备操作方法的实现,按照官方的I/O设备模型需要实现如下操作方法。
在看门狗设备中,由于不需要使用读写功能,则只定义了初始化、打开、关闭、控制的函数。如下的操作方法实现,最终都是调用上一小结中驱动功能的初始化、功能控制来实现的。
static rt_err_t rt_watchdog_init(struct rt_device *dev)
{rt_watchdog_t *wtd;RT_ASSERT(dev != RT_NULL);wtd = (rt_watchdog_t *)dev;if (wtd->ops->init){return (wtd->ops->init(wtd));}return (-RT_ENOSYS);
}static rt_err_t rt_watchdog_open(struct rt_device *dev, rt_uint16_t oflag)
{return (RT_EOK);
}static rt_err_t rt_watchdog_close(struct rt_device *dev)
{rt_watchdog_t *wtd;RT_ASSERT(dev != RT_NULL);wtd = (rt_watchdog_t *)dev;if (wtd->ops->control(wtd, RT_DEVICE_CTRL_WDT_STOP, RT_NULL) != RT_EOK){rt_kprintf(" This watchdog can not be stoped\n");return (-RT_ERROR);}return (RT_EOK);
}static rt_err_t rt_watchdog_control(struct rt_device *dev,int cmd,void *args)
{rt_watchdog_t *wtd;RT_ASSERT(dev != RT_NULL);wtd = (rt_watchdog_t *)dev;return (wtd->ops->control(wtd, cmd, args));
}
通过以上源码,就实现了看门狗设备注册到驱动框架中,接下来就是把它注册到IO设备管理器中。
3.3. 注册到IO设备管理器
注册到IO设备管理器的函数如下,在文件device.c中。
rt_device_register(device, name, flag);
函数实现如下:
rt_err_t rt_device_register(rt_device_t dev,const char *name,rt_uint16_t flags)
{if (dev == RT_NULL)return -RT_ERROR;if (rt_device_find(name) != RT_NULL)return -RT_ERROR;rt_object_init(&(dev->parent), RT_Object_Class_Device, name);dev->flag = flags;dev->ref_count = 0;dev->open_flag = 0;#ifdef RT_USING_POSIXdev->fops = RT_NULL;rt_wqueue_init(&(dev->wait_queue));
#endif /* RT_USING_POSIX */return RT_EOK;
}
RTM_EXPORT(rt_device_register);
到此为止,看门狗设备就被注册到IO设备管理器中了,可以使用IO设备管理接口操作看门狗设备了。
4. 小结
总体来看,要明白这套驱动框架及模式,首先要理解整体流程,其次是RT-Thread的设备对象,所有的设备都是通过设备基类派生出来的,或者用结构体的方式理解就是具体的设备类结构体包含了设备基类的结构体。
用官方的话讲:“RT-Thread 的设备模型是建立在内核对象模型基础之上的,设备被认为是一类对象,被纳入对象管理器的范畴。每个设备对象都是由基对象派生而来,每个具体设备都可以继承其父类对象的属性,并派生出其私有属性。”
设备对象具体的定义如下
通过以上描述,一系列的注册过程就是把硬件驱动的参数设置,功能函数绑定到具体的设备实例上,然后通过设备名字找到该设备实例,接着通过操作接口完成各种功能的使用。
PS:
第一次这么认真的写了如此篇幅的博客,只为积累一些学习知识点,如果有描述错误的地方希望指摘,定虚心学习。
RT-Thread I/O设备模型及驱动框架解析(一)相关推荐
- linux内核中的 哈希表_Linux内核中的设备模型及SCSI示例解析
关于硬件架构 想要了解Linux操作系统的内核设备和驱动模型,最好先了解一下现在计算机硬件的架构.对计算机硬件有一定了解之后,对理解Linux内核中的设备和驱动模型非常有帮助.如图1是常规计算机的硬件 ...
- RT-Thread | UART设备驱动框架解析
1024G 嵌入式资源大放送!包括但不限于C/C++.单片机.Linux等.关注微信公众号[嵌入式大杂烩],回复1024,即可免费获取! UART简介 STM32 芯片具有多个 USART 外设用于串 ...
- linux3.2.0块设备及nandflash驱动框架
块设备框架:app: open,read,write "1.txt" --------------------------------------------- 文件的读写 文件系 ...
- Linux内核(一) [ IMX RK ] TTY-UART驱动框架解析
平台:NXP imx6ull 内核版本:4.1.15 文章目录 一.Linux TTY驱动框架 二.Linux Uart驱动框架 三.UART相关结构体uart_driver(UART驱动结构体) . ...
- zynq-smc驱动框架解析
要搭建一个以zynq为核心的平台,必须要在zynq外部挂载一个flash设备,通常用到的flash设备主要有nor-flash.nand-flash.qspi-flash等等.对于qspi-flash ...
- Linux驱动框架之v4l2视频驱动框架解析
1.简介 v4l2是专门为linux设备设计的一套视频框架,其主体框架在linux内核,可以理解为是整个 linux 系统上面的视频源捕获驱动框架.其广泛应用在嵌入式设备以及移动端.个人电脑设备上面, ...
- MTK平台TP驱动框架解析
一,TP驱动代码的组成 MTK平台TP驱动主要包括两处文件: 1,kernel-3.18\drivers\input\touchscreen\mediatek\mtk_tpd.c 平台设备驱动,主要为 ...
- linux设备模型:devtmpfs虚拟文件系统分析
devtmpfs是一个设备文件系统,它将其所有文件保存在虚拟内存中. devtmpfs中的所有内容都是临时的,因为不会在您的硬盘驱动器上创建任何文件.如果卸载devtmpfs实例,其中存储的所有内容都 ...
- linux设备模型bus,device,driver,(kobject、ktype、kset,bus_type、device、device_driver)
1.1Linux设备驱动模型简介 1.什么是设备驱动模型 (1)类class.总线bus(负责将设备和驱动挂接起来).设备devices.驱动driver(可以看到在驱动源码中,不管是什么样的驱动,都 ...
最新文章
- lucene字典实现原理——FST
- MySQL 在 LIMIT 条件后注入
- wireshark读写pcap文件_PCAP-file-analysis 利用wireshark捕获tcp ip数据包和pcap文件分析 - 下载 - 搜珍网...
- Python强大的格式化format
- 如何正确刷题计算机考研,2020考研:4个方法教你数学如何正确刷题!
- 受大厂们青睐的Web前端工程师需要掌握的3项能力!
- OpenGL创建多维数据集的多个实例
- 数字图像处理小练习存档1
- 公司 邮件 翻译 培训 长难句 结课
- Flurry 统计(国际版)
- Visual Studio 2015 中文社区版下载
- myql GROU_CONCAT 与FIND_IN_SET查询结果为空问题解决
- Cross-Modality Domain Adaptation
- reset.css normalize.css,normalize与css reset的区别
- 单峰数组求峰值,二分思想,LeetCode862
- Elasticsearch7.6.2 rpm集群部署及异常处理
- GeneXus学习(一)安装与介绍
- 自定义 View 之联系人字母索引及定位效果
- c++语言 幂指数,C++ pow(指数函数):求x的y次幂的值
- 揭开前端绘制地图的神秘面纱
热门文章
- 计算机课教学日志,教师教学日志例文
- matlab电位图仿真实验,基于MATLAB的静电场描绘实验仿真
- 特朗普荣获医学教育奖!2020年搞笑诺贝尔奖出炉,还有有味道的一系列研究......
- 介绍计算机专业说明文,介绍电脑的说明文作文
- Invalid name supplied, making object name syntactically valid. New object name is Seurat..ProjectDim
- 水平集(Level Set)的基本方法
- 科沃斯擦窗机器人擦不干净怎么办_科沃斯窗宝能擦到玻璃的边角么想买个擦窗机器人,就是担心擦不干净,尤其是窗子边角的地方,要是还要人返工,那不如找钟点工来擦了...
- IIS5、IIS6、IIS7的ASP.net 请求处理过程比较转
- JavaWeb项目+MVC三层架构+Mysql+Tomcat+汽车配件销售系统前后端+可以用于学习javaweb项目入门
- C# Winform添加背景图片后加载的时候控件卡