设备树下的 LED 驱动实验

news/2025/1/20 16:29:12/文章来源:https://www.cnblogs.com/bathwind/p/18238890

设备树下的 LED 驱动实验

本章实验重点内容如下:
①、在 imx6ull-alientek-emmc.dts 文件中创建相应的设备节点。
②、编写驱动程序(在第四十二章实验基础上完成),获取设备树中的相关属性值。
③、使用获取到的有关属性值来初始化 LED 所使用的 GPIO。

设备树文件添加设备节点:

在根目录下添加这个设备节点,具体如下:

	alphaled {#address-cells = <1>;#size-cells = <1>;compatible = "atkalpha-led";status = "okay";reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */0X0209C000 0X04 /* GPIO1_DR_BASE */0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */};

具体位置如下图所示:
在这里插入图片描述
重新编译设备树文件,并用新的设备树启动板子,进入到启动成功以后进入到/proc/device-tree/目录中查看是否有“alphaled”这个节点,如果编译以及没有错误的话,就会出现下图的情况,具体如下:
在这里插入图片描述
进入到alphaled里面:在这里插入图片描述

LED 灯驱动程序编写

新建名为“4_dtsled”文件夹,然后在 4_dtsled 文件夹里面创建vscode 工程,工作区命名为“dtsled”。工程创建好以后新建 dtsled.c 文件,在 dtsled.c 里面输入如下内容:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define dtsled_CNT 1 /*设备号个数*/
#define dtsled_NAME "dtsled"
#define LEDOFF 0
#define LEDON 1/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;/* dtsled 设备结构体 */
struct dtsled_dev
{dev_t devid;struct cdev cdev;struct class *class;struct device *device;int major;int minor;struct device_node *nd;
};
struct dtsled_dev dtsled;
void LED_Switches(u8 state)
{u32 retval = 0;if (state == LEDON){retval = readl(GPIO1_DR);retval &= ~(1 << 3);writel(retval, GPIO1_DR);}else if (state == LEDOFF){retval = readl(GPIO1_DR);retval |= (1 << 3);writel(retval, GPIO1_DR);}
}
/** @description : 打开设备* @param – inode : 传递给驱动的 inode* @param - filp : 设备文件, file 结构体有个叫做 private_data 的成员变量* 一般在 open 的时候将 private_data 指向设备结构体。* @return : 0 成功;其他 失败*/
static int ledopen(struct inode *inode, struct file *filp)
{filp->private_data = &dtsled;return 0;
}
/** @description : 从设备读取数据* @param - filp : 要打开的设备文件(文件描述符)* @param - buf : 返回给用户空间的数据缓冲区* @param - cnt : 要读取的数据长度* @param - offt : 相对于文件首地址的偏移* @return : 读取的字节数,如果为负值,表示读取失败*/
static int ledread(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}
/** @description : 向设备写数据* @param - filp : 设备文件,表示打开的文件描述符* @param - buf : 要写给设备写入的数据* @param - cnt : 要写入的数据长度* @param - offt : 相对于文件首地址的偏移* @return : 写入的字节数,如果为负值,表示写入失败*/
static ssize_t ledwrite(struct file *filp, char __user *buf, size_t cnt, loff_t off_t)
{int retvalue = 0;unsigned char databuf[1];u8 ledstat;retvalue = copy_from_user(databuf, buf, cnt);if (retvalue < 0){printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0];if (ledstat == LEDON){LED_Switches(LEDON);}else if (ledstat == LEDOFF){LED_Switches(LEDOFF);}return 0;
}
/** @description : 关闭/释放设备* @param – filp : 要关闭的设备文件(文件描述符)* @return : 0 成功;其他 失败*/
static int led_release(struct inode *inode, struct file *filp)
{return 0;
}
static struct file_operations dtsled_fops = {.owner = THIS_MODULE,.open = ledopen,.read = ledread,.write = ledwrite,.release = led_release,
};
/** @description : 驱动入口函数* @param : 无* @return : 无*/
static int __init led_init(void)
{u32 val = 0;int ret;u32 regdata[14];const char *str;struct property *proper;/* 获取设备树中的属性数据 *//* 1、获取设备节点: alphaled */dtsled.nd = of_find_node_by_path("/alphaled");if (dtsled.nd == NULL){printk("alphaled node not found!\r\n");return -EINVAL;}else{printk("alphaled node found!\r\n");}/*2、取compatible属性内容*/proper = of_find_property(dtsled.nd, "compatible", NULL);if (proper == NULL){printk("compatibility property not found!\r\n");}else{printk("compatibility property=%s\r\n", (char *)proper->value);}/* 3、获取 status 属性内容 */ret = of_property_read_string(dtsled.nd, "status", &str);if (ret < 0){printk("status read failed!\r\n");}else{printk("status = %s\r\n", str);}/* 4、获取 reg 属性内容 */ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);if (ret < 0){printk("reg property read failed!\r\n");}else{u8 i = 0;printk("reg data:\r\n");for (i = 0; i < 10; i++)printk("%#X ", regdata[i]);printk("\r\n");}/* 初始化 LED */
#if 0/* 1、寄存器地址映射 */
IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]);
SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]);
GPIO1_DR = ioremap(regdata[6], regdata[7]);
GPIO1_GDIR = ioremap(regdata[8], regdata[9]);
#elseIMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);GPIO1_DR = of_iomap(dtsled.nd, 3);GPIO1_GDIR = of_iomap(dtsled.nd, 4);
#endif/* 2、使能 GPIO1 时钟 */val = readl(IMX6U_CCM_CCGR1);val &= ~(3 << 26); /* 清除以前的设置 */val |= (3 << 26);  /* 设置新值 */writel(val, IMX6U_CCM_CCGR1);/* 3、设置 GPIO1_IO03 的复用功能,将其复用为* GPIO1_IO03,最后设置 IO 属性。*/writel(5, SW_MUX_GPIO1_IO03);/* 寄存器 SW_PAD_GPIO1_IO03 设置 IO 属性 */writel(0x10B0, SW_PAD_GPIO1_IO03);/* 4、设置 GPIO1_IO03 为输出功能 */val = readl(GPIO1_GDIR);val &= ~(1 << 3); /* 清除以前的设置 */val |= (1 << 3);  /* 设置为输出 */writel(val, GPIO1_GDIR);/* 5、默认关闭 LED */val = readl(GPIO1_DR);val |= (1 << 3);writel(val, GPIO1_DR);/* 6、注册字符设备驱动 *//*1、创建设备号*/if (dtsled.major){dtsled.devid = MKDEV(dtsled.major, 0);register_chrdev_region(dtsled.devid, dtsled_CNT, dtsled_NAME);}else{alloc_chrdev_region(&dtsled.devid, 0, dtsled_CNT, dtsled_NAME);dtsled.major = MAJOR(dtsled.devid);dtsled.minor = MINOR(dtsled.devid);}printk("newcheled major: %d minor: %d", dtsled.major, dtsled.minor);dtsled.cdev.owner = THIS_MODULE;cdev_init(&dtsled.cdev, &dtsled_fops);cdev_add(&dtsled.cdev, dtsled.devid, dtsled_CNT);dtsled.class = class_create(THIS_MODULE, dtsled_NAME);if (IS_ERR(dtsled.class)){return PTR_ERR(dtsled.class);}dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, dtsled_NAME);if (IS_ERR(dtsled.device)){return PTR_ERR(dtsled.device);}return 0;
}
static void __exit led_exit(void)
{/* 取消映射 */iounmap(IMX6U_CCM_CCGR1);iounmap(SW_MUX_GPIO1_IO03);iounmap(SW_PAD_GPIO1_IO03);iounmap(GPIO1_DR);iounmap(GPIO1_GDIR);/* 注销字符设备驱动 */cdev_del(&dtsled.cdev); /* 删除 cdev */unregister_chrdev_region(dtsled.devid, dtsled_CNT);device_destroy(dtsled.class, dtsled.devid);class_destroy(dtsled.class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wyw");

相对于上一节变化的内容是以下几个地方:

    IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);GPIO1_DR = of_iomap(dtsled.nd, 3);GPIO1_GDIR = of_iomap(dtsled.nd, 4);

of_iomap函数上一节已经学习了函数,

void __iomem *of_iomap(struct device_node *np,int index)
函数参数和返回值含义如下:
np:设备节点。
index: reg 属性中要完成内存映射的段,如果 reg 属性只有一段的话 index 就设置为 0。
返回值: 经过内存映射后的虚拟内存首地址,如果为 NULL 的话表示内存映射失败,
1 alphaled {
2 #address-cells = <1>;
3 #size-cells = <1>;
4 compatible = "atkalpha-led";
5 status = "okay";
6 reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */
7 0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */
8 0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */
9 0X0209C000 0X04 /* GPIO1_DR_BASE */
10 0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */
11 }

index 参数用于指定要映射的具体内存段。每个内存段由一对

表示,因此 index 的值决定了要映射哪一对。

index = 0: 映射第一对 <0X020C406C 0X04>,即 CCM_CCGR1_BASE
index = 1: 映射第二对 <0X020E0068 0X04>,即 SW_MUX_GPIO1_IO03_BASE
index = 2: 映射第三对 <0X020E02F4 0X04>,即 SW_PAD_GPIO1_IO03_BASE
index = 3: 映射第四对 <0X0209C000 0X04>,即 GPIO1_DR_BASE
index = 4: 映射第五对 <0X0209C004 0X04>,即 GPIO1_GDIR_BASE

剩余编译测试和之前的是一样的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/721932.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

偶函数在零点的泰勒展开式相关知识点

步骤1: 理解偶函数的定义偶函数是指满足 f(x)=f(−x)f(x) = f(-x)f(x)=f(−x) 的函数。这意味着偶函数关于 yyy 轴对称。步骤2: 理解泰勒展开泰勒展开是一种将函数表示为无穷级数的方法,它在函数在某一点的所有导数都存在的情况下非常有效。对于函数 f(x)f(x)f(x) 在零点的泰勒…

三探堆栈欺骗之Custom Call Stacks

本文首发阿里云先知社区:https://xz.aliyun.com/t/14592 背景知识 在之前的文章中,我们介绍了静态欺骗和动态欺骗堆栈,今天我们来一起学习一下另一种技术,它被它的作者称为Custom Call Stacks,即自定义堆栈调用。 关于堆栈欺骗的背景我们就不再说了,这里我们补充一下回调函…

esphome esp8266刷写遇到的问题

问题描述: 在尝试打开串口时出现以下错误信息: Failed to execute open on SerialPort: Failed to open serial port.起因: 莫名其妙地安装了一个乐鑫的新版 CH340 驱动。解决方案:找到正确的驱动程序,如下图所示:卸载安装的新驱动,并等待系统自动安装正确的驱动。

数字滤波器和模拟滤波器(一)

下面介绍模拟滤波器和数字滤波器的频率响应的异同,以及如何使用python地`scipy.signal`来绘制其频谱响应和冲激阶跃响应。在第二期将谈到如何设计模拟滤波器和数字滤波器。模拟滤波器和数字滤波器(一) 下面介绍模拟滤波器和数字滤波器的频率响应的异同,以及如何使用python地…

茗鹤 | 模具如何在APS高级计划排程系统中,配置共模、互换镶件等有限约束条件?

在高度依赖模具的生产环境中,模具管理与APS生产排程的有效整合是提升效率、确保订单交期的关键。以下是针对模具管理的几个核心策略,通过APS高级计划排程系统的辅助,实现产能优化与生产调度的智能化升级在高度依赖模具的生产环境中,模具管理与APS生产排程的有效整合是提升效…

靶机练习:Gitroot

信息收集 扫描全端口以发现服务访问80端口,有hint尝试绑定域名到/etc/vuln,同时提示中存在用户名jen,可以尝试爆破ssh 绑定后能访问站点了用wpscan能扫出wordpress的用户名接下来没爆破出密码 尝试使用wfuzz扫描子域名 wfuzz -c -u http://gitroot.vuln -H "HOST:FUZZ.…

Redis-10-分布式锁.md

参考: 分布式锁介绍 1.概念 额,为什么的话,建议先了解下我这篇文章。 Java-并发-并发的基本概念 我们在并发场景下,区分一个场景是否有并发问题,个人理解,锁的场景需要考虑:共享:是否共享某个资源 竞态:如何构建竞态关系首先,我们得拎清楚它到底会不会共享,不是说多…

一文了解 - - SpringMVC

一、SpringMVC概述Spring MVC 是由Spring官方提供的基于MVC设计理念的web框架。 SpringMVC是基于Servlet封装的用于实现MVC控制的框架,实现前端和服务端的交互。1.1 SpringMVC优势严格遵守了MVC分层思想采用了松耦合、插件式结构;相比较于我们封装的BaseServlet以及其他的一些…

php-status监控流程

1.开启php的状态页功能 #基于php-fpm进程做的实验yum install php-fpm -y修改配置文件,开启php,status功能即可,打开如下参数即可 要求你访问php状态页面的入口就是/status_php[root@web-7 ~]#grep status_ /etc/php-fpm.d/www.conf pm.status_path = /status_phpphp-fpm,…

南昌航空大学软件学院23201823第二次blog

一、前言: 这是第二次的blog,接下来关于这最近三次的PTA大作业,只有第一次是上次答题判题程序的延续,接下来则是一个全新的关于电路的设计,最新的电路设计相较于之前的答题判题程序来说的话,难度确实有所下降。前两次中都含有三道题,而最后一次的PTA则是删去了其余两道题…

BUUCTF-Misc(121-130)

[UTCTF2020]sstv 参考: [UTCTF2020]QSSTV - cuihua- - 博客园 (cnblogs.com) qsstv解密一下flag{6bdfeac1e2baa12d6ac5384cdfd166b0}voip 参考: buuctf VoIP-CSDN博客 voip就是语音通话技术然后wireshark可以直接播放这个语音然后播放一下flag就是考听力的,加油吧,我太垃圾…