0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

设备树下的字符设备驱动框架

CHANBAEK 来源:嵌入式攻城狮 作者:安迪西 2023-04-14 11:35 次阅读

设备树下的字符设备驱动框架

没有引入设备树时,相关寄存器物理地址是直接定义在驱动文件中的,通过地址映射成为虚拟地址后,再操作虚拟地址完成GPIO的初始化。 设备树的本质也是操作寄存器,只不过寄存器的相关信息 放在了设备树中,配置寄存器时使用OF函数从设备树中读取寄存器数据后再进行配置

下图为设备树下的字符设备驱动框架图:

图片

接下来根据上面的框架图,以驱动LED (GPIO1_IO03)为例,分步介绍具体的代码编写流程

1. 修改设备树文件

在内核源码的/arch/arm/boot/dts/文件夹中复制一份官方I.MX6ULL EVK EMMC版的设备树文件imx6ull-14x14-evk-emmc.dts,并自定义文件名,此处重命名为了imx6ull-andyxi-emmc.dts,在根节点中添加LED设备节点

andyxiled {
    #address-cells = <1>;      /*reg中起始地址占用一个字长*/
    #size-cells = <1>;         /*reg中地址长度占用一个字长*/
    compatible = "andyxi-led";
    status = "okay";
    reg = < 0X020C406C 0x04    /*CCM_CCGR1_BASE*/ 
            0X020E0068 0x04    /*SW_MUX_GPIO1_IO03_BASE*/
            0X020E02F4 0x04    /*SW_PAD_GPIO1_IO03_BASE*/
            0X0209C000 0x04    /*GPIO1_DR_BASE*/
            0X0209C004 0x04 >; /*GPIO1_GDIR_BASE*/
};

设备树修改完成后,在内核源码的根目录下执行make命令编译设备树

make dtbs                       #编译设备树
make imx6ull-andyxi-emmc.dtb    #单独编译指定设备树

编译完成后,使用新的设备树启动Linux内核,之后可进入/proc/device-tree文件夹查看dtsled节点是否存在

#启动Linux系统后,在开发板中查看节点
cd /proc/device-tree         #查看andyxiled节点是否存在

2. 编写驱动程序

创建驱动程序文件dtsled.c,添加如下代码

宏定义及设备结构体定义

#define DTSLED_CNT 1            //设备号个数
#define DTSLED_NAME "dtsled"    //名字
#define LEDOFF 0                //关灯
#define LEDON 1                 //开灯
/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
/* 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;    //设备节点
};

struct dtsled_dev dtsled;      //led设备

编写设备操作函数:设备操作函数和LED开关函数,具体代码可参考Linux点灯一文相关部分

驱动入口函数中:使用OF函数获取设备树中的属性值,并初始化

static int __init led_init(void) { 
    u32 val = 0; 
    int ret; 
    u32 regdata[14]; 
    const char *str; 
    struct property *proper; 
    /* 获取设备树中的属性数据 */ 
    /* 1、获取设备节点:andyxiled */ 
    dtsled.nd = of_find_node_by_path("/andyxiled"); 
    if(dtsled.nd == NULL) { 
        printk("andyxiled node can not found!\\r\\n"); 
        return -EINVAL; 
    } else { 
        printk("andyxiled node has been found!\\r\\n"); 
    } 
    /* 2、获取compatible属性内容 */ 
    proper = of_find_property(dtsled.nd, "compatible", NULL); 
    if(proper == NULL) { 
        printk("compatible property find failed\\r\\n"); 
    } else { 
        printk("compatible = %s\\r\\n", (char*)proper->value); 
    }  
    /* 3、获取status属性内容 */ 
    ret = of_property_read_string(dtsled.nd, "status", &str); 
    if(ret < 0){ 
        printk("status read failed!\\r\\n"); 
    } else { 
        printk("status = %s\\r\\n",str); 
    }
    /* 4、获取reg属性内容 */ 
    ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10); 
    if(ret < 0) { 
        printk("reg property read failed!\\r\\n"); 
    } else { 
        u8 i = 0; 
        printk("reg data:\\r\\n"); 
        for(i = 0; i < 10; i++) 
        printk("%#X ", regdata[i]); 
        printk("\\r\\n"); 
    }

    /* 初始化LED */ 
#if 0 
    /* 1、寄存器地址映射 */ 
    IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]); 
    SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]); 
    SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]); 
    GPIO1_DR = ioremap(regdata[6], regdata[7]); 
    GPIO1_GDIR = ioremap(regdata[8], regdata[9]); 
#else 
    IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0); 
    SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1); 
    SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2); 
    GPIO1_DR = of_iomap(dtsled.nd, 3); 
    GPIO1_GDIR = of_iomap(dtsled.nd, 4); 
#endif 
    /* 2、使能GPIO1时钟 */ 
    val = readl(IMX6U_CCM_CCGR1); 
    val &= ~(3 << 26);     //之前的设置
    val |= (3 << 26);      //设置新值
    writel(val, IMX6U_CCM_CCGR1); 
    /* 3、设置GPIO1_IO03复用功能,并设置IO属性 */ 
    writel(5, SW_MUX_GPIO1_IO03); 
    writel(0x10B0, SW_PAD_GPIO1_IO03); 
    /* 4、设置GPIO1_IO03为输出功能 */ 
    val = readl(GPIO1_GDIR); 
    val &= ~(1 << 3);      //之前的设置 
    val |= (1 << 3);       //设置为输出
    writel(val, GPIO1_GDIR); 
    /* 5、默认关闭LED */ 
    val = readl(GPIO1_DR); 
    val |= (1 << 3); 
    writel(val, GPIO1_DR);

驱动入口函数中:注册字符设备驱动,代码与Linux点灯一文中的一样

驱动出口函数中:注销设备驱动,删除类和设备,代码可参考Linux点灯一文

3. 编写测序程序

实现操作驱动文件对外设进行控制的功能。 创建测试程序文件dtsledApp.c,代码内容与Linux点灯一文中的测试程序代码一致,此处不再赘述

4. 编译测试

编译驱动程序:当前目录下创建Makefile文件,并make编译

KERNELDIR := /home/andyxi/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_andyxi
CURRENT_PATH := $(shell pwd)
obj-m := dtsled.o

build: kernel_modules

kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

编译测试程序:无需内核参与,直接编译即可

arm-linux-gnueabihf-gcc dtsledApp.c -o dtsledApp

运行测试:拷贝驱动模块和测试程序到开发板,启动开发板,加载驱动模块后,使用应用程序测试驱动是否正常工作

depmod                         #第一次加载驱动的时候需运行此命令
modprobe dtsled.ko             #加载驱动
./dtsledApp /dev/dtsled 1      #打开LED灯
./dtsledApp /dev/dtsled 0      #关闭LED灯
rmmod dtsled.ko                #卸载驱动模块
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    31

    文章

    5292

    浏览量

    119802
  • 内核
    +关注

    关注

    3

    文章

    1360

    浏览量

    40183
  • 字符
    +关注

    关注

    0

    文章

    230

    浏览量

    25154
  • GPIO
    +关注

    关注

    16

    文章

    1188

    浏览量

    51828
  • 设备树
    +关注

    关注

    0

    文章

    38

    浏览量

    3105
收藏 人收藏

    评论

    相关推荐

    Linux平台设备框架驱动

      平台设备框架(platform)是将一个驱动分为设备层和驱动层两个部分,通过总线模型将设备
    的头像 发表于 09-25 08:59 1729次阅读
    Linux平台<b class='flag-5'>设备</b><b class='flag-5'>框架</b><b class='flag-5'>驱动</b>

    Linux字符设备驱动开发框架介绍

    字符设备是Linux驱动中最基本的一类设备驱动字符设备
    发表于 04-15 11:52 1312次阅读
    Linux<b class='flag-5'>字符</b><b class='flag-5'>设备</b><b class='flag-5'>驱动</b>开发<b class='flag-5'>框架</b>介绍

    ArmSoM系列板卡 嵌入式Linux驱动开发实战指南 之 字符设备驱动

    字符设备驱动 本章,我们将学习字符设备使用、字符设备
    的头像 发表于 04-10 09:53 985次阅读
    ArmSoM系列板卡 嵌入式Linux<b class='flag-5'>驱动</b>开发实战指南 之 <b class='flag-5'>字符</b><b class='flag-5'>设备</b><b class='flag-5'>驱动</b>

    「正点原子Linux连载」第五十五章设备树下的platform驱动编写

    学习一下如何在设备树下编写platform驱动。55.1设备树下的platform驱动简介pla
    发表于 03-21 10:03

    字符设备驱动 —— 字符设备驱动框架

      3、字符设备驱动框架作为字符设备驱动要素:1,必
    发表于 10-19 17:08

    什么是字符设备驱动字符设备驱动开发步骤有哪些?

    什么是字符设备驱动字符设备驱动开发步骤有哪些?如何实现实现
    发表于 10-20 08:02

    字符设备驱动的开发流程

    做嵌入式linux驱动开发,首先要搞明白大致框架。linux的驱动通常分为字符设备驱动、块
    发表于 12-24 08:30

    嵌入式Linux字符设备驱动的设计与应用

    描述了基于嵌入式Linux的字符设备驱动程序的设计方法和实现过程。以电机、数码管、串口和mini键盘的驱动设计为例,详细阐述了嵌入式linux下字符
    发表于 02-23 15:45 24次下载

    深度解析字符设备驱动模型

    ,read,write和ioctl等例程。所以根据应用不同,字符驱动能会调用其他驱动模块,如i2c、spi和v4l2等,于是字符驱动还可分W
    发表于 10-17 10:09 0次下载

    VxWorks设备驱动字符设备驱动详解

    VxWorks设备驱动字符设备驱动详解
    发表于 10-26 10:28 7次下载
    VxWorks<b class='flag-5'>设备</b><b class='flag-5'>驱动</b>之<b class='flag-5'>字符</b><b class='flag-5'>设备</b><b class='flag-5'>驱动</b>详解

    《Linux设备驱动开发详解》第6章、字符设备驱动

    《Linux设备驱动开发详解》第6章、字符设备驱动
    发表于 10-27 11:46 23次下载
    《Linux<b class='flag-5'>设备</b><b class='flag-5'>驱动</b>开发详解》第6章、<b class='flag-5'>字符</b><b class='flag-5'>设备</b><b class='flag-5'>驱动</b>

    你了解过Linux字符设备驱动框架

    字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符
    发表于 05-08 14:28 442次阅读

    驱动之路-高级字符设备驱动程序

    高级字符设备驱动在简单字符驱动的基础上添加ioctl方法、阻塞非阻塞读写、poll方法、和自动创建设备
    发表于 05-15 14:24 791次阅读
    <b class='flag-5'>驱动</b>之路-高级<b class='flag-5'>字符</b><b class='flag-5'>设备</b><b class='flag-5'>驱动</b>程序

    字符设备驱动框架

    1、概述: linux中一切皆文件,设备也如此,并且以操作文件即文件IO的方式访问设备。 应用程序只能通过库函数中的系统调用来操作硬件,对于每个系统调用,驱动程序中都会有一个与之对应的函数,对于
    发表于 10-19 17:20 705次阅读

    Linux新字符设备驱动开发方式

    Linux字符设备驱动开发模板中介绍了旧版本的驱动开发模板,其需要手动分配设备号后,再进行注册,驱动
    的头像 发表于 04-14 12:02 834次阅读
    Linux新<b class='flag-5'>字符</b><b class='flag-5'>设备</b><b class='flag-5'>驱动</b>开发方式