RK3568笔记三十六:LED驱动开发(设备树)
若该文为原创文章,转载请注明原文出处。
记录使用设备树编写一个简单的 LED 灯驱动程序
一、编程思路
程序编写的主要内容为添加 LED 灯的设备树节点、在驱动程序中使用 of 函数获取设备节点中的 属性,编写测试应用程序。
• 首先向设备树添加 LED 设备节点。
• 其次编写平台设备驱动框架,主要包驱动入口函数、驱动注销函数、平台设备结构体定义 三部分内容。
• 实现.probe 函数,对 LED 进行设备注册和初始化。
• 实现字符设备操作函数集,这里主要实现.write 操作。
• 编写测试应用程序,对于输入不同的值控制 LED 亮灭
二、编写设备树
RK3568支持设备树,设备树相关知识执行了解。
设备树目录在arch/arm64/boot/dts/rockchip
下。
修改 rk3568-atk-evb1-ddr4-v10.dtsi文件,在根目录 /
下建立一个节点:
led_test {compatible="yifeng,led_test";status="okay";reg = <0x0 0xFDC20010 0x0 0x08 /* PMU_GRF_GPIO0C_IOMUX_L */ 0x0 0xFDC20090 0x0 0x08 /* PMU_GRF_GPIO0C_DS_0 */ 0x0 0xFDD60004 0x0 0x08 /* GPIO0_SWPORT_DR_H */ 0x0 0xFDD6000C 0x0 0x08 >; /* GPIO0_SWPORT_DDR_H */};
reg里的值参考LED驱动开发,比如“0x0 0xFDC20010 0x0 0x08”表示 RK3568 的 PMU_GRF_GPIO0C_IOMUX_L 寄存器,其中寄存器地址为 0xFDC20010,长度为 8 个字节。
修改如下:
设备树修改完成以后在 SDK 顶层目录输入如下命令重新编译一下内核:
# 指定 SDK 的板级配置文件
./build.sh lunch
# 编译内核
./build.sh kernel
只需要重新烧写boot.img。

三、编写驱动
led_test.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 "led_test" /* 名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 *//* 映射后的寄存器虚拟地址指针 */
static void __iomem *PMU_GRF_GPIO0C_IOMUX_L_PI;
static void __iomem *PMU_GRF_GPIO0C_DS_0_PI;
static void __iomem *GPIO0_SWPORT_DR_H_PI;
static void __iomem *GPIO0_SWPORT_DDR_H_PI;/* dtsled设备结构体 */
struct dtsled_dev{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */int minor; /* 次设备号 */struct device_node *nd; /* 设备节点 */
};struct dtsled_dev dtsled; /* led设备 *//** @description : LED打开/关闭* @param - sta : LEDON(0) 打开LED,LEDOFF(1) 关闭LED* @return : 无*/
void led_switch(u8 sta)
{u32 val = 0;if(sta == LEDON) {val = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0X1 << 0); /* bit0 清零*/val |= ((0X1 << 16) | (0X1 << 0)); /* bit16 置1,允许写bit0,bit0,高电平*/writel(val, GPIO0_SWPORT_DR_H_PI);}else if(sta == LEDOFF) { val = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0X1 << 0); /* bit0 清零*/val |= ((0X1 << 16) | (0X0 << 0)); /* bit16 置1,允许写bit0,bit0,低电平 */writel(val, GPIO0_SWPORT_DR_H_PI);}
}/** @description : 取消映射* @return : 无*/
void led_unmap(void)
{/* 取消映射 */iounmap(PMU_GRF_GPIO0C_IOMUX_L_PI);iounmap(PMU_GRF_GPIO0C_DS_0_PI);iounmap(GPIO0_SWPORT_DR_H_PI);iounmap(GPIO0_SWPORT_DDR_H_PI);
}/** @description : 打开设备* @param - inode : 传递给驱动的inode* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量* 一般在open的时候将private_data指向设备结构体。* @return : 0 成功;其他 失败*/
static int led_open(struct inode *inode, struct file *filp)
{filp->private_data = &dtsled; /* 设置私有数据 */return 0;
}/** @description : 从设备读取数据 * @param - filp : 要打开的设备文件(文件描述符)* @param - buf : 返回给用户空间的数据缓冲区* @param - cnt : 要读取的数据长度* @param - offt : 相对于文件首地址的偏移* @return : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t led_read(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 led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[1];unsigned char 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_switch(LEDON); /* 打开LED灯 */} else if(ledstat == LEDOFF) {led_switch(LEDOFF); /* 关闭LED灯 */}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 = led_open,.read = led_read,.write = led_write,.release = led_release,
};/** @description : 驱动出口函数* @param : 无* @return : 无*/
static int __init led_init(void)
{u32 val = 0;int ret;u32 regdata[16];const char *str;struct property *proper;/* 获取设备树中的属性数据 *//* 1、获取设备节点:led_test */dtsled.nd = of_find_node_by_path("/led_test");if(dtsled.nd == NULL) {printk("led_test node not find!\r\n");goto fail_find_node;} else {printk("led_test node find!\r\n");}/* 2、获取compatible属性内容 */proper = of_find_property(dtsled.nd, "compatible", NULL);if(proper == NULL) {printk("compatible property find failed\r\n");} else {printk("compatible = %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, 16);if(ret < 0) {printk("reg property read failed!\r\n");} else {u8 i = 0;printk("reg data:\r\n");for(i = 0; i < 16; i++)printk("%#X ", regdata[i]);printk("\r\n");}/* 初始化LED *//* 1、寄存器地址映射 */PMU_GRF_GPIO0C_IOMUX_L_PI = of_iomap(dtsled.nd, 0);PMU_GRF_GPIO0C_DS_0_PI = of_iomap(dtsled.nd, 1);GPIO0_SWPORT_DR_H_PI = of_iomap(dtsled.nd, 2);GPIO0_SWPORT_DDR_H_PI = of_iomap(dtsled.nd, 3);/* 2、设置GPIO0_C0为GPIO功能。*/val = readl(PMU_GRF_GPIO0C_IOMUX_L_PI);val &= ~(0X7 << 0); /* bit2:0,清零 */val |= ((0X7 << 16) | (0X0 << 0)); /* bit18:16 置1,允许写bit2:0,bit2:0:0,用作GPIO0_C0 */writel(val, PMU_GRF_GPIO0C_IOMUX_L_PI);/* 3、设置GPIO0_C0驱动能力为level5 */val = readl(PMU_GRF_GPIO0C_DS_0_PI);val &= ~(0X3F << 0); /* bit5:0清零*/val |= ((0X3F << 16) | (0X3F << 0)); /* bit21:16 置1,允许写bit5:0,bit5:0:0,用作GPIO0_C0 */writel(val, PMU_GRF_GPIO0C_DS_0_PI);/* 4、设置GPIO0_C0为输出 */val = readl(GPIO0_SWPORT_DDR_H_PI);val &= ~(0X1 << 0); /* bit0 清零*/val |= ((0X1 << 16) | (0X1 << 0)); /* bit16 置1,允许写bit0,bit0,高电平 */writel(val, GPIO0_SWPORT_DDR_H_PI);/* 5、设置GPIO0_C0为低电平,关闭LED灯。*/val = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0X1 << 0); /* bit0 清零*/val |= ((0X1 << 16) | (0X0 << 0)); /* bit16 置1,允许写bit0,bit0,低电平 */writel(val, GPIO0_SWPORT_DR_H_PI);/* 注册字符设备驱动 *//* 1、创建设备号 */if (dtsled.major) { /* 定义了设备号 */dtsled.devid = MKDEV(dtsled.major, 0);ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);if(ret < 0) {pr_err("cannot register %s char driver [ret=%d]\n",DTSLED_NAME, DTSLED_CNT);goto fail_devid;}} else { /* 没有定义设备号 */ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME); /* 申请设备号 */if(ret < 0) {pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", DTSLED_NAME, ret);goto fail_devid;}dtsled.major = MAJOR(dtsled.devid); /* 获取分配号的主设备号 */dtsled.minor = MINOR(dtsled.devid); /* 获取分配号的次设备号 */}printk("dtsled major=%d,minor=%d\r\n",dtsled.major, dtsled.minor); /* 2、初始化cdev */dtsled.cdev.owner = THIS_MODULE;cdev_init(&dtsled.cdev, &dtsled_fops);/* 3、添加一个cdev */ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);if(ret < 0)goto del_unregister;/* 4、创建类 */dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);if (IS_ERR(dtsled.class)) {goto del_cdev;}/* 5、创建设备 */dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);if (IS_ERR(dtsled.device)) {goto destroy_class;}return 0;destroy_class:class_destroy(dtsled.class);
del_cdev:cdev_del(&dtsled.cdev);
del_unregister:unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
fail_devid:led_unmap();
fail_find_node:return -EIO;
}/** @description : 驱动出口函数* @param : 无* @return : 无*/
static void __exit led_exit(void)
{/* 取消映射 */led_unmap();/* 注销字符设备驱动 */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("ALIENTEK");
MODULE_INFO(intree, "Y");
makefile
KERNELDIR := /home/alientek/rk3568_linux_sdk/kernel
ARCH=arm64
CROSS_COMPILE=/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu-export ARCH CROSS_COMPILECURRENT_PATH := $(shell pwd)
obj-m := led_test.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
四、编写应用
test_app.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"#define LEDOFF 0
#define LEDON 1/** @description : main主程序* @param - argc : argv数组元素个数* @param - argv : 具体参数* @return : 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd, retvalue;char *filename;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打开led驱动 */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 *//* 向/dev/led文件写入数据 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 关闭文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}
编译
/opt/atk-dlrk356x-toolchain/bin/aarch64-buildroot-linux-gnu-gcc ledApp.c -o ledApp
五、测试
1、关闭 LED 的心跳灯
echo none > /sys/class/leds/work/trigger
2、加载和卸载驱动模块
depmod //第一次加载驱动的时候需要运行此命令
modprobe dtsled //加载驱动或
insmod led_test.ko卸载
rmmod led_test
3、测试
./ledApp /dev/led_test 1 //打开 LED 灯
./ledApp /dev/led_test 0 // 关闭 LED 灯
如有侵权,或需要完整代码,请及时联系博主。
相关文章:

RK3568笔记三十六:LED驱动开发(设备树)
若该文为原创文章,转载请注明原文出处。 记录使用设备树编写一个简单的 LED 灯驱动程序 一、编程思路 程序编写的主要内容为添加 LED 灯的设备树节点、在驱动程序中使用 of 函数获取设备节点中的 属性,编写测试应用程序。 • 首先向设备树添加 LED 设备…...

AC修炼计划(AtCoder Regular Contest 180) A~C
A - ABA and BAB A - ABA and BAB (atcoder.jp) 这道题我一开始想复杂了,一直在想怎么dp,没注意到其实是个很简单的规律题。 我们可以发现我们住需要统计一下类似ABABA这样不同字母相互交替的所有子段的长度,而每个字段的的情况有ÿ…...
云计算练习题
第一题:每周日晚上11点59分需要将/data目录打包压缩到/mnt目录下并以时间命名 #crontab -e 59 23 * * 7 /bin/tar czvf /mnt/date %F-data.tar.gz /data 59 23 * * 7 /bin/tar czvf /mnt/date %T.tar.gz /data 第二题:查找出系统中/application目录下所有…...

《战甲神兵》开发者报告:游戏崩溃问题80%发生在Intel可超频酷睿i9处理器上——酷睿i7 K系列CPU也表现出高崩溃率
在Intel持续面临第13代和第14代CPU崩溃问题的背景下,近日,《战甲神兵》(Warframe)的开发者们于7月9日披露了游戏崩溃的统计数据,并描述了诊断该问题的过程。根据开发团队的说法,一名未进行超频且使用全新PC的员工,即便…...

Postman下载及使用说明
Postman使用说明 Postman是什么? Postman是一款接口对接工具【接口测试工具】 接口(前端接口)是什么? 前端发送的请求普遍被称为接口 通常有网页的uri参数格式json/key-value请求方式post/get响应请求的格式json 接…...

什么是im即时通讯?WorkPlus im即时通讯私有化部署安全可控
IM即时通讯是Instant Messaging的缩写,指的是一种实时的、即时的电子信息交流方式,也被称为即时通讯。它通过互联网和移动通信网络,使用户能够及时交换文本消息、语音通话、视频通话、文件共享等信息。而WorkPlus im即时通讯私有化部署则提供…...
hnust 1794: 机器翻译
hnust 1794: 机器翻译 题目描述 小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。 这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存…...

AI人工智能开源大模型生态体系分析
人工智能开源大模型生态体系研究 "人工智能开源大模型生态体系研究报告v1.0"揭示,AI(A)的飞速发展依赖于三大核心:数据、算法和算力。这一理念已得到业界广泛认同,三者兼备才能推动AI的壮大发展。随着AI大模型的扩大与普及…...
ArkTS学习笔记_封装复用之@Styles装饰器
ArkTS学习笔记_封装复用之Styles装饰器 背景: 在开发中,如果每个组件的样式都需要单独设置,就会出现大量代码在进行重复样式设置,虽然可以复制粘贴,但为了代码简洁性和后续方便维护,给出的思路是ÿ…...
根据vue学习react
react的函数式组件与vue2是很像的 一、基础类似点 1、组件下拥有一个根节点,vue2是template,react是幽灵标签<> 2、vue2是{{}}以及v-model,react的绑定是{} 3、vue2编译html是v-html,react是{},并且react的jsx中…...

Hi3861 OpenHarmony嵌入式应用入门--HTTPD
httpd 是 Apache HTTP Server 的守护进程名称,Apache HTTP Server 是一种广泛使用的开源网页服务器软件。 本项目是从LwIP中抽取的HTTP服务器代码; Hi3861 SDK中已经包含了一份预编译的lwip,但没有开启HTTP服务器功能(静态库无法…...

MICS2024|少样本学习、多模态技术以及大语言模型在医学图像处理领域的研究进展|24-07-14
小罗碎碎念 本期推文主题 今天的会议很多主题都集中在大模型、多模态这两个方面,很明显,这两个方向都是目前的研究热点。 所以,我这一期推文会先简单的分析一下秦文健(中科院)和史淼晶(同济大学)…...

ConfigMap-secrets-静态pod
一.ConfigMap 1.概述 ConfigMap资源,简称CM资源,它生成的键值对数据,存储在ETCD数据库中 应用场景:主要是对应用程序的配置 pod通过env变量引入ConfigMap,或者通过数据卷挂载volume的方式引入ConfigMap资源 官方解释…...
SQL Error: 1406, SQLState: 22001
SQL错误代码1406和SQLState 22001通常表示“列数据过长”错误。这意味着尝试插入或更新列中的值,但该值的长度超过了该列允许的最大长度。 解决此问题的几个步骤: 检查列长度: 确定引起错误的列。检查数据库架构中该列允许的最大长度。 验证…...

【密码学基础】基于LWE(Learning with Errors)的全同态加密方案
学习资源: 全同态加密I:理论与基础(上海交通大学 郁昱老师) 全同态加密II:全同态加密的理论与构造(Xiang Xie老师) 现在第二代(如BGV和BFV)和第三代全同态加密方案都是基…...

Linux - 基础开发工具(yum、vim、gcc、g++、make/Makefile、git)
目录 Linux软件包管理器 - yum Linux下安装软件的方式 认识yum 查找软件包 安装软件 如何实现本地机器和云服务器之间的文件互传 卸载软件 Linux编辑器 - vim vim的基本概念 vim下各模式的切换 vim命令模式各命令汇总 vim底行模式各命令汇总 vim的简单配置 Linux编译器 - gc…...
网络安全法律框架更新:最新合规要求与企业应对策略
网络安全法律框架的最新更新 近期,中国的网络安全法律框架经历了重要的更新。2022年,《网络安全法》迎来了首次修改,这一修订主要是为了与《数据安全法》和《个人信息保护法》等新实施的法律进行衔接协调,完善法律责任制度&#x…...
数仓工具—Hive语法之正则表达式函数
正则表达式函数 之前我们介绍过like rlike regexp 这些关键字,都是和匹配有关的,今天我们介绍一下hive 的REGEXP_REPLACE 和REGEXP_EXTRACT 函数,背景是使用Hive正则表达式函数提取数字 在我的其他文章中,我们已经看到了如何使用Hive正则表达式从字符串中提取日期值。正则…...

WKCTF 2024 easy_heap
很经典的house of orange unsortedbin attack FSOP 变量覆盖 不能 free,那首先想到就是 house of orange泄露Libc基址,然后unsortedbin attack。 但是只能show(8),就不能用largebin的套路来泄露堆地址了,那怎么办呢? …...

SQL 多变关联使用子查询去重
不去重状态 select a.*,b.recon_amt from free_settlement_first aleft join free_settlement_second b on a.settlement_first_id b.settlement_first_id 有2条数据出现了重复 使用子查询去重 select a.*,b.recon_amt from free_settlement_first aleft join free_settlem…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...

376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...