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

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); //最低三位置0
    val |= ((0x7 << 16) | (0x0 << 0)); // 16 17 18位置1其他不变,bit2:0:0,用作GPIO0_C0
    writel(val, PMU_GRF_GPIO0C_IOMUX_L_PI);

    // 设置GPIO_C0驱动能力为level5
    val = readl(PMU_GRF_GPIO0C_DS_0_PI);
    val &= ~(0x3f << 0);  // 0 ~ 5置0
    val |= ((0x3f << 16) | (0x3f << 0)); // 16 ~ 21置1,0~5置1同时用作GPIO0c0
    writel(val, PMU_GRF_GPIO0C_DS_0_PI);

    // 设置GPIOO0_c0为输出
    val = readl(GPIO0_SWPORT_DDR_H_PI);
    val &= ~(0x1 << 0); // 0置0
    val |= ((0x1 << 16) | (0x1 << 0)); // 16置1,0置1
    writel(val, GPIO0_SWPORT_DDR_H_PI);

    // 设置GPIO_c0为低电平,关闭LED
    val = 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返回错误时,内核会撤销分配。


http://www.kler.cn/a/568380.html

相关文章:

  • Windows权限维持之不死马(一)
  • Python 数据结构 4.单向链表
  • PHP:IDEA开发工具配置XDebug,断点调试
  • Android Coil3配置Application单例ImageLoader,Kotlin
  • upload
  • python流水线自动化项目教程
  • 从零开始开发纯血鸿蒙应用之语音朗读
  • Python核心技术,Django学习基础入门教程(附环境安装包)
  • 国自然面上项目|基于多模态MR影像的胶质母细胞瘤高危区域定位及预后预测研究|基金申请·25-02-28
  • LINUX基础 - 网络基础 [一]
  • 【ComfyUI】[进阶工作流] 高级采样器与Refiner的工作流优化
  • 6.6.5 SQL访问控制
  • 鸿蒙启动页开发
  • 【音视频】SIP(推流中涉及SIP信息分析)
  • 【软路由】ImmortalWrt 编译指南:从入门到精通
  • SQL Server 视图的更新排查及清除缓存
  • agent实现路径规划
  • ZYNQ-PL实践课堂(三)IP核之MMCM/PLL
  • 想学大模型,但分不清longchain,huggingface,ollama各种工具之间区别?
  • 机器学习的三个基本要素