欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > Rk3568驱动开发_点亮led灯代码完善(手动挡)_6

Rk3568驱动开发_点亮led灯代码完善(手动挡)_6

2025/2/28 9:21:02 来源:https://blog.csdn.net/xyint/article/details/145889139  浏览:    关键词:Rk3568驱动开发_点亮led灯代码完善(手动挡)_6

1.实现思路:

应用层打开设备后通过write函数向内核中写值,1代表要打开灯,0代表要关闭灯
Linux配置gpio和控制gpio多了一个虚拟内存映射操作

2.注意事项:

配置和读写操作的时候要谨慎,比如先关掉gpio再注销掉虚拟内存否则照成内存泄漏设备直接死机(别问我怎么知道的)

3.实现:

驱动代码:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>#define LED_MAJOR 200
#define LED_NAME "led"#define PMU_GRF_BASE						      (0xFDC20000)
#define PMU_GRF_GPIO0C_IOMUX_L				(PMU_GRF_BASE + 0x0010)
#define PMU_GRF_GPIO0C_DS_0					  (PMU_GRF_BASE + 0X0090)
#define GPIO0_BASE						        (0xFDD60000)
#define GPIO0_SWPORT_DR_H				      (GPIO0_BASE + 0X0004)
#define GPIO0_SWPORT_DDR_H				    (GPIO0_BASE + 0X000C)#define LEDOPEN 1
#define LEDCLOSE 0/* 映射后的寄存器虚拟地址指针 */
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;// led gpio初始化操作
void gpio_init(void){u32 val = 0;// 设置GPIO0_c0为GPIO功能val = readl(PMU_GRF_GPIO0C_IOMUX_L_PI);val &= ~(0x7 << 0); //最低三位置0val |= ((0x7 << 16) | (0x0 << 0)); // 16 17 18位置1其他不变,bit2:0:0,用作GPIO0_C0writel(val, PMU_GRF_GPIO0C_IOMUX_L_PI);// 设置GPIO_C0驱动能力为level5val = readl(PMU_GRF_GPIO0C_DS_0_PI);val &= ~(0x3f << 0);  // 0 ~ 5置0val |= ((0x3f << 16) | (0x3f << 0)); // 16 ~ 21置1,0~5置1同时用作GPIO0c0writel(val, PMU_GRF_GPIO0C_DS_0_PI);// 设置GPIOO0_c0为输出val = readl(GPIO0_SWPORT_DDR_H_PI);val &= ~(0x1 << 0); // 0置0val |= ((0x1 << 16) | (0x1 << 0)); // 16置1,0置1writel(val, GPIO0_SWPORT_DDR_H_PI);// 设置GPIO_c0为低电平,关闭LEDval = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0x1 << 0);val |= ((0x1 << 16) | (0x0 << 0));writel(val, GPIO0_SWPORT_DR_H_PI);
}// 真实物理地址映射虚拟内存函数
void led_remap(void){PMU_GRF_GPIO0C_IOMUX_L_PI = ioremap(PMU_GRF_GPIO0C_IOMUX_L, 4);PMU_GRF_GPIO0C_DS_0_PI = ioremap(PMU_GRF_GPIO0C_DS_0, 4);GPIO0_SWPORT_DR_H_PI = ioremap(GPIO0_SWPORT_DR_H, 4);GPIO0_SWPORT_DDR_H_PI = ioremap(GPIO0_SWPORT_DDR_H, 4);}void led_releaseMap(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);
}void led_switch(int status){u32 val = 0;if(status == LEDOPEN){// 开灯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(status == LEDCLOSE){// 关灯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); }}static int led_open(struct inode* inode, struct file* filp){return 0;
}static int led_release(struct inode* inode, struct file* filp){return 0;
}static ssize_t led_write(struct file* filp, const char __user* buf, size_t count, loff_t* ppos){int ret;unsigned char databuf[1];unsigned char state;ret = copy_from_user(databuf, buf, count);if(ret < 0){printk("kernel write failed\r\n");return -EFAULT;}state = databuf[0];if(state == LEDOPEN){led_switch(LEDOPEN);}else if(state == LEDCLOSE){led_switch(LEDCLOSE);}return 0;
}/* 字符设备操作集*/
static const struct file_operations led_fops = {.owner = THIS_MODULE,.write = led_write,.open = led_open,.release = led_release,
};/*注册驱动加载卸载*/static int __init led_init(void){ // 入口int ret = 0;led_remap();  // 内存映射gpio_init();  // 初始化led灯ret = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);   // 注册字符设备if(ret < 0){printk("register chrdev failed!\r\n");return -EIO;}printk("led_init\r\n");return 0;
}static void __exit led_exit(void){ // 出口led_releaseMap();  // 注销内存映射unregister_chrdev(LED_MAJOR, LED_NAME);  // 注销printk("led_exit\r\n");}module_init(led_init);
module_exit(led_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Narnat");

应用层代码:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"int main(int argc, char* argv[]){int fd, ret;char* filename;unsigned char databuf[1];if(argc < 3){printf("ERROR Usage\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]);ret = write(fd, databuf, sizeof(databuf));if(ret < 0){printf("led control failed\r\n");close(fd);return -1;}close(fd);return 0;}

额外的:

在这里插入图片描述
配置了如下.vscode文件让内核函数能跳转到内核函数实现

c_cpp_properties.json:

{"configurations": [{"name": "RK3568 Linux","includePath": ["${workspaceFolder}/**",// ARM64架构核心头文件"/home/saisi/RK3568/SDK/linux/rk3568_linux_sdk/kernel/include","/home/saisi/RK3568/SDK/linux/rk3568_linux_sdk/kernel/arch/arm64/include",// Rockchip芯片专用头文件"/home/saisi/RK3568/SDK/linux/rk3568_linux_sdk/kernel/drivers/soc/rockchip",// 编译生成的头文件(需先编译内核)"/home/saisi/RK3568/SDK/linux/rk3568_linux_sdk/kernel/output/include/generated","/home/saisi/RK3568/SDK/linux/rk3568_linux_sdk/kernel/output/include/config"],"defines": ["__KERNEL__",      // 必须定义内核模式"CONFIG_ARM64",    // 明确ARM64架构"CONFIG_ARCH_ROCKCHIP"],"compilerPath": "/usr/bin/aarch64-linux-gnu-gcc", // 交叉编译器"cStandard": "gnu11","cppStandard": "gnu++14","intelliSenseMode": "linux-gcc-arm64"}],"version": 4
}

setting.json:

{"search.exclude": {"**/node_modules": true,"**/bower_components": true,"**/*.o": true,"**/*.su": true,"**/*.cmd": true,"Documentation": true   },"files.exclude": {"**/.git": true,"**/.svn": true,"**/.hg": true,"**/CVS": true,"**/.DS_Store": true,"**/*.o": true,"**/*.su": true,"**/*.cmd": true,"Documentation": true   },"C_Cpp.intelliSenseEngineFallback": "Disabled","C_Cpp.intelliSenseEngine": "Tag Parser"
}

4.现象:

在这里插入图片描述
1为开灯0为关灯

在这里插入图片描述
在这里插入图片描述

5.额外补充:

由于最开始设备led灯会闪烁,应该是有这个驱动用echo 0 > /sys/class/leds/work/brightness命令关闭后再挂载驱动

一、open函数的特殊性:
内核默认实现,当驱动未显式实现file_operations结构体中的.open函数时,内核会自动提供一个默认的open函数实现。该默认实现仅完成以下操作:
分配文件描述符:在内核的文件描述符表中分配一个未使用的索引。
初始化文件对象:创建struct file对象并关联到设备的file_operations结构体。
权限校验:检查用户是否有权限访问设备(如设备文件的权限位)。
这解释了为什么用户层调用open(“/dev/led”, O_RDWR)即使驱动未实现.open也能成功返回文件描述符

open函数在驱动中属于可选实现,而write/read等函数则是必须实现的。如果驱动未实现write,用户层调用write会直接返回-EINVAL(无效操作)

open函数作用:

在这里插入图片描述
驱动的.open函数是设备初始化的入口,但不影响文件描述符的分配机制。文件描述符的返还是内核的默认行为,仅当驱动.open返回错误时,内核会撤销分配。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词