完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1)实验平台:正点原子Linux开发板
2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南》关注官方微信号公众号,获取更多资料:正点原子 54.3硬件原理图分析本章实验我们只使用到IMX6U-ALPHA开发板上的LED灯,因此实验硬件原理图参考8.3小节即可。 54.4试验程序编写本实验对应的例程路径为:开发板光盘->2、Linux驱动例程->17_platform。 本章实验我们需要编写一个驱动模块和一个设备模块,其中驱动模块是platform驱动程序,设备模块是platform的设备信息。当这两个模块都加载成功以后就会匹配成功,然后platform驱动模块中的probe函数就会执行,probe函数中就是传统的字符设备驱动那一套。 54.4.1platform设备与驱动程序编写新建名为“17_platform”的文件夹,然后在17_platform文件夹里面创建vscode工程,工作区命名为“platform”。新建名为leddevice.c和leddriver.c这两个文件,这两个文件分别为LED灯的platform设备文件和LED灯的platform的驱动文件。在leddevice.c中输入如下所示内容: 示例代码54.4.1.1 leddevice.c文件代码段 1 #include <linux/types.h> 2 #include <linux/kernel.h> 3 #include <linux/delay.h> 4 #include <linux/ide.h> 5 #include <linux/init.h> 6 #include <linux/module.h> 7 #include <linux/errno.h> 8 #include <linux/gpio.h> 9 #include <linux/cdev.h> 10 #include <linux/device.h> 11 #include <linux/of_gpio.h> 12 #include <linux/semaphore.h> 13 #include <linux/timer.h> 14 #include <linux/irq.h> 15 #include <linux/wait.h> 16 #include <linux/poll.h> 17 #include <linux/fs.h> 18 #include <linux/fcntl.h> 19 #include <linux/platform_device.h> 20 #include <asm/mach/map.h> 21 #include <asm/uaccess.h> 22 #include <asm/io.h> 23/*************************************************************** 24 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 25文件名 : leddevice.c 26作者 : 左忠凯 27版本 : V1.0 28描述 : platform设备 29其他 : 无 30论坛 : www.openedv.com 31日志 : 初版V1.0 2019/8/13 左忠凯创建 32 ***************************************************************/ 33 34/* 35 * 寄存器地址定义 36 */ 37 #define CCM_CCGR1_BASE (0X020C406C) 38 #define SW_MUX_GPIO1_IO03_BASE (0X020E0068) 39 #define SW_PAD_GPIO1_IO03_BASE (0X020E02F4) 40 #define GPIO1_DR_BASE (0X0209C000) 41 #define GPIO1_GDIR_BASE (0X0209C004) 42 #define REGISTER_LENGTH 4 43 44/* @description : 释放flatform设备模块的时候此函数会执行 45 * @param - dev : 要释放的设备 46 * @Return : 无 47 */ 48staticvoid led_release(struct device *dev) 49{ 50 printk("led device released!rn"); 51} 52 53/* 54 * 设备资源信息,也就是LED0所使用的所有寄存器 55 */ 56staticstruct resource led_resources[]={ 57[0]={ 58.start = CCM_CCGR1_BASE, 59.end =(CCM_CCGR1_BASE + REGISTER_LENGTH -1), 60.flags = IORESOURCE_MEM, 61}, 62[1]={ 63.start = SW_MUX_GPIO1_IO03_BASE, 64.end =(SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH -1), 65.flags = IORESOURCE_MEM, 66}, 67[2]={ 68.start = SW_PAD_GPIO1_IO03_BASE, 69.end =(SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH -1), 70.flags = IORESOURCE_MEM, 71}, 72[3]={ 73.start = GPIO1_DR_BASE, 74.end =(GPIO1_DR_BASE + REGISTER_LENGTH -1), 75.flags = IORESOURCE_MEM, 76}, 77[4]={ 78.start = GPIO1_GDIR_BASE, 79.end =(GPIO1_GDIR_BASE + REGISTER_LENGTH -1), 80.flags = IORESOURCE_MEM, 81}, 82}; 83 84 85/* 86 * platform设备结构体 87 */ 88staticstruct platform_device leddevice ={ 89.name ="imx6ul-led", 90.id =-1, 91.dev ={ 92.release =&led_release, 93}, 94.num_resources = ARRAY_SIZE(led_resources), 95.resource = led_resources, 96}; 97 98/* 99 * @description : 设备模块加载 100 * @param : 无 101 * @return : 无 102 */ 103staticint __init leddevice_init(void) 104{ 105return platform_device_register(&leddevice); 106} 107 108/* 109 * @description : 设备模块注销 110 * @param : 无 111 * @return : 无 112 */ 113staticvoid __exit leddevice_exit(void) 114{ 115 platform_device_unregister(&leddevice); 116} 117 118 module_init(leddevice_init); 119 module_exit(leddevice_exit); 120 MODULE_LICENSE("GPL"); 121 MODULE_AUTHOR("zuozhongkai"); leddevice.c文件内容就是按照示例代码54.2.3.4的platform设备模板编写的。 第56~82行,led_resources数组,也就是设备资源,描述了LED所要使用到的寄存器信息,也就是IORESOURCE_MEM资源。 第88~96,platform设备结构体变量leddevice,这里要注意name字段为“imx6ul-led”,所以稍后编写platform驱动中的name字段也要为“imx6ul-led”,否则设备和驱动匹配失败。 第103~106行,设备模块加载函数,在此函数里面通过platform_device_register向Linux内核注册leddevice这个platform设备。 第113~116行,设备模块卸载函数,在此函数里面通过platform_device_unregister从Linux内核中删除掉leddevice这个platform设备。 leddevice.c文件编写完成以后就编写leddriver.c这个platform驱动文件,在leddriver.c里面输入如下内容: 示例代码54.4.1.2 leddriver.c文件代码段 1 #include <linux/types.h> 2 #include <linux/kernel.h> 3 #include <linux/delay.h> 4 #include <linux/ide.h> 5 #include <linux/init.h> 6 #include <linux/module.h> 7 #include <linux/errno.h> 8 #include <linux/gpio.h> 9 #include <linux/cdev.h> 10 #include <linux/device.h> 11 #include <linux/of_gpio.h> 12 #include <linux/semaphore.h> 13 #include <linux/timer.h> 14 #include <linux/irq.h> 15 #include <linux/wait.h> 16 #include <linux/poll.h> 17 #include <linux/fs.h> 18 #include <linux/fcntl.h> 19 #include <linux/platform_device.h> 20 #include <asm/mach/map.h> 21 #include <asm/uaccess.h> 22 #include <asm/io.h> 23/*************************************************************** 24 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 25文件名 : leddriver.c 26作者 : 左忠凯 27版本 : V1.0 28描述 : platform驱动 29其他 : 无 30论坛 : www.openedv.com 31日志 : 初版V1.0 2019/8/13 左忠凯创建 32 ***************************************************************/ 33 34 #define LEDDEV_CNT 1 /* 设备号长度 */ 35 #define LEDDEV_NAME "platled" /* 设备名字 */ 36 #define LEDOFF 0 37 #define LEDON 1 38 39/* 寄存器名 */ 40staticvoid __iomem *IMX6U_CCM_CCGR1; 41staticvoid __iomem *SW_MUX_GPIO1_IO03; 42staticvoid __iomem *SW_PAD_GPIO1_IO03; 43staticvoid __iomem *GPIO1_DR; 44staticvoid __iomem *GPIO1_GDIR; 45 46/* leddev设备结构体 */ 47struct leddev_dev{ 48 dev_t devid; /* 设备号 */ 49struct cdev cdev; /* cdev */ 50struct class *class; /* 类 */ 51struct device *device; /* 设备 */ 52int major; /* 主设备号 */ 53}; 54 55struct leddev_dev leddev; /* led设备 */ 56 57/* 58 * @description : LED打开/关闭 59 * @param - sta : LEDON(0) 打开LED,LEDOFF(1) 关闭LED 60 * @return : 无 61 */ 62void led0_switch(u8 sta) 63{ 64 u32 val =0; 65if(sta == LEDON){ 66 val = readl(GPIO1_DR); 67 val &=~(1<<3); 68 writel(val, GPIO1_DR); 69}elseif(sta == LEDOFF){ 70 val = readl(GPIO1_DR); 71 val|=(1<<3); 72 writel(val, GPIO1_DR); 73} 74} 75 76/* 77 * @description : 打开设备 78 * @param – inode : 传递给驱动的inode 79 * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量 80 * 一般在open的时候将private_data指向设备结构体。 81 * @return : 0 成功;其他失败 82 */ 83staticint led_open(struct inode *inode,struct file *filp) 84{ 85 filp->private_data =&leddev;/* 设置私有数据 */ 86return0; 87} 88 89/* 90 * @description : 向设备写数据 91 * @param – filp : 设备文件,表示打开的文件描述符 92 * @param - buf : 要写给设备写入的数据 93 * @param - cnt : 要写入的数据长度 94 * @param - offt : 相对于文件首地址的偏移 95 * @return : 写入的字节数,如果为负值,表示写入失败 96 */ 97static ssize_t led_write(struct file *filp,constchar __user *buf, size_t cnt, loff_t *offt) 98{ 99int retvalue; 100unsignedchar databuf[1]; 101unsignedchar ledstat; 102 103 retvalue = copy_from_user(databuf, buf, cnt); 104if(retvalue <0){ 105return-EFAULT; 106} 107 108 ledstat = databuf[0]; /* 获取状态值 */ 109if(ledstat == LEDON){ 110 led0_switch(LEDON); /* 打开LED灯 */ 111}elseif(ledstat == LEDOFF){ 112 led0_switch(LEDOFF); /* 关闭LED灯 */ 113} 114return0; 115} 116 117/* 设备操作函数 */ 118staticstruct file_operations led_fops ={ 119.owner = THIS_MODULE, 120.open = led_open, 121.write = led_write, 122}; 123 124/* 125 * @description : flatform驱动的probe函数,当驱动与设备 126 * 匹配以后此函数就会执行 127 * @param - dev : platform设备 128 * @return : 0,成功;其他负值,失败 129 */ 130staticint led_probe(struct platform_device *dev) 131{ 132int i =0; 133int ressize[5]; 134 u32 val =0; 135struct resource *ledsource[5]; 136 137 printk("led driver and device has matched!rn"); 138/* 1、获取资源 */ 139for(i =0; i <5; i++){ 140 ledsource[i]= platform_get_resource(dev, IORESOURCE_MEM, i); 141if(!ledsource[i]){ 142 dev_err(&dev->dev,"No MEM resource for always onn"); 143return-ENXIO; 144} 145 ressize[i]= resource_size(ledsource[i]); 146} 147 148/* 2、初始化LED */ 149/* 寄存器地址映射 */ 150 IMX6U_CCM_CCGR1 = ioremap(ledsource[0]->start, ressize[0]); 151 SW_MUX_GPIO1_IO03 = ioremap(ledsource[1]->start, ressize[1]); 152 SW_PAD_GPIO1_IO03 = ioremap(ledsource[2]->start, ressize[2]); 153 GPIO1_DR = ioremap(ledsource[3]->start, ressize[3]); 154 GPIO1_GDIR = ioremap(ledsource[4]->start, ressize[4]); 155 156 val = readl(IMX6U_CCM_CCGR1); 157 val &=~(3<<26); /* 清除以前的设置 */ 158 val |=(3<<26); /* 设置新值 */ 159 writel(val, IMX6U_CCM_CCGR1); 160 161/* 设置GPIO1_IO03复用功能,将其复用为GPIO1_IO03 */ 162 writel(5, SW_MUX_GPIO1_IO03); 163 writel(0x10B0, SW_PAD_GPIO1_IO03); 164 165/* 设置GPIO1_IO03为输出功能 */ 166 val = readl(GPIO1_GDIR); 167 val &=~(1<<3); /* 清除以前的设置 */ 168 val |=(1<<3); /* 设置为输出 */ 169 writel(val, GPIO1_GDIR); 170 171/* 默认关闭LED1 */ 172 val = readl(GPIO1_DR); 173 val |=(1<<3); 174 writel(val, GPIO1_DR); 175 176/* 注册字符设备驱动 */ 177/*1、创建设备号 */ 178if(leddev.major){ /* 定义了设备号 */ 179 leddev.devid = MKDEV(leddev.major,0); 180 register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME); 181}else{ /* 没有定义设备号 */ 182 alloc_chrdev_region(&leddev.devid,0, LEDDEV_CNT, LEDDEV_NAME); 183 leddev.major = MAJOR(leddev.devid); 184} 185 186/* 2、初始化cdev */ 187 leddev.cdev.owner = THIS_MODULE; 188 cdev_init(&leddev.cdev,&led_fops); 189 190/* 3、添加一个cdev */ 191 cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT); 192 193/* 4、创建类 */ 194 leddev.class = class_create(THIS_MODULE, LEDDEV_NAME); 195if(IS_ERR(leddev.class)){ 196return PTR_ERR(leddev.class); 197} 198 199/* 5、创建设备 */ 200 leddev.device = device_create(leddev.class,NULL, leddev.devid, NULL, LEDDEV_NAME); 201if(IS_ERR(leddev.device)){ 202return PTR_ERR(leddev.device); 203} 204 205return0; 206} 207 208/* 209 * @description :移除platform驱动的时候此函数会执行 210 * @param - dev : platform设备 211 * @return : 0,成功;其他负值,失败 212 */ 213staticint led_remove(struct platform_device *dev) 214{ 215 iounmap(IMX6U_CCM_CCGR1); 216 iounmap(SW_MUX_GPIO1_IO03); 217 iounmap(SW_PAD_GPIO1_IO03); 218 iounmap(GPIO1_DR); 219 iounmap(GPIO1_GDIR); 220 221 cdev_del(&leddev.cdev); /* 删除cdev */ 222 unregister_chrdev_region(leddev.devid, LEDDEV_CNT); 223 device_destroy(leddev.class, leddev.devid); 224 class_destroy(leddev.class); 225return0; 226} 227 228/* platform驱动结构体 */ 229staticstruct platform_driver led_driver ={ 230.driver ={ 231.name ="imx6ul-led",/* 驱动名字,用于和设备匹配 */ 232}, 233.probe = led_probe, 234.remove = led_remove, 235}; 236 237/* 238 * @description : 驱动模块加载函数 239 * @param : 无 240 * @return : 无 241 */ 242staticint __init leddriver_init(void) 243{ 244return platform_driver_register(&led_driver); 245} 246 247/* 248 * @description : 驱动模块卸载函数 249 * @param : 无 250 * @return : 无 251 */ 252staticvoid __exit leddriver_exit(void) 253{ 254 platform_driver_unregister(&led_driver); 255} 256 257 module_init(leddriver_init); 258 module_exit(leddriver_exit); 259 MODULE_LICENSE("GPL"); 260 MODULE_AUTHOR("zuozhongkai"); leddriver.c文件内容就是按照示例代码54.2.2.5的platform驱动模板编写的。 第34~122行,传统的字符设备驱动。 第130~206行,probe函数,当设备和驱动匹配以后此函数就会执行,当匹配成功以后会在终端上输出“led driver and device has matched!”这样语句。在probe函数里面初始化LED、注册字符设备驱动。也就是将原来在驱动加载函数里面做的工作全部放到probe函数里面完成。 第213~226行,remobe函数,当卸载platform驱动的时候此函数就会执行。在此函数里面释放内存、注销字符设备等。也就是将原来驱动卸载函数里面的工作全部都放到remove函数中完成。 第229~235行,platform_driver驱动结构体,注意name字段为"imx6ul-led",和我们在leddevice.c文件里面设置的设备name字段一致。 第242~245行,驱动模块加载函数,在此函数里面通过platform_driver_register向Linux内核注册led_driver驱动。 第252~255行,驱动模块卸载函数,在此函数里面通过platform_driver_unregister从Linux内核卸载led_driver驱动。 54.4.2 测试APP编写测试APP的内容很简单,就是打开和关闭LED灯,新建ledApp.c这个文件,然后在里面输入如下内容: 示例代码54.4.2.1 ledApp.c文件代码段 1 #include "stdio.h" 2 #include "unistd.h" 3 #include "sys/types.h" 4 #include "sys/stat.h" 5 #include "fcntl.h" 6 #include "stdlib.h" 7 #include "string.h" 8/*************************************************************** 9 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 10文件名 : ledApp.c 11作者 : 左忠凯 12版本 : V1.0 13描述 : platform驱动驱测试APP。 14其他 : 无 15使用方法 :./ledApp /dev/platled 0 关闭LED 16 ./ledApp /dev/platled 1 打开LED 17论坛 : www.openedv.com 18日志 : 初版V1.0 2019/8/16 左忠凯创建 19 ***************************************************************/ 20 #define LEDOFF 0 21 #define LEDON 1 22 23/* 24 * @description : main主程序 25 * @param - argc : argv数组元素个数 26 * @param - argv : 具体参数 27 * @return : 0 成功;其他失败 28 */ 29int main(int argc,char*argv[]) 30{ 31 int fd, retvalue; 32 char*filename; 33 unsignedchar databuf[2]; 34 35 if(argc !=3){ 36 printf("Error Usage!rn"); 37 return-1; 38 } 39 40 filename = argv[1]; 41 /* 打开led驱动 */ 42 fd = open(filename, O_RDWR); 43 if(fd <0){ 44 printf("file %s open failed!rn", argv[1]); 45 return-1; 46 } 47 48 databuf[0]= atoi(argv[2]);/* 要执行的操作:打开或关闭 */ 49 retvalue = write(fd, databuf,sizeof(databuf)); 50 if(retvalue <0){ 51 printf("LED Control Failed!rn"); 52 close(fd); 53 return-1; 54 } 55 56 retvalue = close(fd);/* 关闭文件 */ 57 if(retvalue <0){ 58 printf("file %s close failed!rn", argv[1]); 59 return-1; 60 } 61 return0; 62} ledApp.c文件内容很简单,就是控制LED灯的亮灭,和第四十一章的测试APP基本一致,这里就不重复讲解了。 54.5 运行测试54.5.1 编译驱动程序和测试APP1、编译驱动程序 编写Makefile文件,本章实验的Makefile文件和第四十章实验基本一样,只是将obj-m变量的值改为“leddevice.o leddriver.o”,Makefile内容如下所示: 示例代码54.5.1.1 Makefile文件 1 KERNELDIR:= /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek ...... 4 obj-m := leddevice.o leddriver.o ...... 11 clean: 12$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean 第4行,设置obj-m变量的值为“leddevice.o leddriver.o”。 输入如下命令编译出驱动模块文件: make-j32 编译成功以后就会生成一个名为“leddevice.ko leddriver.ko”的驱动模块文件。 2、编译测试APP 输入如下命令编译测试ledApp.c这个测试程序: arm-linux-gnueabihf-gccledApp.c -o ledApp 编译成功以后就会生成ledApp这个应用程序。 54.4.2 运行测试将上一小节编译出来leddevice.ko、leddriver.ko和ledApp这两个文件拷贝到rootfs/lib/modules/4.1.15目录中,重启开发板,进入到目录lib/modules/4.1.15中,输入如下命令加载leddevice.ko设备模块和leddriver.ko这个驱动模块。 depmod //第一次加载驱动的时候需要运行此命令 modprobe leddevice.ko //加载设备模块 modprobeleddriver.ko //加载驱动模块 根文件系统中/sys/bus/platform/目录下保存着当前板子platform总线下的设备和驱动,其中devices子目录为platform设备,drivers子目录为plartofm驱动。查看/sys/bus/platform/devices/目录,看看我们的设备是否存在,我们在leddevice.c中设置leddevice(platform_device类型)的name字段为“imx6ul-led”,也就是设备名字为imx6ul-led,因此肯定在/sys/bus/platform/devices/目录下存在一个名字“imx6ul-led”的文件,否则说明我们的设备模块加载失败,结果如图54.4.2.1所示: 图54.4.2.1 imx6ul-led设备 同理,查看/sys/bus/platform/drivers/目录,看一下驱动是否存在,我们在leddriver.c中设置led_driver(platform_driver类型)的name字段为“imx6ul-led”,因此会在/sys/bus/platform/drivers/目录下存在名为“imx6ul-led”这个文件,结果如图54.4.2.2所示: 图54.4.2.2 imx6ul-led驱动 驱动模块和设备模块加载成功以后platform总线就会进行匹配,当驱动和设备匹配成功以后就会输出如图54.4.2.3所示一行语句: 图54.4.2.3 驱动和设备匹配成功 驱动和设备匹配成功以后就可以测试LED灯驱动了,输入如下命令打开LED灯: ./ledApp /dev/platled 1 //打开LED灯 在输入如下命令关闭LED灯: ./ledApp /dev/platled 0 //关闭LED灯 观察一下LED灯能否打开和关闭,如果可以的话就说明驱动工作正常,如果要卸载驱动的话输入如下命令即可: rmmod leddevice.ko rmmodleddriver.ko |
|
相关推荐
|
|
590 浏览 0 评论
AI模型部署边缘设备的奇妙之旅:如何在边缘端部署OpenCV
2241 浏览 0 评论
tms320280021 adc采样波形,为什么adc采样频率上来波形就不好了?
1233 浏览 0 评论
1788 浏览 0 评论
1464 浏览 0 评论
74826 浏览 21 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 03:30 , Processed in 0.979129 second(s), Total 63, Slave 46 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号