1. 程式人生 > 其它 >字元裝置驅動:LED平臺匯流排實現方式

字元裝置驅動: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. 注意平臺裝置驅動與平臺驅動匹配的條件