1. 程式人生 > >Note06_02_LED驅動_GPIO子系統控制

Note06_02_LED驅動_GPIO子系統控制

Note06_02_LED驅動_GPIO子系統控制

        接上節,使用另一種方式來初始化LED的暫存器:採用GPIO子系統函式介面,對LED的引腳對應的SFR進行設定和初始化,然後使用 ioctl命令來 讀取和設定LED燈的狀態;

  • Led硬體電路圖

  • Led GPIO引腳控制暫存器

參見上節《Note06_01_LED驅動_ioctl傳輸命令》內容。

  • GPIO子系統:

linux核心封裝了一套專門用於操作 GPIO 的介面函式,這些介面函式是通過一個GPIO號來訪問控制的,現將常用的GPIO 介面函式羅列如下:

To  test if such number from such a structure could reference a GPIO, you may use this predicate:    int gpio_is_valid(int number);

Using GPIOs
-----------
The first thing a system should do with a GPIO is allocate it, using the gpio_request() call; see later.

One of the next things to do with a GPIO, often in board setup code when setting up a platform_device using the GPIO, is mark its direction:

    /* set as input or output, returning 0 or negative errno */
    int gpio_direction_input(unsigned gpio);
    int gpio_direction_output(unsigned gpio, int value);

The return value is zero for success, else a negative errno.  It should be checked, since the get/set calls don't have error returns and since misconfiguration is possible.

Spinlock-Safe GPIO access
-------------------------
Most GPIO controllers can be accessed with memory read/write instructions.
Those don't need to sleep, and can safely be done from inside hard (nonthreaded) IRQ handlers and similar contexts.

Use the following calls to access such GPIOs, for which gpio_cansleep() will always return false (see below):

    /* GPIO INPUT:  return zero or nonzero */
    int gpio_get_value(unsigned gpio);

    /* GPIO OUTPUT */
    void gpio_set_value(unsigned gpio, int value);

The values are boolean, zero for low, nonzero for high.  When reading the value of an output pin, the value returned should be what's seen on the pin ... that won't always match the specified output value, because of issues including open-drain signaling and output latencies.

linux GPIO 子系統的相關介紹,詳見之前寫過的剛查閱的一篇文章《linux GPIO子系統核心原始碼追蹤》

  • Led驅動程式框架及驅動實現流程

1)驅動程式入口實現

通過gpio 子系統介面函式,實現資源申請

設定LED pin 引腳模式,及初始電平

註冊 misc 雜項裝置驅動

122 /*
123 **  init miscdecvice's source
124 */

125 static struct miscdevice misc = {
126     .minor  = MISC_DYNAMIC_MINOR,
127     .name   = DEVNAME,
128     .fops   = &fops,
129 };

// 驅動入口函式

132 static int __init demo_init(void)
133 {
134     int ret;
135     int i;
136     // request GPIO_sys source: arg (pin, name)
137     for (i = 0; i < ARRAY_SIZE(ledgpios); ++i) {
138         ret = gpio_request(ledgpios[i], "led");

139         if (ret < 0) {
140             goto error0;
141         }
142         // set mode of LED's pins
143         ret = gpio_direction_output(ledgpios[i], 1);

144         if (ret < 0) {
145             gpio_free(ledgpios[i]);
146             goto error0;
147         }
148     }
149     // register misc's devive
150     ret = misc_register(&misc);

151     if (ret < 0) {
152         goto error0;
153     }
154
155     return 0;
156
157 error0:
158     while (i--) {
159         gpio_free(ledgpios[i]);
160     }
161
162     return ret;
163 }
164
165 module_init(demo_init);

2)驅動程式出口實現

復位LED pin引腳的控制電平:非使能態,1

釋放申請的gpio 子系統資源

登出 misc 雜項裝置驅動

167 static void __exit demo_exit(void)
168 {
169     int i;
170
171     for (i = 0; i < ARRAY_SIZE(ledgpios); ++i) {
172         // reset led pins' state is unenable
173         gpio_set_value(ledgpios[i], 1);

174         // free request gpio resource
175         gpio_free(ledgpios[i]);
176     }
177
178     misc_deregister(&misc);
179 }
180
181 module_exit(demo_exit);

3)驅動程式fops操作介面實現

open() 函式實現

ioctl() 函式實現

release() 函式實現

115 static struct file_operations fops = {
116     .owner      = THIS_MODULE,
117     .open       = mill_open,
118     .unlocked_ioctl = mill_unlocked_ioctl,

119     .release    = mill_release,
120 };

  • Led驅動實現流程

通過執行測試程式,傳入引數直接控制 LED 燈,比如 ./test  onall,./test on 1

1)首先開啟驅動註冊的LED 驅動裝置檔案;

2)根據傳入的引數個數,以及控制引數和LED 的編號(是控制第一個還是最後1個),構造 ioctl 的命令碼

3)通過 ioctl()  系統呼叫函式,直接進行控制 LED 燈;

21 int main(int argc, char **argv)
 22 {
 23     int fd;
 24     int num;
 25     int request;
 26     int ret;
 27     char buf[4] = {0};
 28     int i;
 29
 30     if ((argc != 2)&&(argc != 3)) {
 31         usage(argv[0]);
 32     }

 33     // 開啟leds 雜項裝置驅動,試想,若不註冊雜項裝置驅動,則操作led燈
 34     // 時,每個led燈都要有可操作的裝置節點;
 35     fd = open("/dev/leds0-4", O_RDWR | O_NDELAY);
 36     assert(fd > 0);
 37
 38     if (argc == 2)
 39     {   // 全部開啟

 40         if (!strncmp("onall", argv[1], 5))
 41         {
 42             ret = ioctl(fd, LED_ON_ALL);

 43             assert(ret == 0);
 44         }// 全部關閉
 45         else if (!strncmp("offall", argv[1], 5))
 46         {
 47             ret = ioctl(fd, LED_OFF_ALL);

 48             assert(ret == 0);
 49         }// 獲取當前狀態
 50         else if (!strncmp("stat", argv[1], 4))
 51         {
 52             ret = ioctl(fd, LED_GET_STAT, buf);

 53             for (i = 0; i < 4; i++) {

54                 if (buf[i] == '1')
 55                 {
 56                     printf("led %d is on\n", i+1);
 57                 } else if (buf[i] == '0')
 58                 {
 59                     printf("led %d is off\n", i+1);
 60                 } else {
 61                     exit(1);
 62                 }
 63             }
 64         } else {
 65             usage(argv[0]);
 66         }
 67     }
 68     else
 69     {   // 開啟指定的 led燈

 70         if (!strncmp("on", argv[1], 2))
 71         {
 72             request = LED_ON;
 73         }// 關閉指定的 led燈
 74         else if (!strncmp("off", argv[1], 3))
 75         {
 76             request = LED_OFF;
 77         }
 78         else
 79         {
 80             usage(argv[0]);
 81         }
 82         // 通過可執行檔案傳入的'第幾個led燈'引數,是字元型,需轉換為整形
 83         num = atoi(argv[2]);

 84         // 通過ioctl 系統呼叫,下發控制命令
 85         ret = ioctl(fd, request, num);

 86         assert(ret == 0);
 87     }
 88
 89         return 0;

90 }

assert巨集的原型定義在<assert.h>中,其作用是如果它的條件返回錯誤,則終止程式執行,原型定義:
#include <assert.h>
void assert( int expression );

assert的作用是現計算表示式 expression ,如果其值為假(即為0),那麼它先向stderr列印一條出錯資訊,然後通過呼叫 abort 來終止程式執行。

缺點: 頻繁的呼叫會極大的影響程式的效能,增加額外的開銷。

4)ioclt 命令碼:應用層 和 驅動層使用統一套命令碼命名規則;

  1 #ifndef MILLET_LED_H_
  2 #define MILLET_LED_H_
  3
  4 #include <linux/ioctl.h>
  5
  6 #define LEDTYPE 'L'                     // 幻數
  7
  8 #define LED_ON                   _IOW(LEDTYPE, 0, int)           // 控制命令
  9 #define LED_OFF                 _IOW(LEDTYPE, 1, int)
 10 #define LED_ON_ALL         _IO(LEDTYPE, 2)
 11 #define LED_OFF_ALL       _IO(LEDTYPE, 3)
 12 #define LED_GET_STAT    _IOR(LEDTYPE, 4, int)

 13
 14 #endif

  • 測試結果

1)插入LED 裝置驅動

2)檢視註冊的裝置驅動名稱:次裝置號為 50,裝置名:leds0-4

因是雜項裝置驅動,故在 /proc/misc 目錄下檢視

3)整個系統中搜索 裝置名

/dev/leds0-4                                                  // LED 裝置檔案節點,包含4個LED
/sys/devices/virtual/misc/leds0-4               // 暫時未知
/sys/class/misc/leds0-4                               // 因驅動裝置檔案是通過 misc 雜項裝置機制註冊的,故自動建立了裝置節點

備註: class_create() 自動建立裝置節點形式,晚上進行總結;

4)點亮所有 LED,並檢視狀態

 12 // LED 控制狀態,或當前狀態
 13 enum led{
 14     OFF,    // 0, led unlight
 15     ON      // 1, led light
 16 };

5)熄滅LED 3,並檢視狀態

  • ioctl.h 檔案

應用層和驅動,共用 ioctl() 命令碼:

  1 #ifndef MILLET_LED_H_
  2 #define MILLET_LED_H_
  3 
  4 #include <linux/ioctl.h>
  5 
  6 #define LEDTYPE 'L'
  7 
  8 #define LED_ON      _IOW(LEDTYPE, 0, int)
  9 #define LED_OFF     _IOW(LEDTYPE, 1, int)
 10 #define LED_ON_ALL  _IO(LEDTYPE, 2)
 11 #define LED_OFF_ALL _IO(LEDTYPE, 3)
 12 #define LED_GET_STAT    _IOR(LEDTYPE, 4, int)
 13 
 14 #endif
  • Led 測試程式實現

1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include <unistd.h>
  6 #include <assert.h>
  7 #include <stdlib.h>
  8 #include <string.h>
  9 
 10 #include "ioctl.h"
 11 
 12 void usage(const char *str)
 13 {
 14     fprintf(stderr, "Usage:\n");
 15     fprintf(stderr, "      %s on/off num(1~4)\n", str);
 16     fprintf(stderr, "      %s onall/offall\n", str);
 17     fprintf(stderr, "      %s stat\n", str);
 18     exit(1);
 19 }
 20 
 21 int main(int argc, char **argv)
 22 {
 23     int fd;
 24     int num;
 25     int request;
 26     int ret;
 27     char buf[4] = {0};
 28     int i;
 29 
 30     if ((argc != 2)&&(argc != 3)) {
31         usage(argv[0]);
 32     }
 33     // 開啟leds 雜項裝置驅動,試想,若不註冊雜項裝置驅動,則操作led燈
 34     // 時,每個led燈都要有可操作的裝置節點;
 35     fd = open("/dev/leds0-4", O_RDWR | O_NDELAY);
 36     assert(fd > 0);
 37 
 38     if (argc == 2)
 39     {   // 全部開啟
 40         if (!strncmp("onall", argv[1], 5))
 41         {
 42             ret = ioctl(fd, LED_ON_ALL);
 43             assert(ret == 0);
 44         }// 全部關閉
 45         else if (!strncmp("offall", argv[1], 5))
 46         {
 47             ret = ioctl(fd, LED_OFF_ALL);
 48             assert(ret == 0);
 49         }// 獲取當前狀態
 50         else if (!strncmp("stat", argv[1], 4))
 51         {
 52             ret = ioctl(fd, LED_GET_STAT, buf);
 53             for (i = 0; i < 4; i++) {
 54                 if (buf[i] == '1')
 55                 {
 56                     printf("led %d is on\n", i+1);
 57                 } else if (buf[i] == '0')
 58                 {
 59                     printf("led %d is off\n", i+1);
 60                 } else {
 61                     exit(1);
 62                 }
 63             }
 64         } else {
 65             usage(argv[0]);
66         }
 67     }
 68     else
 69     {   // 開啟指定的 led燈
 70         if (!strncmp("on", argv[1], 2))
 71         {
 72             request = LED_ON;
 73         }// 關閉指定的 led燈
 74         else if (!strncmp("off", argv[1], 3))
 75         {
 76             request = LED_OFF;
 77         }
 78         else
 79         {
 80             usage(argv[0]);
 81         }
 82         // 通過可執行檔案傳入的'第幾個led燈'引數,是字元型,需轉換為整形
 83         num = atoi(argv[2]);
 84         // 通過ioctl 系統呼叫,下發控制命令
 85         ret = ioctl(fd, request, num);
 86         assert(ret == 0);
 87     }
 88 
 89         return 0;
 90 }
  • Led驅動程式實現

1 #include <linux/init.h>
  2 #include <linux/module.h>
  3 #include <linux/fs.h>
  4 #include <linux/io.h>
  5 #include <linux/gpio.h>
  6 #include <linux/miscdevice.h>
  7 #include "ioctl.h"
  8 
  9 // misc 裝置驅動名稱
 10 #define DEVNAME "leds0-4"
 11 
 12 // LED 控制狀態,或當前狀態
 13 enum led{
 14     OFF,    // 0, led unlight
 15     ON      // 1, led light
 16 };
 17 
 18 // LED gpio 編號,使用該變化,可向系統申請資源
 19 // EXYNOS4X12_GPM4(x),這個巨集解析後,為對應引腳暫存器的首地址
 20 static int ledgpios[] = {
 21     EXYNOS4X12_GPM4(0),
 22     EXYNOS4X12_GPM4(1),
 23     EXYNOS4X12_GPM4(2),
 24     EXYNOS4X12_GPM4(3)
 25 };
 26 
 27 static int
 28 mill_open (struct inode *inodp, struct file *filp)
 29 {
 30     printk("KER-[%s]\n", __func__);
 31     return 0;
 32 }
 33 
 34 void led_ctl(enum led cmd, int arg)
 35 {
 36     printk("KER-[%s], control LED%d's state is %d\n", __func__, arg, cmd);
 37     // LED 低電平為有效電平,亮
38     if (cmd == ON) {
 39         gpio_set_value(ledgpios[arg-1], 0);
 40     } else if (cmd == OFF) {
 41         gpio_set_value(ledgpios[arg-1], 1);
 42     }
 43 }
 44 
 45 /*
 46 **   set leds all ON or OFF
 47 */
 48 void led_ctl_all(enum led cmd)
 49 {
 50     int i;
 51 
 52     for (i = 0; i < 4; i++) {
 53         if (cmd == ON) {
 54             led_ctl(ON, i+1);
 55         } else {
 56             led_ctl(OFF, i+1);
 57         }
 58     }
 59 }
 60 /*
 61 **   get leds status
 62 */
 63 static void led_get_stat(char *buf)
 64 {
 65     int i;
 66 
 67     for (i = 0; i < 4; i++) {
 68         buf[i] = gpio_get_value(ledgpios[i])?'0':'1';
 69         printk("KER-[%s], get LED%d's state is %c\n", __func__, (i+1), buf[i]);
 70     }
 71 }
 72 
 73 /*
74 **  get user space's ioctl cmd to contral leds
 75 **  request:    ioctl CMD from user space
 76 */
 77 static long
 78 mill_unlocked_ioctl (struct file *filp, unsigned int request, unsigned long arg)
 79 {
 80     printk("KER-[%s], TYPE: %c, NR: %d\n", __func__, _IOC_TYPE(request), _IOC_NR(reque    st));
 81     if (_IOC_TYPE(request) == LEDTYPE) {// get ioctl cmd TYPE's data
 82         switch (_IOC_NR(request)) {     // get ioctl cmd's data
 83             case 0:
 84                 if (arg < 1 || arg > 4) {
 85                     return -EINVAL;
 86                 }
 87                 led_ctl(ON, arg);
 88                 break;
 89             case 1:
 90                 if (arg < 1 || arg > 4) {
 91                     return -EINVAL;
 92                 }
 93                 led_ctl(OFF, arg);
 94                 break;
 95             case 2:
 96                 led_ctl_all(ON);
 97                 break;
 98             case 3:
 99                 led_ctl_all(OFF);
100                 break;
101             case 4:
102                 led_get_stat((char *)arg);
103             default:
104                 return -EINVAL;
105         }
106     }
107 
108     return 0;
109 }
110 
111 static int
112 mill_release (struct inode *inodp, struct file *filp)
113 {
114 
115     return 0;
116 }
117 
118 static struct file_operations fops = {
119     .owner      = THIS_MODULE,
120     .open       = mill_open,
121     .unlocked_ioctl = mill_unlocked_ioctl,
122     .release    = mill_release,
123 };
124 
125 /*
126 **  init miscdecvice's source 
127 */
128 static struct miscdevice misc = {
129     .minor  = MISC_DYNAMIC_MINOR,
130     .name   = DEVNAME,
131     .fops   = &fops,
132 };
133 
134     /*ioread32/ioread16/ioared8  iowrite32/iowrite16/iowrite8*/
135 static int __init demo_init(void)
136 {
137     int ret;
138     int i;
139     // request GPIO_sys source: arg (pin, name)
140     for (i = 0; i < ARRAY_SIZE(ledgpios); ++i) {
141         ret = gpio_request(ledgpios[i], "led");
142         if (ret < 0) {
143             goto error0;
144         }
145         // set mode of LED's pins 
146         ret = gpio_direction_output(ledgpios[i], 1);
147         if (ret < 0) {
148             gpio_free(ledgpios[i]);
149             goto error0;
150         }
151     }
152     // register misc's devive
153     ret = misc_register(&misc);
154     if (ret < 0) {
155         goto error0;
156     }
157 
158     printk("KER-[%s] leave.\n", __func__);
159     return 0;
160 
161 error0:
162     while (i--) {
163         gpio_free(ledgpios[i]);
164     }
165 
166     return ret;
167 }
168 
169 module_init(demo_init);
170 
171 static void __exit demo_exit(void)
172 {
173     int i;
174 
175     for (i = 0; i < ARRAY_SIZE(ledgpios); ++i) {
176         // reset led pins' state is unenable
177         gpio_set_value(ledgpios[i], 1);
178         // free request gpio resource
179         gpio_free(ledgpios[i]);
180     }
181 
182     misc_deregister(&misc);
183 }
184 
185 module_exit(demo_exit);
186 
187 MODULE_LICENSE("GPL");
188 
189 MODULE_AUTHOR("zhang li lin");
190 MODULE_VERSION("zhang leds control2");
191 MODULE_DESCRIPTION("It is a simple example for module.");