文章目录
- 一、设备树修改
- 二、led驱动程序编写
- **1️⃣ `led_driver.c`(LED 设备驱动)**
- **📌 关键部分解析**
- **1. 设备节点创建**
- **2. GPIO 资源获取**
- **3. 读/写操作**
- **2️⃣ `led_test.c`(用户态测试程序)**
- **📌 关键部分解析**
- **1. 打开 LED 设备**
- **2. 解析 `on/off` 命令**
- **3. 写入 LED 状态**
- **4. 关闭设备**
- **3️⃣ `Makefile`(编译内核模块)**
- **📌 关键部分解析**
- **1. `KERNELDIR`**
- **2. `obj-m := led_driver.o`**
- **3. `build` 目标**
- **4. `kernel_modules` 目标**
- **5. `clean` 目标**
- **4️⃣ 编译和运行**
- **🔹 1. 编译内核模块**
- **🔹 2. 加载驱动**
- **🔹 3. 确认设备文件**
- **🔹 4. 运行测试程序**
- **🔹 5. 卸载驱动**
- **总结**
- **📌 1. `led_driver.c`**
- **📌 2. `led_test.c`**
- **📌 3. `Makefile`**
一、设备树修改
打开rk3568-evb.dtsi文件:
正点原子的RK3568中,LED灯被用于了心跳灯,所以在这里我们需要关闭这个心跳灯,使用status = "disable"即可关闭。
打开rk3568-atk-evb1-ddr4-v10.dtsi文件,在这个文件里面加入led灯的相关设备树:
二、led驱动程序编写
1️⃣ led_driver.c
(LED 设备驱动)
这是一个 基于平台设备(platform_driver
)的 LED 控制内核驱动,它的主要功能是:
- 从设备树获取 LED GPIO 引脚
- 设置 GPIO 为输出模式
- 通过
write
接口 控制 LED 开关 - 通过
platform_driver
注册驱动
📌 关键部分解析
1. 设备节点创建
device_create(led_class, NULL, MKDEV(major, 0), NULL, "alientekled");
- 这行代码会创建
/dev/alientekled
设备文件,用户程序可以通过open("/dev/alientekled", O_RDWR)
来访问 LED。
2. GPIO 资源获取
led_gpio = gpiod_get(&pdev->dev, NULL, 0);
- 从设备树 解析
alientek,led
设备的 GPIO 引脚。 led_gpio
指向该 GPIO 的控制结构体。
3. 读/写操作
led_write()
char val; copy_from_user(&val, buf, 1); gpiod_set_value(led_gpio, val);
- 读取用户传来的 1字节数据,然后调用
gpiod_set_value()
控制 GPIO 输出高/低电平。
- 读取用户传来的 1字节数据,然后调用
2️⃣ led_test.c
(用户态测试程序)
这个程序是 用户空间的 LED 控制程序,它:
- 通过 命令行参数 获取
/dev/xxx
设备文件名 和on/off
指令 - 通过
open()
打开设备 - 通过
write()
写入1
或0
来控制 LED
📌 关键部分解析
1. 打开 LED 设备
fd = open(argv[1], O_RDWR);
argv[1]
是传入的设备文件名,例如/dev/alientekled
- 如果
open()
失败,会返回fd < 0
,说明设备文件可能不存在。
2. 解析 on/off
命令
if (strcmp(argv[2], "on") == 0) {val = 1;
} else if (strcmp(argv[2], "off") == 0) {val = 0;
}
- 如果
argv[2]
是on
,val=1
让 LED 亮 - 如果
argv[2]
是off
,val=0
让 LED 灭
3. 写入 LED 状态
write(fd, &val, 1);
- 调用 驱动的
write()
函数 - 传递
1
或0
,让 LED 亮灭
4. 关闭设备
close(fd);
- 释放设备文件
3️⃣ Makefile
(编译内核模块)
KERNELDIR := /home/alientek//rk3568_linux_sdk/kernel/
CURRENT_PATH := $(shell pwd)
obj-m := led_driver.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
📌 关键部分解析
1. KERNELDIR
KERNELDIR := /home/alientek//rk3568_linux_sdk/kernel/
- 指定 Linux 内核源码路径,用于交叉编译内核模块。
2. obj-m := led_driver.o
obj-m := led_driver.o
- 告诉内核:需要编译
led_driver.c
成led_driver.ko
(内核模块)。
3. build
目标
build: kernel_modules
make
时,调用kernel_modules
目标。
4. kernel_modules
目标
kernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
- 进入内核源码目录
$(KERNELDIR)
- 使用当前目录的代码(
M=$(CURRENT_PATH)
)编译led_driver.ko
- 进入内核源码目录
5. clean
目标
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
- 清理 编译过程中生成的
.o
、.ko
、.mod
等文件。
4️⃣ 编译和运行
🔹 1. 编译内核模块
make
会生成:
led_driver.ko
🔹 2. 加载驱动
insmod led_driver.ko
🔹 3. 确认设备文件
ls /dev/alientekled
如果 /dev/alientekled
存在,则驱动正常。
🔹 4. 运行测试程序
./led_test /dev/alientekled on # 打开LED
./led_test /dev/alientekled off # 关闭LED
🔹 5. 卸载驱动
rmmod led_driver
总结
📌 1. led_driver.c
- 平台驱动 (
platform_driver
),从 设备树 获取 GPIO 并控制 LED。 - 通过
write()
接收用户指令,控制 GPIO 高/低电平。
#include <linux/module.h>
#include <linux/poll.h>#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>
#include <linux/timex.h>int major=0;
static struct class *led_class;
static struct gpio_desc *led_gpio;static ssize_t led_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{return 0;}static int led_open (struct inode *inode, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static ssize_t led_write (struct file *file, const char __user *buf, size_t size, loff_t *off)
{char val;int err;err = copy_from_user(&val, buf, 1);gpiod_set_value(led_gpio, val);printk("%s %s line %d val:%d\n", __FILE__, __FUNCTION__, __LINE__,val);return 1;
}static struct file_operations led_ops={.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,
};static int led_probe(struct platform_device *pdev)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/*1.获取硬件信息*/led_gpio=gpiod_get(&pdev->dev, NULL, 0);if (IS_ERR(led_gpio)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);}/*将GPIO引脚设置为输出引脚*/gpiod_direction_output(led_gpio, 0);/*2.创建设备节点*/ device_create(led_class,NULL, MKDEV(major, 0), NULL, "alientekled");return 0;}static int led_remove(struct platform_device *pdev)
{ printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);gpiod_put(led_gpio);return 0;
}static const struct of_device_id my_led[] = {{ .compatible = "alientek,led" },{ },
};static struct platform_driver led={.driver = {.name = "led",.of_match_table = my_led, },.probe = led_probe,.remove = led_remove,
};static int __init led_init(void)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/*确定主设备号*/major=register_chrdev(major, "myled", &led_ops);/*创建类*/led_class=class_create(THIS_MODULE, "led");if (IS_ERR(led_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "myled");return PTR_ERR(led_class);}err=platform_driver_register(&led);return 0;
}static void __exit led_exit(void)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(led_class, MKDEV(major, 0));class_destroy(led_class);unregister_chrdev(major, "myled");platform_driver_unregister(&led);
}module_init(led_init);
module_exit(led_exit);MODULE_LICENSE("GPL");
📌 2. led_test.c
- 用户空间程序,通过
write()
控制/dev/alientekled
设备。 - 传入 “on” / “off” 控制 LED。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(int argc,char **argv)
{int fd;char val;if(argc != 3){printf("Usage:\n");printf("%s <dev> <on|off>\n",argv[0]);}fd = open(argv[1],O_RDWR);if(fd<0){printf("can not open the file\n");return -1;}if(strcmp(argv[2],"on") == 0){val = 1;}else if(strcmp(argv[2],"off") == 0){val = 0;}write(fd,&val,1);close(fd);return 0;
}
📌 3. Makefile
- 使用
make
交叉编译 LED 内核驱动。 - 生成
led_driver.ko
并安装到内核。
这样,你的 LED 驱动 + 用户态控制程序 就完整了 🎉!
KERNELDIR := /home/alientek//rk3568_linux_sdk/kernel/
CURRENT_PATH := $(shell pwd)
obj-m := led_driver.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean