字符设备驱动: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头文件,添加如下代码

字符设备驱动:LED平台总线实现方式
 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文件,添加平台设备代码

字符设备驱动:LED平台总线实现方式
 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文件,添加平台驱动代码

字符设备驱动:LED平台总线实现方式
  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. 测试代码

字符设备驱动:LED平台总线实现方式
 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. 注意平台设备驱动与平台驱动匹配的条件

 

上一篇:51单片机 第五课:LED流水灯


下一篇:51单片机 Proteus仿真定时器0计数由LED组成的二进制显示