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.");


 

猜你喜欢

转载自blog.csdn.net/llzhang_fly/article/details/83687718