Linux驱动开发(3)——设备树
一、驱动和设备分离
设备树可以统一管理设备,存放所有设备信息。
1、设备树文件类型:
- dts:设备树源文件
- dtsi:设备树头文件
- dtb:编译好的二进制设备树文件,可直接使用
- DTC:编译工具
2、设备树节点语法:
设备名{ #address-cells = <1>;//每个地址所占字节 #size-cells = <1>;//地址大小所占字节 // 寄存器物理地址 + 长度 可以写多组 reg = <地址1 长度1 地址2 长度2 地址3 长度3>; status = "okey";//设备可用状态 }3、设备树函数接口:
1).函数:struct device_node *of_find_node_by_path(const char *path);
- 功能:通过路径获得设备树节点
- 参数:@path --- 设备树节点全路径(比如'/myled')
2).函数:intof_property_read_string(struct device_node *np, const char *propname, const char **out_string);
- 功能:读取属性中的字符串值
- 参数:@np --- 设备树节点,@propname --- 要读的属性名,@out_string --- 存放缓冲区
3).函数:void __iomem *of_iomap(struct device_node *np, int index);
- 功能:设备树中的物理地址映射为内核虚拟地址
- 参数:@np --- 设备树节点,@index --- reg数组的下标,从0开始)
二、驱动分层:
设备有4 类寄存器:
- 引脚复用
- 电气属性
- GPIO 方向
- GPIO 数据
将寄存器的配置移入设备树(Device Tree),由 Linux 内核的pinctrl 子系统和GPIO 子系统完成。
1、pinctrl子系统:操作iomuxrc的寄存器
1).pinctrl方案定义
pinctrl_myled:myledgrp{//方案名:组名 fsl,pins = < //引脚配置宏 电器属性参数 >; }2).pinctrl方案使用
设备树节点{ pinctrl-names = "default";//默认按照pinctrl方案定义配置 pinctrl-0 = <&pinctrl方案名>; };2、GPIO子系统:操作GPIO的寄存器
设备树节点{ gpio-led = <&gpio控制器编号 gpio引脚号 有效电平>; };1).函数:intof_get_named_gpio(struct device_node *np, const char *propname, int index);
- 功能:从设备树节点中解析并获取指定GPIO硬件编号
- 参数:@np --- 设备树节点,@propname --- GPIO子系统名,@index --- 方案编号,从0开始
- 返回值:成功返回引脚编号,失败返回-1
2).函数:intdevm_gpio_request(struct device *dev, unsigned gpio, const char *label);
- 功能:或得单个GPIO描述符,设备卸载时自动释放
- 参数:@dev --- 设备,@gpio --- 要占用的引脚编号,@label --- 标明占用资源的驱动
3).函数:int gpio_get_value(unsigned int gpio);
- 功能:读取输入引脚电平
4).函数:void gpio_set_value(unsigned int gpio, int value);
- 功能:设置输出引脚电平
三、Linux中断
Linux 内核有中断子系统,专门管理所有硬件中断,不用我们直接操作寄存器配置中断。中断子系统分为:顶半部 + 底半部。
顶半部:
- 硬件一触发中断立刻执行
- 执行时间必须很短,只做最简单的事:标记中断、读取按键电平
- 不能延时、不能睡眠、不能做耗时操作
底半部:
- 把耗时、复杂的任务挪到底半部执行
- 顶半部快速退出,不占用 CPU,提高系统响应速度
- 常见实现:工作队列、tasklet 等
设备节点{ interrupt-parent = <&gpio1>;//中断控制器 interrupts = <15 IRQ_TYPE_EDGE_FALLING>;//触发方式,例如15下降沿触发 };