天天看點

i.MX6ULL終結者Linux核心定時器實驗實驗程式編寫

文章目錄

    • 1 驅動程式編寫
    • 2 應用測試程式編寫

1 驅動程式編寫

本實驗例程路徑:i.MX6UL終結者CD光牒資料/06_Linux驅動例程/09_gpioled_timer

建立gpioled_timer.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.h>
 12 #include <linux/of_address.h>
 13 #include <linux/of_gpio.h>
 14 #include <linux/semaphore.h>
 15 #include <linux/timer.h>
 16 #include <asm/mach/map.h>
 17 #include <asm/uaccess.h>
 18 #include <asm/io.h>
 19 
 20 #define TIMER_CNT 1 /* 裝置号個數 */
 21 #define TIMER_NAME "timer" /* 名字 */
 22 #define CLOSE_CMD (_IO(0XEF, 0x1)) /* 關閉定時器 */
 23 #define OPEN_CMD (_IO(0XEF, 0x2)) /* 打開定時器 */
 24 #define SETPERIOD_CMD (_IO(0XEF, 0x3)) /* 設定定時器周期指令 */
 25 
 26 #define LEDON 1 /* 開燈 */
 27 #define LEDOFF 0 /* 關燈 */
 28 
 29 /* timer 裝置結構體 */
 30 struct timer_dev{
 31         dev_t devid; /* 裝置号 */
 32         struct cdev cdev; /* cdev */
 33         struct class *class; /* 類 */
 34         struct device *device; /* 裝置 */
 35         int major; /* 主裝置号 */
 36         int minor; /* 次裝置号 */
 37         struct device_node *nd; /* 裝置節點 */
 38         int led_gpio; /* key 所使用的 GPIO 編号 */
 39         int timeperiod; /* 定時周期,機關為 ms */
 40         struct timer_list timer; /* 定義一個定時器 */
 41         spinlock_t lock; /* 定義自旋鎖 */
 42 };
 43 
 44 struct timer_dev timerdev; /* timer 裝置 */
 45 
 46 /*
 47  * @description : 初始化 LED 燈 IO,open 函數打開驅動的時候
 48  * 初始化 LED 燈所使用的 GPIO 引腳。
 49  * @param : 無
 50  * @return : 無
 51  */
 52 static int led_init(void)
 53 {
 54         int ret = 0;
 55 
 56         timerdev.nd = of_find_node_by_path("/gpioled");
 57         if (timerdev.nd== NULL) {
 58                 return -EINVAL;
 59         }
 60 
 61         timerdev.led_gpio = of_get_named_gpio(timerdev.nd ,"led-gpio", 0);
 62         if (timerdev.led_gpio < 0) {
 63                 printk("can't get led\r\n");
 64                 return -EINVAL;
 65         }
 66 
 67         /* 初始化 led 所使用的 IO */
 68         gpio_request(timerdev.led_gpio, "led"); /* 請求 IO */
 69         ret = gpio_direction_output(timerdev.led_gpio, 1);
 70         if(ret < 0) {
 71                 printk("can't set gpio!\r\n");
 72         }
 73         return 0;
 74 }
 75 
 76 /*
 77  * @description : 打開裝置
 78  * @param – inode : 傳遞給驅動的 inode
 79  * @param - filp : 裝置檔案,file 結構體有個叫做 private_data 的成員變量
 80  * 一般在 open 的時候将 private_data 指向裝置結構體。
 81  * @return : 0 成功;其他 失敗
 82  */
 83 static int timer_open(struct inode *inode, struct file *filp)
 84 {
 85         int ret = 0;
 86         filp->private_data = &timerdev; /* 設定私有資料 */
 87 
 88         timerdev.timeperiod = 1000; /* 預設周期為 1s */
 89         ret = led_init(); /* 初始化 LED IO */
 90         if (ret < 0) {
 91                 return ret;
 92         }
 93         return 0;
 94 }
 95 
 96 /*
 97  * @description : ioctl 函數,
 98  * @param – filp : 要打開的裝置檔案(檔案描述符)
 99  * @param - cmd : 應用程式發送過來的指令
100  * @param - arg : 參數
101  * @return : 0 成功;其他 失敗
102  */
103 static long timer_unlocked_ioctl(struct file *filp,
104                 unsigned int cmd, unsigned long arg)
105 {
106         struct timer_dev *dev = (struct timer_dev *)filp->private_data;
107         int timerperiod;
108         unsigned long flags;
109 
110         switch (cmd) {
111                 case CLOSE_CMD: /* 關閉定時器 */
112                         del_timer_sync(&dev->timer);
113                         break;
114                 case OPEN_CMD: /* 打開定時器 */
115                         spin_lock_irqsave(&dev->lock, flags);
116                         timerperiod = dev->timeperiod;
117                         spin_unlock_irqrestore(&dev->lock, flags);
118                         mod_timer(&dev->timer, jiffies +
119                                         msecs_to_jiffies(timerperiod));
120                         break;
121                 case SETPERIOD_CMD: /* 設定定時器周期 */
122                         spin_lock_irqsave(&dev->lock, flags);
123                         dev->timeperiod = arg;
124                         spin_unlock_irqrestore(&dev->lock, flags);
125                         mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg));
126                         break;
127                 default:
128                         break;
129         }
130         return 0;
131 }
132 
133 /* 裝置操作函數 */
134 static struct file_operations timer_fops = {
135         .owner = THIS_MODULE,
136         .open = timer_open,
137         .unlocked_ioctl = timer_unlocked_ioctl,
138 };
139 
140 /* 定時器回調函數 */
141 void timer_function(unsigned long arg)
142 {
143         struct timer_dev *dev = (struct timer_dev *)arg;
144         static int sta = 1;
145         int timerperiod;
146         unsigned long flags;
147 
148         sta = !sta; /* 每次都取反,實作 LED 燈反轉 */
149         gpio_set_value(dev->led_gpio, sta);
150 
151         /* 重新開機定時器 */
152         spin_lock_irqsave(&dev->lock, flags);
153         timerperiod = dev->timeperiod;
154         spin_unlock_irqrestore(&dev->lock, flags);
155         mod_timer(&dev->timer, jiffies +
156                         msecs_to_jiffies(dev->timeperiod));
157 }
158 
159 /*
160  * @description : 驅動入口函數
161  * @param : 無
162  * @return : 無
163  */
164 static int __init timer_init(void)
165 {
166         /* 初始化自旋鎖 */
167         spin_lock_init(&timerdev.lock);
168 
169         /* 注冊字元裝置驅動 */
170         /* 1、建立裝置号 */
171         if (timerdev.major) { /* 定義了裝置号 */
172                 timerdev.devid = MKDEV(timerdev.major, 0);
173                 register_chrdev_region(timerdev.devid, TIMER_CNT,
174                                 TIMER_NAME);
175         } else { /* 沒有定義裝置号 */
176                 alloc_chrdev_region(&timerdev.devid, 0, TIMER_CNT,
177                                 TIMER_NAME);
178                 timerdev.major = MAJOR(timerdev.devid); /* 擷取主裝置号 */
179                 timerdev.minor = MINOR(timerdev.devid); /* 擷取次裝置号 */
180         }
181 
182         /* 2、初始化 cdev */
183         timerdev.cdev.owner = THIS_MODULE;
184         cdev_init(&timerdev.cdev, &timer_fops);
185 
186         /* 3、添加一個 cdev */
187         cdev_add(&timerdev.cdev, timerdev.devid, TIMER_CNT);
188 
189         /* 4、建立類 */
190         timerdev.class = class_create(THIS_MODULE, TIMER_NAME);
191         if (IS_ERR(timerdev.class)) {
192                 return PTR_ERR(timerdev.class);
193         }
194 
195         /* 5、建立裝置 */
196         timerdev.device = device_create(timerdev.class, NULL,
197                         timerdev.devid, NULL, TIMER_NAME);
198         if (IS_ERR(timerdev.device)) {
199                 return PTR_ERR(timerdev.device);
200         }
201 
202  /* 6、初始化 timer,設定定時器處理函數,還未設定周期,所有不會激活定時器 */
203         init_timer(&timerdev.timer);
204         timerdev.timer.function = timer_function;
205         timerdev.timer.data = (unsigned long)&timerdev;
206         return 0;
207 }
208 
209 /*
210  * @description : 驅動出口函數
211  * @param : 無
212  * @return : 無
213  */
214 static void __exit timer_exit(void)
215 {
216 
217         gpio_set_value(timerdev.led_gpio, 1); /* 解除安裝驅動的時候關閉 LED */
218         del_timer_sync(&timerdev.timer); /* 删除 timer */
219 #if 0
220         del_timer(&timerdev.tiemr);
221 #endif
222 
223         /* 登出字元裝置驅動 */
224         cdev_del(&timerdev.cdev); /* 删除 cdev */
225         unregister_chrdev_region(timerdev.devid, TIMER_CNT);
226 
227         device_destroy(timerdev.class, timerdev.devid);
228         class_destroy(timerdev.class);
229 }
230 
231 module_init(timer_init);
232 module_exit(timer_exit);
233 MODULE_LICENSE("GPL");
234 MODULE_AUTHOR("topeet");
           

第 30~42 行,定時器裝置結構體,在 40 行定義了一個定時器成員變量 timer。

第 83~94 行,函數 timer_open,對應應用程式的 open 函數,應用程式調用 open 函數打開/dev/timer 驅動檔案的時候此函數就會執行。此函數設定檔案私有資料為 timerdev,并且初始化定時周期預設為 1 秒,最後調用 led_init 函數初始化 LED 所使用的 IO。

第 103~131 行,函數 timer_unlocked_ioctl,對應應用程式的 ioctl 函數,應用程式調用 ioctl函數向驅動發送控制資訊,此函數響應并執行。此函數有三個參數:filp,cmd 和 arg,其中 filp是對應的裝置檔案,cmd 是應用程式發送過來的指令資訊,arg 是應用程式發送過來的參數,在本章例程中 arg 參數表示定時周期。

一共有三種指令 CLOSE_CMD,OPEN_CMD 和 SETPERIOD_CMD,這三個指令分别為關閉定時器、打開定時器、設定定時周期。這三個指令的左右如下:

CLOSE_CMD:關閉定時器指令,調用 del_timer_sync 函數關閉定時器。

OPEN_CMD:打開定時器指令,調用 mod_timer 函數打開定時器,定時周期為 timerdev 的timeperiod 成員變量,定時周期預設是 1 秒。

SETPERIOD_CMD:設定定時器周期指令,參數 arg 就是新的定時周期,設定 timerdev 的

timeperiod 成員變量為 arg 所表示定時周期值。并且使用 mod_timer 重新打開定時器,使定時器以新的周期運作。

第 134~138 行,定時器驅動操作函數集 timer_fops。

第 141~157 行,函數 timer_function,定時器服務函數,此函數有一個參數 arg,在本例程中arg 參數就是 timerdev 的位址,這樣通過 arg 參數就可以通路到裝置結構體。當定時周期到了以後此函數就會被調用。在此函數中将 LED 燈的狀态取反,實作 LED 燈閃爍的效果。因為核心定時器不是循環的定時器,執行一次以後就結束了,是以在 161 行又調用了 mod_timer 函數重新開啟定時器。

第 203~205 行初始化定時器,設定定時器的定時處理函數為 timer_function,另外設定要傳遞給 timer_function 函數的參數為 timerdev。在此函數中并沒有調用 timer_add 函數來開啟定時器,是以定時器預設是關閉的,除非應用程式發送打開指令。

第 220 行調用 del_timer_sync 函數删除定時器,也可以使用 del_timer 函數。

2 應用測試程式編寫

測試程式我們要實作的内容如下:

① 運作程式以後提示我們輸入要測試的指令,輸入 1 表示關閉定時器、輸入 2 表示打開定時器,輸入 3 設定定時器周期。

② 如果要設定定時器周期的話,需要讓使用者輸入要設定的周期值,機關為毫秒。

建立名為 gpioled_timer_test.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 #include "sys/ioctl.h"
  9 
 10 /* 指令值 */
 11 #define CLOSE_CMD (_IO(0XEF, 0x1)) /* 關閉定時器 */
 12 #define OPEN_CMD (_IO(0XEF, 0x2)) /* 打開定時器 */
 13 #define SETPERIOD_CMD (_IO(0XEF, 0x3)) /* 設定定時器周期指令 */
 14 
 15 /*
 16  * @description : main 主程式
 17  * @param - argc : argv 數組元素個數
 18  * @param - argv : 具體參數
 19  * @return : 0 成功;其他 失敗
 20  */
 21 int main(int argc, char *argv[])
 22 {
 23         int fd, ret;
 24         char *filename;
 25         unsigned int cmd;
 26         unsigned int arg;
 27         unsigned char str[100];
 28 
 29         if (argc != 2) {
 30                 printf("Error Usage!\r\n");
 31                 return -1;
 32         }
 33 
 34         filename = argv[1];
 35 
 36         fd = open(filename, O_RDWR);
 37         if (fd < 0) {
 38                 printf("Can't open file %s\r\n", filename);
 39                 return -1;
 40         }
 41 
 42         while (1) {
 43                 printf("Input CMD:");
 44                 ret = scanf("%d", &cmd);
 45                 if (ret != 1) { /* 參數輸入錯誤 */
 46                         gets(str); /* 防止卡死 */
 47                 }
 48 
 49                 if(cmd == 1) /* 關閉 LED 燈 */
 50                         cmd = CLOSE_CMD;
 51                 else if(cmd == 2) /* 打開 LED 燈 */
 52                         cmd = OPEN_CMD;
 53                 else if(cmd == 3) {
 54                         cmd = SETPERIOD_CMD; /* 設定周期值 */
 55                         printf("Input Timer Period:");
 56                         ret = scanf("%d", &arg);
 57                         if (ret != 1) { /* 參數輸入錯誤 */
 58                                 gets(str); /* 防止卡死 */
 59                         }
 60                 }
 61                 ioctl(fd, cmd, arg); /* 控制定時器的打開和關閉 */
 62         }
 63         close(fd);
 64 }
           

第 10~13 行,指令值。

第 42~62 行,while(1)循環,讓使用者輸入要測試的指令,然後通過第 61 行的 ioctl 函數發送給驅動程式。如果是設定定時器周期指令 SETPERIOD_CMD,那麼 ioctl 函數的 arg 參數就是使用者輸入的周期值。

i.MX6ULL終結者Linux核心定時器實驗實驗程式編寫

繼續閱讀