ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

字符设备驱动:LED平台总线实现方式

2021-10-14 21:34:47  阅读:159  来源: 互联网

标签:字符 gpio1 led GPIO 总线 imx device LED


1. 环境:

1.1 开发板:正点原子 I.MX6U ALPHA V2.2

1.2 开发PC:Ubuntu20.04

1.3 U-boot:uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

1.4 LInux内核:linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

1.5 rootfs:busybox-1.29.0.tar.bz2制作

1.6 交叉编译工具链:gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

 

2. 硬件控制说明

2.1 由GPIO1 PIN3控制,高---熄灭, 低---点亮

 

3. 驱动代码

3.1 新建led.h头文件,添加如下代码

 1 #ifndef __LED_H
 2 #define __LED_H
 3 #include <linux/cdev.h>
 4 
 5 //GPIO寄存器地址,参考"i.MX 6ULL Applications Processor Reference Manual"
 6 #define LED_GPIO1_3_BASE        0x0209c000
 7 #define GPIO_DR                 0x0
 8 #define GPIO_GDIR               0x4
 9 #define GPIO_PSR                0x8         //本驱动未使用到
10 #define GPIO_ICR1               0xc         //本驱动未使用到
11 #define GPIO_ICR2               0x10        //本驱动未使用到
12 #define GPIO_IMR                0x14        //本驱动未使用到
13 #define GPIO_ISR                0x18        //本驱动未使用到
14 #define GPIO_EDGE_SEL           0x1c        //本驱动未使用到
15 
16 #define LED_GPIO1_3_MUX         0X020E0068      //GPIO复用功能选择寄存器地址
17 #define LED_GPIO1_3_PAD         0X020E02F4      //GPIO驱动方式选择寄存器地址
18 #define LED_GPIO1_3_CCM         0X020C406C      //GPIO时钟寄存器地址
19 
20 //led控制
21 #define LED_ON                  1
22 #define LED_OFF                 0
23 #define LED_MAJOR               249     //用于静态设备号申请
24 
25 
26 //自定义一个结构体,将一些需要的变量放在其中,以便管理
27 struct led_device
28 {
29     struct class *led_class;
30     struct device *led_device;
31     struct cdev led_cdev;  
32     dev_t   devno;
33     int major;                   //用于保存主设备号
34     void __iomem *led_dr;        //保存GPIO数据寄存器内存映射后的地址
35     void __iomem *led_gdir;      //保存GPIO输入输出方向选择寄存器内存映射后的地址
36     void __iomem *led_pd;        //保存GPIO驱动方式选择寄存器内存映射后的地址
37     void __iomem *led_mux;       //保存GPIO复用功能选择寄存器内存映射后的地址
38     void __iomem *led_ccm;       //保存GPIO时钟寄存器内存映射后的地址
39 };
40 
41 
42 #endif
View Code

 

3.2 新建led_dev_platform.c文件,添加平台设备代码

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/ioport.h>
 4 #include <linux/platform_device.h>
 5 #include "led.h"
 6 
 7 //填充平台资源结构体
 8 struct resource led_resource[] = {
 9     [0] = {  //GPIO数据寄存器
10             .start  =   LED_GPIO1_3_BASE + GPIO_DR,
11             .end    =   LED_GPIO1_3_BASE + GPIO_DR + 4 -1,
12             .flags  =   IORESOURCE_MEM
13     },
14 
15     [1] = { //GPIO方向寄存器
16         .start  =    LED_GPIO1_3_BASE + GPIO_GDIR,
17         .end    =   LED_GPIO1_3_BASE + GPIO_GDIR + 4 -1,
18         .flags  =   IORESOURCE_MEM
19     },
20 
21     [2] = { //GPIO复用功能寄存器
22             .start  =   LED_GPIO1_3_MUX,
23             .end    =   LED_GPIO1_3_MUX + 4 -1,
24             .flags  =   IORESOURCE_MEM
25     },
26 
27     [3] = { //GPIO驱动方式寄存器
28         .start  =   LED_GPIO1_3_PAD,
29         .end    =   LED_GPIO1_3_PAD + 4 -1,
30         .flags  =   IORESOURCE_MEM
31     },
32 
33     [4] = { //GPIO时钟寄存器
34     .start  =   LED_GPIO1_3_CCM,
35     .end    =   LED_GPIO1_3_CCM + 4 -1,
36     .flags  =   IORESOURCE_MEM
37     },
38 
39 };
40 
41 
42 static void led_release(struct device *dev)
43 {
44 
45 }
46 
47 //填充平台结构体
48 struct platform_device led_gpio_device = {
49      .name           =   "led_gpio",
50   //  .name           =   "imx6ul-led",
51     .id             =   1,
52     .resource       =   led_resource,
53     .num_resources   =   ARRAY_SIZE(led_resource),
54     .dev            =   {
55         .release    =   led_release,
56     },
57 };
58 
59 
60 static int __init led_dev_platform_init(void)
61 {
62     //注册平台设备
63     platform_device_register(&led_gpio_device);  
64     return 0;
65 }
66 
67 
68 static void __exit led_dev_platform_exit(void)
69 {
70     //注销平台设备
71     platform_device_unregister(&led_gpio_device);
72 }
73 
74 
75 module_init(led_dev_platform_init);
76 module_exit(led_dev_platform_exit);
77 MODULE_LICENSE("Dual BSD/GPL");
View Code

 

3.3 新建led_drv_platform.c文件,添加平台驱动代码

  1 #include <linux/init.h>
  2 #include <linux/module.h>
  3 #include <linux/fs.h>           //文件操作函数,file_operarions
  4 #include <linux/device.h>       //设备申请函数,device_create
  5 #include <linux/platform_device.h>
  6 #include <linux/slab.h>         //内核空间申请函数件,kmalloc
  7 #include <asm/uaccess.h>        //内核与用户空间消息拷贝函数,copy_to_usser  &  copy_from_user
  8 #include <asm/io.h>             //内存地址映射函数,ioremap
  9 #include <linux/cdev.h>
 10 #include "led.h"
 11 
 12    
 13 static  struct led_device *imx_led_gpio1_3;
 14 
 15 //设备文件的open操作函数
 16 static  int led_open(struct inode *inode, struct file *file)
 17 {
 18 
 19     return 0;
 20 }
 21 
 22 //设备文件的close操作函数
 23 static int led_close(struct inode *inode, struct file *file)
 24 {
 25     return 0;
 26 }
 27 
 28 //设备文件的read操作函数
 29 static ssize_t led_read(struct file *file, char __user *buf, size_t size, loff_t *f_ops)
 30 {
 31     return 0;
 32 }
 33 
 34 //设备文件的write操作函数
 35 static ssize_t led_write(struct file *file, const char __user *buf, size_t size, loff_t *f_ops)
 36 {
 37     int ret;
 38     unsigned char cmd[1];
 39     u32 val = 0;
 40 
 41     ret = copy_from_user(cmd, buf, size);
 42     if(ret < 0) 
 43     {
 44         printk("kernel write failed!\r\n");
 45         return -EFAULT;
 46     }
 47 
 48 
 49    if(*cmd == LED_ON)
 50    {
 51         val = readl(imx_led_gpio1_3->led_dr);
 52         val &= ~(1 << 3);    
 53         writel(val, imx_led_gpio1_3->led_dr);
 54    }
 55    else if(*cmd == LED_OFF)
 56    {
 57         val = readl(imx_led_gpio1_3->led_dr);
 58         val|= (1 << 3);    
 59         writel(val, imx_led_gpio1_3->led_dr);
 60    }
 61    else
 62    {
 63        printk("led command is invalid!\n");
 64        return -2;
 65    } 
 66 
 67     return 0;
 68 }
 69 
 70 static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 71 {
 72     u32 val = 0;
 73     switch (cmd)
 74     {
 75      case LED_OFF:
 76                 val = readl(imx_led_gpio1_3->led_dr);
 77                 val|= (1 << 3);    
 78                 writel(val, imx_led_gpio1_3->led_dr);
 79                 break;
 80     case LED_ON:
 81                 val = readl(imx_led_gpio1_3->led_dr);
 82                 val &= ~(1 << 3);    
 83                 writel(val, imx_led_gpio1_3->led_dr);
 84                 break;
 85     default:
 86                 val = readl(imx_led_gpio1_3->led_dr);
 87                 val|= (1 << 3);    
 88                 writel(val, imx_led_gpio1_3->led_dr);
 89                 break;
 90     }
 91 
 92 
 93     return 0;
 94 }
 95 
 96 //填充file_operations结构体
 97 static  const struct file_operations led_fops = {
 98     .owner               =   THIS_MODULE,
 99     .open                =   led_open,
100     .release             =   led_close,
101     .write               =   led_write,
102     .read                =   led_read,
103     .unlocked_ioctl      =   led_ioctl,
104 };
105 
106 
107 static int led_gpio_probe(struct platform_device *pdev)
108 {
109     int ret = -2;
110     int i;
111     u32 val = 0;
112     struct resource *res[5];
113     int ressize[5];
114 
115     //为自定义机构提申请内核内存
116     imx_led_gpio1_3 = kmalloc(sizeof(struct led_device), GFP_KERNEL);
117     if(imx_led_gpio1_3 == NULL)
118     {
119         printk(KERN_ERR "kmalloc fail!\n");
120         return -ENOMEM;
121     }
122 
123 
124     //ret = register_chrdev(LED_MAJOR, "led", &led_fops);
125     //如果使用动态申请,则必须使用cdev_init() & cdev_add()这两个函数;静态则不需要
126     ret = alloc_chrdev_region(&imx_led_gpio1_3->devno, 0, 1, "led");
127     if(ret < 0)
128     {
129         printk(KERN_ERR "register major failed!\n");
130         ret = -EINVAL;
131         goto err1;
132     }
133 
134 
135     imx_led_gpio1_3->major = MAJOR(imx_led_gpio1_3->devno);
136     imx_led_gpio1_3->led_cdev.owner = THIS_MODULE;
137 
138     //dev_major = MAJOR(devno);
139     imx_led_gpio1_3->led_cdev.owner = THIS_MODULE;
140 
141     //将设备与文件操作函数关联
142     cdev_init(&imx_led_gpio1_3->led_cdev, &led_fops);
143 
144     //注册设备
145     cdev_add(&imx_led_gpio1_3->led_cdev,imx_led_gpio1_3->devno, 1);
146 
147     //创建设备类
148     imx_led_gpio1_3->led_class = class_create(THIS_MODULE, "led_class");
149     if(IS_ERR(imx_led_gpio1_3->led_class))
150     {
151         printk(KERN_ERR "failed to create class!\n");
152         ret = PTR_ERR(imx_led_gpio1_3->led_class);
153         goto err2;
154     }
155 
156     
157     //创建设备文件,此函数最后一个参数led,其实就是设备名,应用程序open打开的也是这个文件名
158     imx_led_gpio1_3->led_device = device_create(imx_led_gpio1_3->led_class, NULL, imx_led_gpio1_3->devno, NULL, "led");
159     if(IS_ERR(imx_led_gpio1_3->led_device))
160     {
161         printk(KERN_ERR "failed to create device!\n");
162         ret = PTR_ERR(imx_led_gpio1_3->led_device);
163         goto err3;
164     }
165  
166    //获取设备资源
167    for(i = 0;  i < 5; i++)
168    {
169        res[i] =   platform_get_resource(pdev, IORESOURCE_MEM, i);
170        if(!res[i])
171        {
172            dev_err(&pdev->dev, "No MEM resource for always on \n");
173            return -ENXIO;
174        }
175        ressize[i] = resource_size(res[i]);
176    }
177     
178 
179     //对物理地址进行内存映射,以便后面操作
180     imx_led_gpio1_3->led_dr = ioremap(res[0]->start, ressize[0]);
181     if(imx_led_gpio1_3->led_dr == NULL)
182     {
183         printk(KERN_ERR "led_dr ioremap fail!\n");
184         ret = -ENOMEM;
185         goto err4;
186     }
187 
188     imx_led_gpio1_3->led_gdir = ioremap(res[1]->start, ressize[1]);
189     if(imx_led_gpio1_3->led_gdir == NULL)
190     {
191         printk(KERN_ERR "led_gdir ioremap fail!\n");
192         ret = -ENOMEM;
193         goto err4;
194     }
195 
196     imx_led_gpio1_3->led_mux = ioremap(res[2]->start, ressize[2]);
197     if(imx_led_gpio1_3->led_mux  == NULL)
198     {
199         printk(KERN_ERR "led_mux ioremap fail!\n");
200         ret = -ENOMEM;
201         goto err4;
202     }    
203 
204     imx_led_gpio1_3->led_pd = ioremap(res[3]->start, ressize[3]);
205     if(imx_led_gpio1_3->led_pd == NULL)
206     {
207         printk(KERN_ERR "led_pd ioremap fail!\n");
208         ret = -ENOMEM;
209         goto err4;
210     }
211 
212 
213     imx_led_gpio1_3->led_ccm = ioremap(res[4]->start, ressize[4]);
214     if(imx_led_gpio1_3->led_ccm == NULL)
215     {
216         printk(KERN_ERR "led_ccm ioremap fail!\n");
217         ret = -ENOMEM;
218         goto err4;
219     }
220 
221     // 使能GPIO1时钟 
222     val = readl(imx_led_gpio1_3->led_ccm);
223     val &= ~(3 << 26);    //清掉旧值
224     val |= (3 << 26);    // 设置新值
225     writel(val, imx_led_gpio1_3->led_ccm);
226 
227     // 将GPIO1_IO03复用功能设置为GPIO
228     writel(5, imx_led_gpio1_3->led_mux);
229 
230     // 设置GPIO的电流驱动能力
231     writel(0x10B0, imx_led_gpio1_3->led_pd);
232 
233    // 设置GPIO的输入输出方向 
234     val = readl(imx_led_gpio1_3->led_gdir);
235     val &= ~(1 << 3);    // 清除以前的设置
236     val |= (1 << 3);    // 设置为输出 
237     writel(val, imx_led_gpio1_3->led_gdir);
238 
239 
240     //设置LED的默认状态:LED熄灭 
241     val = readl(imx_led_gpio1_3->led_dr);
242     val |= (1 << 3);    
243     writel(val, imx_led_gpio1_3->led_dr);
244 
245     return 0;
246 
247 //注销设备节点
248 err4:
249     device_destroy(imx_led_gpio1_3->led_class, imx_led_gpio1_3->major);
250 
251 //注销设备类
252 err3:
253     class_destroy(imx_led_gpio1_3->led_class);
254 
255 //注销设备号
256 err2:
257     unregister_chrdev(imx_led_gpio1_3->major, "led");
258 
259 //释放由kmalloc申请的内存
260 err1:
261     kfree(imx_led_gpio1_3);
262     return ret;
263 
264 }
265 
266 
267 static int led_gpio_remove(struct platform_device *pdev)
268 {
269     device_destroy(imx_led_gpio1_3->led_class, imx_led_gpio1_3->major);
270     class_destroy(imx_led_gpio1_3->led_class);
271     unregister_chrdev(imx_led_gpio1_3->major, "led");
272     iounmap(imx_led_gpio1_3->led_dr);
273     iounmap(imx_led_gpio1_3->led_gdir);
274     iounmap(imx_led_gpio1_3->led_pd);
275     iounmap(imx_led_gpio1_3->led_mux);
276     iounmap(imx_led_gpio1_3->led_ccm);
277     kfree(imx_led_gpio1_3);
278 
279     return 0;
280 }
281 
282 struct platform_device_id led_ids[] = {
283     [0] =   {
284         .name           =   "led_gpio",
285         .driver_data    =   0,
286     },
287 };
288 
289 struct platform_driver led_gpio_driver = {
290     .probe  =   led_gpio_probe,
291     .remove =   led_gpio_remove,
292     .driver =   {
293         .owner  =   THIS_MODULE,
294         .name   =   "led_gpio"
295     },
296     .id_table   =   led_ids //当成员driver中的name成员不匹配时,从成员id_table中寻找
297 };
298 
299 static int __init led_init(void)
300 {
301     platform_driver_register(&led_gpio_driver);
302     return 0;
303 }
304 
305 static void __exit led_exit(void)
306 {   
307     platform_driver_unregister(&led_gpio_driver);
308 }
309 
310 module_init(led_init);
311 module_exit(led_exit);
312 MODULE_LICENSE("Dual BSD/GPL");
View Code

 

4. 测试代码

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <sys/types.h>
 4 #include <sys/stat.h>
 5 #include <fcntl.h>
 6 
 7 #define LED_ON                  1
 8 #define LED_OFF                 0
 9 
10 
11 int main(int argc, char **argv)
12 {
13     int fd;
14     int buf = 0;
15 
16     fd = open("/dev/led", O_RDWR);
17 
18     printf("fd = %d\n", fd);
19     if(fd < 0)
20     {
21         perror("open fail!\n");
22        
23         exit(1);
24     }
25 
26 #if 0
27 
28     if(strcmp(argv[1], "on") == 0)
29     {
30         if(ioctl(fd, LED_ON) < 0)
31             perror("ioctrl fail:led on\n");
32         else
33             printf("ioctl ok:led on\n");
34     }
35     else if(strcmp(argv[1], "off") == 0)
36     {
37         if(ioctl(fd, LED_OFF) < 0)
38              perror("ioctrl fail:led off\n");
39         else
40             printf("ioctl ok:led off\n");
41     }
42     else
43     {
44         perror("command is invalid!\n");
45     }
46 
47 
48 #else
49      if(strcmp(argv[1], "on") == 0)
50      {
51          buf = 1;
52         if(write(fd, &buf, sizeof(buf)) < 0)
53         {
54             perror("write fail:led on!\n");
55             exit(1); 
56         }
57         else
58         {
59             printf("write ok:led on!\n");         
60         }
61      }
62      else if(strcmp(argv[1], "off") == 0)
63      {
64          buf = 0;
65         if(write(fd, &buf, sizeof(buf)) < 0)
66         {
67             perror("write fail:led off!\n");
68             exit(1);
69         }
70         else
71         {
72             printf("write ok:led off!\n");
73         }
74      }
75      else
76     {
77         perror("command is invalid!\n");
78     }
79 
80 
81 #endif
82 
83     close(fd);
84     return 0;
85 }
View Code

 

总结:

1. 当平台设备有多个资源时,平台驱动需要逐个获取

2. 注意平台设备驱动与平台驱动匹配的条件

 

标签:字符,gpio1,led,GPIO,总线,imx,device,LED
来源: https://www.cnblogs.com/QSHL/p/15408522.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有