
设备驱动
congrats-女方嫁女对联大全
2023年3月18日发(作者:色彩绘画)RT-ThreadIO设备模型及驱动框架解析(⼀)
⽬录
1.概述
本着由简⼊繁的原则,分析源码以STM32平台的看门狗源码为例,正好参考官⽅资料辅助学习下。
硬件平台及软件版本如下:
硬件平台:STM32F407ZG
RT-Thread版本:4.0.4
在分析源码前需要了解的基础知识如下:
⾃动初始化机制
I/O驱动模型
看门狗驱动框架
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。
看门狗设备的结构体定义如下,该看门狗设备采⽤静态初始化的⽅法,定义了看门狗设备对象及看门狗设备的操作⽅法。
其中使⽤了看门狗驱动框架的看门狗相关的结构体定义,后⾯在说。硬件结构体,就是stm32官⽅的定义,这个可以去看官⽅驱动⽰例。那
么结合结构体的定义,看门狗的初始化如下,主要是配置硬件参数,然后向驱动框架中注册该设备,名字即为“wdt”。
1structstm32_wdt_obj
2{
3//看门狗设备定义
4rt_watchdog_twatchdog;
5//看门狗的硬件结构体定义
6IWDG_HandleTypeDefhiwdg;
7//是否初始化的标志
8rt_uint16_tis_start;
9};
10//看门狗实例
11staticstructstm32_wdt_objstm32_wdt;
12//看门狗的操作⽅法
13staticstructrt_watchdog_opsops;
此处看门狗设备的创建采⽤⾃动初始化机制,按照⾃动初始化机制描述,该初始化是板级初始化。初始化相关宏如下:
接下来再看下,看门狗设备定义的驱动功能,就两个,⼀个是看门狗的初始化,另⼀个是看门狗的各种功能控制(喂狗、设置超时时间
等)。此处的初始化是⼀个空函数,看门狗的初始化是通过
结合RT_DEVICE_CTRL_WDT_START命令来实现的。
1intrt_wdt_init(void)
2{
3//看门狗硬件参数配置
4#ifdefined(SOC_SERIES_STM32H7)
5stm32_ce=IWDG1;
6#else
7stm32_ce=IWDG;
8#endif
9stm32_ler=IWDG_PRESCALER_256;
10
11stm32_=0x00000FFF;
12#ifdefined(SOC_SERIES_STM32F0)||defined(SOC_SERIES_STM32L4)||defined(SOC_SERIES_STM32F7)
13||defined(SOC_SERIES_STM32H7)||defined(SOC_SERIES_STM32L0)
14stm32_=0x00000FFF;
15#endif
16stm32__start=0;
17
18//操作⽅法的绑定
=&wdt_init;
l=&wdt_control;
21stm32_=&ops;
22
23//向驱动框架中注册该设备
24if(rt_hw_watchdog_register(&stm32_og,"wdt",RT_DEVICE_FLAG_DEACTIVATE,RT_NULL)!=RT_EOK)
25{
26LOG_E("wdtdeviceregisterfailed.");
27return-RT_ERROR;
28}
29LOG_D("wdtdeviceregistersuccess.");
30returnRT_EOK;
31}
32INIT_BOARD_EXPORT(rt_wdt_init);
staticrt_err_twdt_control(rt_watchdog_t*wdt,intcmd,void*arg)
1staticrt_err_twdt_init(rt_watchdog_t*wdt)
2{
3returnRT_EOK;
3returnRT_EOK;
4}
5
6staticrt_err_twdt_control(rt_watchdog_t*wdt,intcmd,void*arg)
7{
8switch(cmd)
9{
10/*feedthewatchdog*/
11caseRT_DEVICE_CTRL_WDT_KEEPALIVE:
12if(HAL_IWDG_Refresh(&stm32_)!=HAL_OK)
13{
14LOG_E("watchdogkeepalivefail.");
15}
16break;
17/*setwatchdogtimeout*/
18caseRT_DEVICE_CTRL_WDT_SET_TIMEOUT:
19#ifdefined(LSI_VALUE)
20if(LSI_VALUE)
21{
22stm32_=(*((rt_uint32_t*)arg))*LSI_VALUE/256;
23}
24else
25{
26LOG_E("PleasedefinethevalueofLSI_VALUE!");
27}
28if(stm32_>0xFFF)
29{
30LOG_E("wdgsettimeoutparametertoolarge,pleaselessthan%ds",0xFFF*256/LSI_VALUE);
31return-RT_EINVAL;
32}
33#else
34#error"PleasedefinethevalueofLSI_VALUE!"
35#endif
36if(stm32__start)
37{
38if(HAL_IWDG_Init(&stm32_)!=HAL_OK)
39{
40LOG_E("wdgsettimeoutfailed.");
41return-RT_ERROR;
42}
43}
44break;
45caseRT_DEVICE_CTRL_WDT_GET_TIMEOUT:
46#ifdefined(LSI_VALUE)
47if(LSI_VALUE)
48{
49(*((rt_uint32_t*)arg))=stm32_*256/LSI_VALUE;
50}
51else
52{
53LOG_E("PleasedefinethevalueofLSI_VALUE!");
54}
55#else
56#error"PleasedefinethevalueofLSI_VALUE!"
57#endif
58break;
59caseRT_DEVICE_CTRL_WDT_START:
60if(HAL_IWDG_Init(&stm32_)!=HAL_OK)
61{
62LOG_E("wdtstartfailed.");
63return-RT_ERROR;
64}
65stm32__start=1;
66break;
67default:
68LOG_W("Thiscommandisnotsupported.");
那么通过以上的源码就实现了硬件层⾯的看门狗的创建,或者说参数配置及功能实现。
接下来就是注册看门狗设备到看门狗设备框架了。
3.2.注册到驱动框架
上⼀⼩节的的看门狗初始化中调⽤了注册函数,该函数即实现了注册到驱动框架的功能。
看门狗的驱动框架实现见watchdog.c,注册函数的实现如下:
此处,就是把看门狗设备的类型、回调函数、操作⽅法设置或绑定好,由于看门狗设备操作简单,此处的回调直接为空。
其中RT_USING_DEVICE_OPS宏,个⼈理解是为了兼容不同版本的问题,除了ram和rom使⽤⼤⼩有区别,其他没有什么影响。
接下来看下看门狗框架中的设备操作⽅法的实现,按照官⽅的I/O设备模型需要实现如下操作⽅法。
68LOG_W("Thiscommandisnotsupported.");
69return-RT_ERROR;
70}
71returnRT_EOK;
72}
1if(rt_hw_watchdog_register(&stm32_og,"wdt",RT_DEVICE_FLAG_DEACTIVATE,RT_NULL)!=RT_EOK)
2{
3LOG_E("wdtdeviceregisterfailed.");
4return-RT_ERROR;
5}
1rt_err_trt_hw_watchdog_register(structrt_watchdog_device*wtd,
2constchar*name,
3rt_uint32_tflag,
4void*data)
5{
6structrt_device*device;
7RT_ASSERT(wtd!=RT_NULL);
8
9device=&(wtd->parent);
10
11device->type=RT_Device_Class_Security;
12//回调处理
13device->rx_indicate=RT_NULL;
14device->tx_complete=RT_NULL;
15
16//看门狗设备的操作⽅法
17#ifdefRT_USING_DEVICE_OPS
18device->ops=&wdt_ops;
19#else
20device->init=rt_watchdog_init;
21device->open=rt_watchdog_open;
22device->close=rt_watchdog_close;
23device->read=RT_NULL;
24device->write=RT_NULL;
25device->control=rt_watchdog_control;
26#endif
27device->user_data=data;
28
29/*registeracharacterdevice*/
30returnrt_device_register(device,name,flag);
31}
在看门狗设备中,由于不需要使⽤读写功能,则只定义了初始化、打开、关闭、控制的函数。如下的操作⽅法实现,最终都是调⽤上⼀⼩
结中驱动功能的初始化、功能控制来实现的。
通过以上源码,就实现了看门狗设备注册到驱动框架中,接下来就是把它注册到IO设备管理器中。
3.3.注册到IO设备管理器
注册到IO设备管理器的函数如下,在⽂件device.c中。
函数实现如下:
1staticrt_err_trt_watchdog_init(structrt_device*dev)
2{
3rt_watchdog_t*wtd;
4
5RT_ASSERT(dev!=RT_NULL);
6wtd=(rt_watchdog_t*)dev;
7if(wtd->ops->init)
8{
9return(wtd->ops->init(wtd));
10}
11
12return(-RT_ENOSYS);
13}
14
15staticrt_err_trt_watchdog_open(structrt_device*dev,rt_uint16_toflag)
16{
17return(RT_EOK);
18}
19
20staticrt_err_trt_watchdog_close(structrt_device*dev)
21{
22rt_watchdog_t*wtd;
23
24RT_ASSERT(dev!=RT_NULL);
25wtd=(rt_watchdog_t*)dev;
26
27if(wtd->ops->control(wtd,RT_DEVICE_CTRL_WDT_STOP,RT_NULL)!=RT_EOK)
28{
29rt_kprintf("Thiswatchdogcannotbestopedn");
30
31return(-RT_ERROR);
32}
33
34return(RT_EOK);
35}
36
37staticrt_err_trt_watchdog_control(structrt_device*dev,
38intcmd,
39void*args)
40{
41rt_watchdog_t*wtd;
42
43RT_ASSERT(dev!=RT_NULL);
44wtd=(rt_watchdog_t*)dev;
45
46return(wtd->ops->control(wtd,cmd,args));
47}
rt_device_register(device,name,flag);
到此为⽌,看门狗设备就被注册到IO设备管理器中了,可以使⽤IO设备管理接⼝操作看门狗设备了。
4.⼩结
总体来看,要明⽩这套驱动框架及模式,⾸先要理解整体流程,其次是RT-Thread的设备对象,所有的设备都是通过设备基类派⽣出来的,
或者⽤结构体的⽅式理解就是具体的设备类结构体包含了设备基类的结构体。
1rt_err_trt_device_register(rt_device_tdev,
2constchar*name,
3rt_uint16_tflags)
4{
5if(dev==RT_NULL)
6return-RT_ERROR;
7
8if(rt_device_find(name)!=RT_NULL)
9return-RT_ERROR;
10
11rt_object_init(&(dev->parent),RT_Object_Class_Device,name);
12dev->flag=flags;
13dev->ref_count=0;
14dev->open_flag=0;
15
16#ifdefRT_USING_POSIX
17dev->fops=RT_NULL;
18rt_wqueue_init(&(dev->wait_queue));
19#endif/*RT_USING_POSIX*/
20
21returnRT_EOK;
22}
23RTM_EXPORT(rt_device_register);
⽤官⽅的话讲:“RT-Thread的设备模型是建⽴在内核对象模型基础之上的,设备被认为是⼀类对象,被纳⼊对象管理器的范畴。每个设
备对象都是由基对象派⽣⽽来,每个具体设备都可以继承其⽗类对象的属性,并派⽣出其私有属性。”
设备对象具体的定义如下
通过以上描述,⼀系列的注册过程就是把硬件驱动的参数设置,功能函数绑定到具体的设备实例上,然后通过设备名字找到该设备实例,
接着通过操作接⼝完成各种功能的使⽤。
PS:
第⼀次这么认真的写了如此篇幅的博客,只为积累⼀些学习知识点,如果有描述错误的地⽅希望指摘,定虚⼼学习。