一、设备树
1、设备树概述
设备树是搭配驱动使用,记录硬件信息的一种文件形式,常见的设备树文件类型如下
- .dts文件:设备树源代码
- .dtsi文件:用于存放可以被多个dts文件共享的公共硬件描述部分,类似于C语言中的头文件
- DTC:设备树的编译工具
- .dtb文件:编译成二进制的设备树文件
2、设备树的语法规则
设备树的编写格式如下
节点{
属性1;
属性2 = 整形值;
属性3 = 字符值;
属性4 = 数组;
属性5 = &节点名;
};
下面以led设备树为例,介绍设备树的基本语法结构
puteled {
#address-cells = <1>;
#size-cells = <1>;
compatible = "pute-led";
reg = <0x20E0068 0x4
0x20E02F4 0x4
... ...
>;
status = "okay";
};
puteled:节点名
#address-cells:该节点下的reg属性中的地址占1个字(4个字节)
#size-cells:该节点下的reg属性中的地址大小占1个字(4个字节)
compatible:将来用于总线驱动中设备与驱动的匹配
reg:寄存器地址和寄存器地址的大小
status:使能该节点 okay disable关闭该节点
3、设备树子系统
pinctrl子系统
主要功能:主要用于设置引脚的复用功能
在相应设备树文件iomuxc节点中定义controller引脚配置:
pinctrl_putekey: putekey {
fsl,pins = <MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 0xf0b0>
};
其中
MX6UL_PAD_UART1_CTS_B__GPIO1_IO18为 IO 复用配置
0xf0b0为引脚电器属性
在对应的 putekey 设备树节点中添加如下语句
pinctrl-names = "default";
定义设备的状态名称,其中default表示设备在正常工作时的默认状态
pinctrl-0 = <&pinctrl_putekey>;
pinctrl-0属性指定了第0个状态(即default状态)所对应的引脚配置<&pinctrl_putekey>表示引用 pinctrl 中的 pinctrl_putekey 节点
二、驱动代码实现
实现按键中断的相关代码如下
1.设备、中断相关操作
1.1 在 misc 设备中注册按键设备
cpp
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = key_read,
};
static struct miscdevice misc_key = {
.minor = MISC_DYNAMIC_MINOR,
.name = "misc_key",
.fops = &fops,
};
int ret = 0;
init_waitqueue_head(&wq);
ret = misc_register(&misc_key);
if (ret) {
pr_info("misc_register failed\n");
return -1;
}
1.2 查找设备树节点、获取GPIO编号
cpp
pkeynode = of_find_node_by_path("/putekey");
if (NULL == pkeynode) {
pr_info("of_find_node_by_path failed\n");
return -1;
}
//从设备树节点属性中获取GPIO编号
gpiokey = of_get_named_gpio(pkeynode, "gpio-key", 0);
if (gpiokey < 0) {
pr_info("of_get_named_gpio failed\n");
return -1;
}
1.3申请、配置GPIO
cpp
//申请使用GPIO引脚
ret = gpio_request(gpiokey, "pute-key-gpio");
if (ret) {
pr_info("gpio_request failed\n");
return -1;
}
//配置GPIO输入/输出方向
ret = gpio_direction_input(gpiokey);
if (ret) {
pr_info("gpio_direction_input failed\n");
return -1;
}
1.4 获取、注册中断号
cpp
//将GPIO软件编号映射为中断号
#if 0
irqno = gpio_to_irq(gpiokey);
if (irqno < 0) {
pr_info("gpio_to_irq failed\n");
return -1;
}
#endif
//从设备树节点解析获取软件逻辑中断号
irqno = of_irq_get(pkeynode, 0);
if (irqno < 0) {
pr_info("of_irq_get failed\n");
return -1;
}
//注册中断号与中断处理函数
ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING, "pute-irq-key", NULL);
if (ret) {
pr_info("request_irq failed\n");
return -1;
}
2、驱动端功能代码
cpp
static ssize_t key_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{
unsigned long nret = 0;
condition = 0;
status = 0;
wait_event_interruptible(wq, condition); //阻塞等待
nret = copy_to_user(puser, &status, sizeof(status));//驱动与应用层交互,传递按键状态信息
if (nret) {
pr_info("copy_to_user failed\n");
return -1;
}
return nret;
}
static irqreturn_t key_irq_handler(int irqno, void *pdev)
{
condition = 1;
status = 1;
wake_up_interruptible(&wq); //检测到按键信号唤醒
return IRQ_HANDLED;
}
3、应用层代码
cpp
int main(void)
{
int fd = 0;
int stat = 0;
fd = open("/dev/misc_key", O_RDWR);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
while (1)
{
//当按键被按下时,获取到stat信息
read(fd, &stat, sizeof(stat));
printf("stat = %d\n", stat);
}
close(fd);
return 0;
}