2024-07-02
GR5526 GUI专题(4) - Lvgl字体的位图数组合并转换为bin文件的说明
写技术文章
精选推荐
cl_eslab
•
name:设备实例名称。为设备树的label属性值,例如以下是gpio0设备树节点,指GPIOA分组IO,注册时设备实例名称为GPIO_0。
•
config:用于驱动配置数据,比如寄存器地址,时钟源或者设备其他物理数据等等。下面为GPIO在驱动注册时,通过驱动模型接口获取相关设备树节点描述,获取寄存器地址,并赋值给config结构体。
• data:可为设备管理指定一个数据结构,比如存放一个锁或者信号,用于API中write/read的阻塞模式实现,等等。
•
api:该结构体将通用子系统api映射到驱动程序中用于设备驱动的实现。驱动api接口在注册时,通过对函数指针赋值,对结构体进行填充,实现通用子系统api。
•
state:表明是否被初始化和是否初始化成功,在进行驱动注册时确定。如下,设备初始化会调用z_sys_init_run_level函数,对驱动设备进行注册,会调用驱动的初始化函数,函数执行结果赋值给state->init_res,同时state->initialized =
true。
4、API系统框架
Zephyr每个驱动实现一个具备通用设备驱动API的子系统,该子系统可以构造多个设备驱动实例,每个实例中的device->api再由具体的底层驱动实现。
以GPIO为例,GPIO设备驱动通用API位于zephyr\include\gpio.h,包含gpio所有对外接口,其中gpio_pin_configure_dt如下,其调用device的api接口pin_configure。
device的api接口pin_configure在zephyr\drivers\gpio\gpio_gr55xx.c中被填充,gpio_gr55xx_config是驱动设备API的具体实现。
5、注册接口
路径zephyr\include\device.h下提供了宏接口,用于设备驱动的注册,并且按照注册的参数自动进行初始化。下面介绍几个常用的宏。
5.1、DEVICE_DEFINE
1)参数说明 DEVICE_DEFINE(dev_name, drv_name, init_fn, pm_action_cb, data_ptr, cfg_ptr, level, prio, api_ptr)
• dev_name - 设备名称,设备树Label属性,用户可通过该名称使用device_get_binding()获取设备
•
drv_name
- 驱动名称
•
inif_fn
- 初始化函数指针
•
pm_action_cb
- 电源管理回调函数指针,如果NULL表明不使用PM
•
data_ptr
- 设备私有数据指针
• cfg_ptr - 该驱动实例的配置数据指针
•
level,prio - 初始化等级,后面会具体介绍
•
api_ptr
- 提供该设备驱动的struct api结构指针
2)函数原型
注册时通过宏定义来声明一个struct dev,并保存至段.z_device_。用户调用device_get_binding()时,就会从段.z_device_通过dev_name遍历到对应的struct dev。同时通过Z_INIT_ENTRY_DEFINE宏,将初始化函数和初始化等级注册进内核,在内核启动时会相应完成自动初始化。
5.2、DEVICE_DT_DEFINE
基本和DEVICE_DEFINE一样的作用,只是传入的参数是节点标识符,dev_name和drv_name是通过节点标识符node_id转换而来。dev_name就是Z_DEVICE_DT_DEV_NAME(node_id),drv_name是DEVICE_DT_NAME(node_id)。
GPIO调用接口进行初始化如下:
6、设备初始化
6.1、注册实现
前面分析过DEVICE_DEFINE()不仅完成设备的注册,同时也完成设备初始化函数以及初始化等级的注册,最终是通过Z_INIT_ENTRY_DEFINE()来实现的,路径zephyr\include\init.h。
下面是具体代码实现,声明结构体struct init_entry,其成员init为注册的初始化函数,dev为该设备结构体指针,将做为初始化函数入参传递。将init_entry放到段.z_init中,同时附带上优先级参数。
6.2、优先级
·
level
内核规定4个优先级
·
prio
用于level等级相同时,该值越小越早初始化,范围为0~99。
6.3、上电初始化
前面知道通过Z_INIT_ENTRY_DEFINE函数将初始化函数和设备驱动结构体放在内存中,当上电初始化时,会在进入z_cstart函数中实现设备初始化,路径zephyr\kernel\init.c,其调用函数z_sys_init_run_level实现,路径zephyr\kernel\device.c,参数为level,函数通过优先级高低进行注册。
7、如何获取设备
7.1、device_get_binding(DT_LABEL(节点标识符))
·
通过设备名称获取struct device *;或者通过DT_LABEL(节点标识符)
·
从段地址_device_start~_device_end之间查询,获取设备名称与查找相同的设备驱动。
7.2、DEVICE_DT_GET(节点标识符)
·
通过节点标识符获取设备指针,该设备是由DEVICE_DT_DEFINE(node_id,
...)注册的。
8、gpio驱动注册
以gpio0子系统来分析完整的设备驱动模型,包括应用层如何使用,底层驱动如何移植适配。
8.1、gpio的应用接口
在include\drivers\gpio.h头文件中,定义了应用层可以调用的API,以及这些API的实现。如下,列出了gpio的API接口,以及调用的设备驱动的接口dev->api。
gpio_pin_configure(dev->api->pin_configure)
gpio_pin_set(api->port_set_bits_raw;api->port_clear_bits_raw)
gpio_pin_get(api->port_get_raw)
gpio_port_set_bits(api->port_set_masked_raw)
gpio_port_clear_bits(api->port_set_masked_raw)
gpio_pin_toggle(api->port_toggle_bits)
gpio_pin_interrupt_configure_dt(api->pin_interrupt_configure)
gpio_add_callback(api->manage_callback)
8.2、gpio设备驱动接口
内核统一规范了GPIO子系统对外的API接口,即应用接口,不同硬件平台只需适配设备驱动接口,即struct gpio_driver_api中的功能即可。
8.3、gpio驱动设备适配和注册
分析zephyr\drivers\gpio\gpio_gr55xx.c代码和dts文件,看下goodix驱动是如何适配到GPIO子系统中
1) 设备树定义如下
GPIO_0为GPIOA分组,寄存器地址0x10010000,地址长度为0x1000。
2) 驱动注册
首先,定义初始化函数,gpio_gr55xx_init。DEVICE_DT_DEFINE()将初始化函数gpio_stm32_init()注册进系统,在内核启动阶段会按优先级自动执行。(一些时钟和电源管理的配置可放在该函数中)
其次,定义dev结构体相关成员,例如设备名称name、config、data和api成员等;这里分别为DT_NODELABEL(gpio##__suffix)、gpio_gr55xx_data_##
__suffix、gpio_gr55xx_cfg_## __suffix、gpio_gr55xx_driver,其中gpio_gr55xx_driver完成底层驱动需适配的功能函数。
最后, DEVICE_DT_DEFINE完成驱动注册,创建init_entry结构体,将初始化函数和设备树驱动dev填充init_entry结构体,并将结构体放到段.z_init中,同时附带上优先级参数。
打开微信,使用“扫一扫”即可关注