当前位置: 首页 > news >正文

RK3568使用gpio子系统完成led驱动程序编写

文章目录

  • 一、设备树修改
  • 二、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 输出高/低电平

2️⃣ led_test.c(用户态测试程序)

这个程序是 用户空间的 LED 控制程序,它:

  • 通过 命令行参数 获取 /dev/xxx 设备文件名 和 on/off 指令
  • 通过 open() 打开设备
  • 通过 write() 写入 10 来控制 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]onval=1 让 LED 亮
  • 如果 argv[2]offval=0 让 LED 灭
3. 写入 LED 状态
write(fd, &val, 1);
  • 调用 驱动的 write() 函数
  • 传递 10,让 LED 亮灭
4. 关闭设备
close(fd);
  • 释放设备文件

3️⃣ Makefile(编译内核模块)

KERNELDIR := /home/alientek//rk3568_linux_sdk/kernel/
CURRENT_PATH := $(shell pwd)
obj-m := led_driver.o
 
build: kernel_modules
 
kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
	$(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.cled_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.o
 
build: kernel_modules
 
kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

相关文章:

  • f103.delay,teitou
  • docker-compose部署prometheus+grafana+node_exporter+alertmanager规则+邮件告警
  • DevOps部署平台
  • 【数论2】
  • 【ArcGIS操作】ArcGIS 进行空间聚类分析
  • 全星研发管理APQP软件系统:驱动汽车产品研发全周期,打造高效合规的质量管理引擎
  • TDengine 中的关联查询
  • Java康威生命游戏(Conway‘s Game of Life)
  • opencv图像处理之指纹识别
  • Java并发编程笔记
  • unity3d端监听 uri scheme
  • 解决Docker端口映射后外网无法访问的问题
  • Leetcode-100 回溯法-单词搜索
  • c#中的virtual方法
  • redis错误分析 forceUnlock的问题说明
  • #基于Django实现机器学习医学指标概率预测网站
  • 双塔模型2之如何选择正确的正负样本
  • Matlab基础知识与常见操作【无痛入门】
  • GPT Workspace体验
  • # 基于 OpenCV 的选择题自动批改系统实现
  • 叫别人做网站安全吗/广州百度推广开户
  • wap网站的开发/滨州网站建设
  • 鞍山网站开发/seosem顾问
  • 苏州网站制作推广/焊工培训班
  • 南漳县建设局网站/如何做自己的网站
  • 河南新乡做网站公司哪家好/种子搜索引擎 磁力天堂