fmql之字符驱动设备(2)

news2024/9/27 7:30:13

例行的点灯来喽。

之前是寄存器读写,现在要学习通过设备树点灯。

dtsled.c

寄存器写在reg

把用到的寄存器写在设备树的led节点的reg属性。

其实还是对寄存器的读写。 (不推荐)

头文件

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/cdev.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_address.h>
#include <linux/of.h>

#define DTSLED_NAME   "dtsled"            /* class_create */
#define DTSLED_COUNT     1                // 申请设备号的数量

#define LEDOFF      0       /* led off */
#define LEDON       1       /* led on  */

/*  物理地址  */
#define GPIO_C_DR_BASE       (0xE0003200)    // data
#define GPIO_C_DDR_BASE      (0xE0003204)    // direction
#define GPIO_C_INTEN_BASE    (0xE0003230)    // interrup enable

/* 地址映射后的虚拟指针 */
static void __iomem *FMQL_GPIO_C_DR;
static void __iomem *FMQL_GPIO_C_DDR;
static void __iomem *FMQL_GPIO_C_INTEN;

设备结构体★

/* 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;     /* 设备节点*/
};
static struct dtsled_dev dtsled;    /* led device */

操作函数

static int dtsled_open(struct inode * inode, struct file * filp){
    filp -> private_data = &dtsled; /* 设置私有数据 */
    return 0;
}

static ssize_t dtsled_read(struct file * filp, char __user * buf,
       size_t cnt, loff_t *offt){
    return 0;
}

static int dtsled_release(struct inode * inode, struct file * filp){
    return 0;
}
static ssize_t dtsled_write(struct file * filp, const char __user * buf,
       size_t cnt, loff_t *offt){
    int ret;
    int val;
    char kern_databuf[1];

    ret = copy_from_user(kern_databuf, buf, cnt);   /* 得到应用层传递过来的数据 */
    if(ret < 0){
        printk(KERN_ERR "kernel write failed!\r\n");
        return -EFAULT;
    }

    val = readl(FMQL_GPIO_C_DR);
    if(kern_databuf[0] == 1){
        val |= (1 << 5);       // led on
    } else if(kern_databuf[0] == 0){
        val &= ~(1 << 5);       // led off   
    }
    writel(val, FMQL_GPIO_C_DR);

    return 0;
}
/*  dtsled 设备操作函数 */
static struct file_operations dtsled_ops = {
    .owner = THIS_MODULE,
    .read = dtsled_read,
    .write = dtsled_write,
    .open = dtsled_open,
    .release = dtsled_release,
};

 地址映射

/* 地址映射 */          
// of_iomap(dtsled.nd, num)     // num为设备树定义的reg属性
// 因为我的设备树文件里,led节点下没有reg属性,所以就按之前的方式写
static inline void led_ioremap(void){
    FMQL_GPIO_C_DR = ioremap(GPIO_C_DR_BASE, 4);
    FMQL_GPIO_C_DDR = ioremap(GPIO_C_DDR_BASE, 4);
    FMQL_GPIO_C_INTEN = ioremap(GPIO_C_INTEN_BASE, 4);
}

/* 取消地址映射 */
static inline void led_iounmap(void)
{
    iounmap(FMQL_GPIO_C_DR);
    iounmap(FMQL_GPIO_C_DDR);
    iounmap(FMQL_GPIO_C_INTEN);
}

驱动模块注册/卸载

/* 驱动模块注册和卸载 */
static int __init dtsled_init(void)
{
    const char *str;
    u32 val = 0;
    int ret = 0;
    printk(KERN_EMERG "dtsled init\r\n");

    /* 获取led设备节点 */
    dtsled.nd = of_find_node_by_path("/leds");
    if (NULL == dtsled.nd) {
        printk(KERN_ERR "dtsled: can't find node\n");
        return -ENOENT;
    }

    /* 获取status属性 */
    ret = of_property_read_string(dtsled.nd, "status", &str);
    if (!str || strcmp(str, "okay")) {
        printk(KERN_ERR "dtsled: status is not okay\n");
        return -ENOENT;
    }

    /* 读取compatible属性并进行匹配 */
    //str = of_get_property(dtsled.nd, "compatible", NULL);
    ret = of_property_read_string(dtsled.nd, "compatible", &str);
    if (!str || strcmp(str, "gpio-leds")) {
        printk(KERN_ERR "dtsled: compatible is not gpio-leds\n");
        return -ENOENT;
    }
    printk(KERN_ERR "dtsled: successful\r\n");

    /* 寄存器地址映射 */
    led_ioremap();
    /* 初始化 */
    val = readl(FMQL_GPIO_C_DDR);
    val |= (1 << 5);       // 1: output
    writel(val, FMQL_GPIO_C_DDR);

    val = readl(FMQL_GPIO_C_DR);
    ret = of_property_read_string(dtsled.nd, "default-state", &str);
    if(!ret){
        if(!strcmp(str, "on"))
           val |= (1 << 5);          // 1: led on
        else val &= ~(1 << 5);       // 0: led off
    } else   val &= ~(1 << 5); 
    writel(val, FMQL_GPIO_C_DR);

    /* 注册字符设备驱动 */      // 设备号,cdev,class,devie
#if 1       // 设备号
    if(dtsled.major){
        dtsled.devid = MKDEV(dtsled.major, 0);
        ret = register_chrdev_region(dtsled.devid, DTSLED_COUNT, DTSLED_NAME);
        if(ret)
            goto out1;
    } else {
        ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_COUNT, DTSLED_NAME);
        if(ret)
            goto out1;
        dtsled.major = MAJOR(dtsled.devid);
        dtsled.minor = MINOR(dtsled.devid);
    }
    printk(KERN_EMERG "dtsled major = %d, minor = %d\r\n", dtsled.major, dtsled.minor);
#endif    

#if 1       // cdev
    cdev_init(&dtsled.cdev, &dtsled_ops);
    dtsled.cdev.owner = THIS_MODULE;
    ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_COUNT);
    if(ret)
        goto out2;
#endif 

#if 1       // class
    dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
    if(IS_ERR(dtsled.class)) {
        ret = PTR_ERR(dtsled.class);
        goto out3;
    }
#endif

#if 1       // device
    dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);
    if(IS_ERR(dtsled.device)) {
        ret = PTR_ERR(dtsled.device);
        goto out4;
    }
#endif

    return 0;

#if 1       // goto
out4:
    class_destroy(dtsled.class);
out3:
    cdev_del(&dtsled.cdev);
out2:
    unregister_chrdev_region(dtsled.devid, DTSLED_COUNT);
out1:
    led_iounmap();
    return ret;
#endif
}
static void __exit dtsled_exit(void)
{
    u32 val = 0;
    printk(KERN_EMERG "dtsled exit\r\n");
    /* 灭灯 */
    val = readl(FMQL_GPIO_C_DR);
    val &= ~(1 << 5);       // 0: led off
    writel(val, FMQL_GPIO_C_DR);
    
    /* 注销字符驱动设备 */      // device, class, cdev, 设备号
    device_destroy(dtsled.class, dtsled.devid);
    class_destroy(dtsled.class);
    cdev_del(&dtsled.cdev);
    unregister_chrdev_region(dtsled.devid, DTSLED_COUNT);

    /* 取消地址映射 */
    led_iounmap();
}
module_init(dtsled_init);
module_exit(dtsled_exit);

MODULE_AUTHOR("Skylar");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("FMQL DTS LED dev");

 测试程序APP

沿用之前的ledAPP.c

gpioled.c

不用寄存器读写,采用设备树的方式。

 gpio子系统

#define GPIO_ACTIVE_HIGH   0
#define GPIO_ACTIVE_LOW    1

fmql中对gpio的节点描述为:(在amba@0父节点下)

gpio0,也为porta,是MIO[31:0](porta定义了snps,nr-gpios 为引脚数量)

同理,gpio1 = portb =   MIO[53:32]

           gpio2 = portc = EMIO[85:54]

           gpio3 = portd = EMIO[117:86]

我用的是引脚59,即portc的MIO[5] :

 gpio API函数

(来自正点原子pdf)

gpio of函数

pinctrl子系统

代码

在dtsled.c的基础上进行修改。

设备树不需要reg属性了,因为不直接对寄存器进行操作。

led-gpio属性是为了获取gpio编号

#include

记得添加:(不然没有of_gpio相关的函数)

#include <linux/of_gpio.h>

 设备结构体

/* dtsled设备结构体 */
struct gpioled_dev {
    dev_t devid;			/* 设备号 */
    struct cdev cdev;		/* cdev */
    struct class *class;		/* 类 */
    struct device *device;	/* 设备 */
    int major;				/* 主设备号 */
    int minor;			/* 次设备号 */
    struct device_node *nd;	/* 设备节点 */
    int led_gpio;			/* LED所使用的GPIO编号 */
};

和dtsled.c相比,多了最后的led_gpio。

驱动模块注册

 __init函数多的步骤:

读取设备节点,以及节点属性(status,compatible)后,

多了读取led-gpio属性。然后向gpio子系统申请使用gpio、管脚设置为output。

    /* 4.获取设备树中的led-gpio属性,得到LED所使用的GPIO编号 */
    gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
    if(!gpio_is_valid(gpioled.led_gpio)) {
        printk(KERN_ERR "gpioled: Failed to get led-gpio\n");
        return -EINVAL;
    }
    printk(KERN_INFO "gpioled: led-gpio num = %d\r\n", gpioled.led_gpio);

    /* 5.向gpio子系统申请使用GPIO */
    ret = gpio_request(gpioled.led_gpio, "LED-GPIO");
    if (ret) {
        printk(KERN_ERR "gpioled: Failed to request led-gpio\n");
        return ret;
    }

    /* 6.将led gpio管脚设置为输出模式 */
    gpio_direction_output(gpioled.led_gpio, 0);

后面的内容就一样了:初始化led,注册字符设备驱动(设备号,cdev,class,device)

__exit函数多了一步,就是最后的释放gpio

static void __exit led_exit(void)
{
    /* 注销设备 */
    device_destroy(gpioled.class, gpioled.devid);

    /* 注销类 */
    class_destroy(gpioled.class);

    /* 删除cdev */
    cdev_del(&gpioled.cdev);

    /* 注销设备号 */
    unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);

    /* 释放GPIO */
    gpio_free(gpioled.led_gpio);
}

运行

dtsled.c

(绝对路径)设备节点写错了 。

其实没错,我写的是“/leds/gpio-led3”。但是就是不行。

后来改成“/leds”就可以了。

(因为我leds下面只有一个gpio-led3子节点?所以二者等效?)

  • “/”是根节点
  • “/leds”表示根节点下,有名为“leds”的节点
  • 在我自己写的设备树文件中,leds作为父节点,有一个子节点gpio-led3
  • 查看正点原子linux开发pdf:

gpioled.c

修改了led的设备树,不用led3父节点下又有gpio-led节点了。现在根节点下,只有led3 :

led3 {
		compatible = "fmql,led";
		status = "okay";
		label = "led3";
		led-gpio = <&portc 5 GPIO_ACTIVE_HIGH>; // 59
		//linux,default-trigger = "timer";//or heartbeat
		default-state = "off";
};

运行测试:ok

获取设备节点的属性时,一定要记得设备节点以及属性的名称与设备树中写的一致,如status,compatible,led-gpio等

比如我这里寻找的是根节点下的名为”led3“的设备节点,但是设备树中写的是”leds“,就会failed。

再比如,我这里找的是节点的”led-gpio“属性,如果设备树中写的是”gpios“,则也会failed。

(以上均为本人调试过程中出现的问题)一定要照着设备树写!!!

其他 - vscode

修改vscode设置(因为tab键是4个空格而不是一个tab字符:

vscode怎么修改tab缩进 • Worktile社区

设置了但是不管用。。。

原来是修改这里就行:

vscode页面的右下角,”制表符长度:4“,这里原来是”空格“,点击”空格“, 修改为制表符缩进即可:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2169338.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-26

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-26 1. LLMs Still Can’t Plan; Can LRMs? A Preliminary Evaluation of OpenAI’s o1 on PlanBench Authors: Karthik Valmeekam, Kaya Stechly, Subbarao Kambhampati LLMs仍然无法规划&#xff1b;LRMs可以…

【通俗易懂】FFT求解全过程,各参数详细解释

在进行FFT全过程讲解之前&#xff0c;小编先给大家解释一下&#xff0c;在FFT中出现的一些参数名词解释。 &#xff08;1&#xff09;采样频率 Fs Fs 1 / 采样间隔 根据奈奎斯特定理&#xff1a;Fs ≥ 最高频率分量的两倍&#xff0c;这样才能避免混叠 &#xff08;2&…

解决macOS安装redis以后不支持远程链接的问题

参考文档:https://blog.csdn.net/qq_37703224/article/details/142542179?spm1001.2014.3001.5501 安装的时候有个提示, 使用指定配置启动: /opt/homebrew/opt/redis/bin/redis-server /opt/homebrew/etc/redis.conf那么我们可以尝试修改这个配置文件: code /opt/homebrew/…

傅里叶级数在机器人中的应用(动力学参数辨识)

B站首发&#xff01;草履虫都能看懂的【傅里叶变换】讲解&#xff0c;清华大学李永乐老师教你如何理解傅里叶变换&#xff0c;辨清美颜和变声原理&#xff0c;&#xff01;&#xff01;_哔哩哔哩_bilibiliB站首发&#xff01;草履虫都能看懂的【傅里叶变换】讲解&#xff0c;清…

AI 智能体 | 手捏素材选题库 Coze Bot,帮你实现无限输出

做自媒体的同学经常遇到的一个痛点就是无限输出&#xff0c;那怎么才能有源源不断的选题呢&#xff1f;那就是搭建一个选题素材库。 下面就为大家介绍一下基于 Coze Bot 快速搭建素材选题库&#xff0c;希望能让大家才思泉涌。 一、流程拆解 日常素材库积累的过程可以描述为…

eslint-plugin-react的使用中,所出现的react版本警告

记一次使用eslint-plugin-react的警告 Warning: React version not specified in eslint-plugin-react settings. See https://github.com/jsx-eslint/eslint-plugin-react#configuration . 背景 我们在工程化项目中&#xff0c;常常会通过eslint来约束我们代码的一些统一格…

汽车总线之----J1939总线

instruction SAE J1939 是由美国汽车工程协会制定的一种总线通信协议标准&#xff0c;广泛应用于商用车&#xff0c;船舶&#xff0c;农林机械领域中&#xff0c;J1939协议是基于CAN的高层协议&#xff0c;我们来看一下两者之间的关系。在J1939 中&#xff0c;物理层和数据链路…

第13讲 实践:设计SLAM系统

设计一个视觉里程计&#xff0c;理解SLAM软件框架如何搭建&#xff0c;理解视觉里程计设计容易出现的问题以及解决方法。 目录 1、工程目标 2、工程框架 3、实现 附录 1、工程目标 实现一个精简版的双目视觉里程计。由一个光流追踪的前端和一个局部BA的后端组成。 2、工程…

asp.net mvc core 路由约束,数据标记DataTokens

》从0自己搭建MVC 》用 asp.net Core web 应用 空web 应用程序 需要配置 mvc服务 、mvc路由 新建 Controller 、Models、Views 》》》core 6 之前版本 vs2022 asp.net Core Web 应用&#xff08;模型-视图-控制器&#xff09; 不需要配置 就是mvc框架 asp.net Core web 应…

从Elasticsearch到RedisSearch:探索更快的搜索引擎解决方案

文章目录 RedisSearch 的关键功能与 ElasticSearch 对比性能对比产品对比 如何使用 Docker 安装 RedisSearch1. 获取 RedisSearch Docker 镜像2. 启动 RedisSearch 容器3. 验证安装 RedisSearch 使用示例1. 连接到 RedisSearch2. 创建索引3. 添加文档4. 执行搜索搜索所有包含 &…

【Geoserver使用】2.26.0版本发布主要内容

文章目录 前言一、GeoServer 2.26.0 版本二、主要内容1.Java17支持2.Docker更新3.搜索改进4.广泛的 MapML 改进4.重写演示请求页面5.栅格属性表扩展6.GeoCSS 改进7.地球静止卫星 AUTO 代码8.labelPoint 功能改进9.改进的矢量图块生成10.GeoPackage QGIS 兼容性改进11.新的图像马…

深度学习—神经网络基本概念

一&#xff0c;神经元 1.生物神经元与人工神经元 1.1神经元是人脑的基本结构和功能单位之一。人脑中有数1000亿个神经元&#xff0c;其功能是接受&#xff08;树突&#xff09;&#xff0c;整合&#xff08;细胞体&#xff09;&#xff0c;传导&#xff08;轴突&#xff09;和…

MySQL --用户管理

文章目录 1.用户1.1用户信息1.2创建用户1.3删除用户1.4修改用户密码 2.数据库的权限2.1给用户授权2.2回收权限 如果我们只能使用root用户&#xff0c;这样存在安全隐患。这时&#xff0c;就需要使用MySQL的用户管理。 1.用户 1.1用户信息 MySQL中的用户&#xff0c;都存储在系…

Spring Cloud 教程(二) | 搭建SpringCloudAlibaba

Spring Cloud 教程&#xff08;二&#xff09; | 搭建SpringCloudAlibaba 前言一、SpringBoot 与 SpringCloud 版本对应关系&#xff1a;二、SpringCloud 与 SpringCloudAlibaba版本对应关系&#xff1a;三、SpringCloudAlibaba版本 与 组件版本 对应关系&#xff1a;四、搭建S…

Django项目配置日志

需求 在Django项目中实现控制台输出到日志文件&#xff0c;并且设置固定的大小以及当超过指定大小后覆盖最早的信息。 系统日志 使用Django自带的配置&#xff0c;可以自动记录Django的系统日志。 可以使用logging模块来配置。下面是一个完整的示例代码&#xff0c;展示了如…

Java | Leetcode Java题解之第438题找到字符串中所有字母异位词

题目&#xff1a; 题解&#xff1a; class Solution {public List<Integer> findAnagrams(String s, String p) {int sLen s.length(), pLen p.length();if (sLen < pLen) {return new ArrayList<Integer>();}List<Integer> ans new ArrayList<Int…

163页制造业变革转型:营销/服务/研发/供应链/制造/质量/财务

制造业企业要实现变革转型&#xff0c;可以从营销、服务、研发、供应链、制造、质量、劳务以及人力资源等多个方面着手&#xff1a; 一、营销 市场调研与定位 深入了解目标市场的需求、趋势和竞争态势。通过大数据分析、消费者调研等手段&#xff0c;精准把握市场动态&#…

管家婆分销费用分摊功能使用说明!

在入库和销售业务中&#xff0c;往往会产生一些费用需要和入库单和销售单关联&#xff0c;这时候就可以使用费用分摊的功能。下面我们一起来学习在分销A\V系列软件中费用分摊的操作步骤 一、入库费用分摊 1、只有移动加权平均成本算法才有入库费用分摊单&#xff1b;入库费用分…

centos7系统安装宝塔面板

1、开始安装 适用系统 Centos/OpenCloud/Alibaba 稳定版9.0.0 urlhttps://download.bt.cn/install/install_lts.sh;if [ -f /usr/bin/curl ];then curl -sSO $url;else wget -O install_lts.sh $url;fi;bash install_lts.sh ed8484bec等待命令执行&#xff0c;安装完成&#…

【理解 Java 中的 for 循环】

理解 Java 中的 for 循环 for 循环是 Java 中用于迭代的常用控制结构&#xff0c;它可以帮助我们重复执行某段代码&#xff0c;直到满足特定条件。本文将介绍 for 循环的基本语法、执行流程、注意事项及一些练习。 基本语法 for 循环的基本语法如下&#xff1a; for (循环变…