码迷,mamicode.com
首页 > 其他好文 > 详细

树莓派上的GPIO字符驱动程序

时间:2016-06-04 07:03:28      阅读:529      评论:0      收藏:0      [点我收藏+]

标签:

前言

主要是在嵌入式Linux(树莓派)中如何使用已有的函数库编写应用程序操纵GPIO,如何编写字符设备驱动程序在内核程序中使用GPIO

 

硬件连接图

技术分享

 

虚拟文件系统操作GPIO

 Linux可以通过访问sys/class/gpio下的一些文件,通过对这些文件的读写来实现对于GPIO的访问。

树莓派下面的可用的GPIO如下图所示,需要注意树莓派一代和二代的区别

技术分享

 

首先用一个小灯来测试下操作。首先向export中写入18,表示启用18号gpio端口,执行之后,可以看到该目录下多出了一个gpio18的目录。进入该目录后,先direction中写入out,表示该gpio作为输出,然后向value文件中写入1,表示其输出1,小灯的一端接gpio18,另一端接地,就可以看到小灯点亮。实验成功。

技术分享

 

 

将其改装成用C操作文件的方式进行,这里我写了一个简单的库,包括gpio的export和unexport以及read和write 

 1 //gpio_sys.h
 2 #ifndef _GPIO_SYS_H_
 3 #define _GPIO_SYS_H_
 4 
 5 #include <stdio.h>
 6 #include <stdlib.h>
 7 
 8 #define BUFFMAX 3
 9 #define SYSFS_GPIO_EXPORT     "/sys/class/gpio/export"
10 #define SYSFS_GPIO_UNEXPORT "/sys/class/gpio/unexport"
11 #define SYSFS_GPIO_DIR_IN    0
12 #define SYSFS_GPIO_DIR_OUT    1
13 #define SYSFS_GPIO_VAL_HIGH 1
14 #define SYSFS_GPIO_VAL_LOW    0
15 
16 #define ERR(args...) fprintf(stderr, "%s\n", args);
17 
18 int GPIOExport(int pin);
19 int GPIOUnexport(int pin);
20 int GPIODirection(int pin, int dir);
21 int GPIORead(int pin);
22 int GPIOWrite(int pin, int value);
23 
24 #endif

  

  1 //gpio_sys.c
  2 #include "gpio_sys.h"
  3 #include <sys/stat.h>
  4 #include <sys/types.h>
  5 #include <fcntl.h>
  6 #include <unistd.h>
  7 #include <string.h>
  8 
  9 int GPIOExport(int pin)
 10 {
 11     char buff[BUFFMAX];
 12 
 13     int fd;
 14     if((fd=open(SYSFS_GPIO_EXPORT, O_WRONLY)) == -1)
 15     {
 16         ERR("Failed to open export for writing!\n");
 17         return -1;
 18     }
 19 
 20     int len = snprintf(buff, sizeof(buff), "%d", pin);
 21     if(write(fd, buff, len) == -1)
 22     {
 23         ERR("Failed to export gpio!\n");
 24         return -1;
 25     }
 26 
 27     if(close(fd) == -1)
 28     {
 29         ERR("Failed to close export!\n");
 30         return -1;
 31     }
 32     return 0;
 33 }
 34 
 35 int GPIOUnexport(int pin)
 36 {
 37     char buff[BUFFMAX];
 38     int fd;
 39     if((fd=open(SYSFS_GPIO_UNEXPORT, O_WRONLY)) == -1)
 40     {
 41         ERR("Failed to open unexport for writing!\n");
 42         return -1;        
 43     }
 44 
 45     int len = snprintf(buff, sizeof(buff), "%d", pin);
 46     if(write(fd, buff, len) == -1)
 47     {
 48         ERR("Failed to unexport gpio!\n");
 49         return -1;
 50     }
 51 
 52     if(close(fd) == -1)
 53     {
 54         ERR("Failed to close unexport!\n");
 55         return -1;
 56     }
 57     return 0;
 58 }
 59 
 60 int GPIODirection(int pin, int dir)
 61 {
 62     char dirCh[][5] =  {"in", "out"};
 63     char path[64];
 64 
 65     int fd;
 66     snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", pin);
 67     printf(path);
 68     if((fd = open(path, O_WRONLY)) == -1)
 69     {
 70         ERR("Failed to open direction for writing!\n");
 71         return -1;    
 72     }
 73 
 74     if(write(fd, dirCh[dir], strlen(dirCh[dir])) == -1)
 75     {
 76         ERR("Failed to set direction!\n");
 77         return -1;        
 78     }
 79 
 80     if(close(fd) == -1)
 81     {
 82         ERR("Failed to close direction!\n");
 83         return -1;
 84     }
 85     return 0;
 86 }
 87 
 88 int GPIORead(int pin)
 89 {
 90     char path[64];
 91     char buff[BUFFMAX];
 92 
 93     snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);
 94 
 95     int fd;
 96     if((fd == open(path, O_RDONLY)) == -1)
 97     {
 98         ERR("Failed to open value for reading!\n");
 99         return -1;    
100     }
101 
102     if(read(fd, buff, sizeof(buff)) == -1)
103     {
104         ERR("Failed to read value!\n");
105         return -1;
106     }
107 
108     if(close(fd) == -1)
109     {
110         ERR("Failed to close value!\n");
111         return -1;
112     }
113 
114     return atoi(buff);
115 }
116 
117 int GPIOWrite(int pin, int value)
118 {
119     char path[64];
120     char valuestr[][2] = {"0", "1"};
121     
122     if(value != 0 && value != 1)
123     {
124         fprintf(stderr, "value = %d\n", value);
125         ERR("Writing erro value!\n");
126         return -1;
127     }
128     snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);
129     int fd;
130     if((fd = open(path, O_WRONLY)) == -1)
131     {
132         ERR("Failed to open value for writing!\n");
133         return -1;    
134     }
135 
136     if(write(fd, valuestr[value], 1) == -1)
137     {
138         ERR("Failed to write value!\n");
139         return -1;
140     }
141 
142     if(close(fd) == -1)
143     {
144         ERR("Failed to close value!\n");
145         return -1;
146     }
147     return 0;
148 }

 

 

 技术分享

 

 

然后编写MAX_7219的显示程序。

MAX_7219其输出引DIG0-7和SEG A-G连接了8*8的LED矩阵,而我们关心的则是其输入的5个引脚,分别是5V的VCC和接地GND,以及时钟信号CLK,片选信号CS,串行数据输入端口DIN。

MAX_7219使用前需要对一些寄存器进行初始化设置,包括编码寄存器,亮度寄存器,模式寄存器以及显示检测寄存器。

 

技术分享

MAX_7219的数据的写入是在时钟上升沿写入的,连续数据的后16位被移入寄存器中,其中8-11为地址,0-7位为数据。

      

       在了解了MAX_7219的工作原理之后就可以使用上面的gpio的操纵函数进行字母的显示了。下面给出的是在8*8的矩阵上依次显示0-9,a-z以及中国字样的程序。

       采用的GPIO为gpio18,gpio23和gpio24分别连接CLK,CS以及DIN。

首先需要对于树莓派的GPIO口进行一些配置

 

1 void Init_MAX7219(void)    
2 {
3      Write_Max7219(0x09, 0x00);       //encoding with BCD
4      Write_Max7219(0x0a, 0x03);       //luminance
5      Write_Max7219(0x0b, 0x07);       //scanning bound
6      Write_Max7219(0x0c, 0x01);       //mode: normal
7      Write_Max7219(0x0f, 0x00);       
8 }

  

始化MAX_7219的一些寄存器,设置其译码方式为BCD译码,亮度为3,扫描界限为8个数码管显示,采用普通模式,正常显示。

1 void Init_MAX7219(void)
2 {
3      Write_Max7219(0x09, 0x00);       //encoding with BCD
4      Write_Max7219(0x0a, 0x03);       //luminance
5      Write_Max7219(0x0b, 0x07);       //scanning bound
6      Write_Max7219(0x0c, 0x01);       //mode: normal
7      Write_Max7219(0x0f, 0x00);       
8 }

 

 然后编写数据写入函数,其首先将地址写入,然后在将数据写入。其中写入的顺序为从高位到低位按位依次写入即可。写入的时候首先将CLK设为低电平,然后使得数据有限,然后将CLK设为高电平,让数据在上升沿时写入。 

 1 void Write_Max7219_byte(uchar DATA)         
 2 {
 3     uchar i;   
 4     GPIOWrite(pinCS, SYSFS_GPIO_VAL_LOW); 
 5     for(i=8;i>=1;i--)
 6     {
 7         GPIOWrite(pinCLK, SYSFS_GPIO_VAL_LOW);
 8         GPIOWrite(pinDIN, (DATA&0x80) >> 7);
 9         DATA <<= 1;
10         GPIOWrite(pinCLK, SYSFS_GPIO_VAL_HIGH);
11     }                                 
12 }
13 
14 void Write_Max7219(uchar address,uchar dat)
15 { 
16      GPIOWrite(pinCS, SYSFS_GPIO_VAL_LOW); 
17      Write_Max7219_byte(address);           //writing address
18      Write_Max7219_byte(dat);               //writing data 
19      GPIOWrite(pinCS, SYSFS_GPIO_VAL_HIGH);                      
20 }
21 
22 void Init_MAX7219(void)
23 {
24      Write_Max7219(0x09, 0x00);       //encoding with BCD
25      Write_Max7219(0x0a, 0x03);       //luminance
26      Write_Max7219(0x0b, 0x07);       //scanning bound
27      Write_Max7219(0x0c, 0x01);       //mode: normal
28      Write_Max7219(0x0f, 0x00);       
29 }

 

最后将上述过程拼接起来就可以了。

连接线路然后运行就可以看到字符输出了,下面列出了8, W,中的显示图像。

 技术分享        技术分享   技术分享

 1 #define uchar unsigned char
 2 #define uint  unsigned int
 3 
 4 #define pinCLK     18
 5 #define pinCS     23
 6 #define pinDIN     24
 7 
 8 uchar codeDisp[38][8]={
 9     {0x3C,0x42,0x42,0x42,0x42,0x42,0x42,0x3C},//0
10     {0x10,0x18,0x14,0x10,0x10,0x10,0x10,0x10},//1
11     {0x7E,0x2,0x2,0x7E,0x40,0x40,0x40,0x7E},//2
12     {0x3E,0x2,0x2,0x3E,0x2,0x2,0x3E,0x0},//3
13     {0x8,0x18,0x28,0x48,0xFE,0x8,0x8,0x8},//4
14     {0x3C,0x20,0x20,0x3C,0x4,0x4,0x3C,0x0},//5
15     {0x3C,0x20,0x20,0x3C,0x24,0x24,0x3C,0x0},//6
16     {0x3E,0x22,0x4,0x8,0x8,0x8,0x8,0x8},//7
17     {0x0,0x3E,0x22,0x22,0x3E,0x22,0x22,0x3E},//8
18     {0x3E,0x22,0x22,0x3E,0x2,0x2,0x2,0x3E},//9
19     {0x8,0x14,0x22,0x3E,0x22,0x22,0x22,0x22},//A
20     {0x3C,0x22,0x22,0x3E,0x22,0x22,0x3C,0x0},//B
21     {0x3C,0x40,0x40,0x40,0x40,0x40,0x3C,0x0},//C
22     {0x7C,0x42,0x42,0x42,0x42,0x42,0x7C,0x0},//D
23     {0x7C,0x40,0x40,0x7C,0x40,0x40,0x40,0x7C},//E
24     {0x7C,0x40,0x40,0x7C,0x40,0x40,0x40,0x40},//F
25     {0x3C,0x40,0x40,0x40,0x40,0x44,0x44,0x3C},//G
26     {0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44},//H
27     {0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x7C},//I
28     {0x3C,0x8,0x8,0x8,0x8,0x8,0x48,0x30},//J
29     {0x0,0x24,0x28,0x30,0x20,0x30,0x28,0x24},//K
30     {0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7C},//L
31     {0x81,0xC3,0xA5,0x99,0x81,0x81,0x81,0x81},//M
32     {0x0,0x42,0x62,0x52,0x4A,0x46,0x42,0x0},//N
33     {0x3C,0x42,0x42,0x42,0x42,0x42,0x42,0x3C},//O
34     {0x3C,0x22,0x22,0x22,0x3C,0x20,0x20,0x20},//P
35     {0x1C,0x22,0x22,0x22,0x22,0x26,0x22,0x1D},//Q
36     {0x3C,0x22,0x22,0x22,0x3C,0x24,0x22,0x21},//R
37     {0x0,0x1E,0x20,0x20,0x3E,0x2,0x2,0x3C},//S
38     {0x0,0x3E,0x8,0x8,0x8,0x8,0x8,0x8},//T
39     {0x42,0x42,0x42,0x42,0x42,0x42,0x22,0x1C},//U
40     {0x42,0x42,0x42,0x42,0x42,0x42,0x24,0x18},//V
41     {0x0,0x49,0x49,0x49,0x49,0x2A,0x1C,0x0},//W
42     {0x0,0x41,0x22,0x14,0x8,0x14,0x22,0x41},//X
43     {0x41,0x22,0x14,0x8,0x8,0x8,0x8,0x8},//Y
44     {0x0,0x7F,0x2,0x4,0x8,0x10,0x20,0x7F},//Z
45     {0x8,0x7F,0x49,0x49,0x7F,0x8,0x8,0x8},//
46     {0xFE,0xBA,0x92,0xBA,0x92,0x9A,0xBA,0xFE},//
47 };
48 
49 void Delay_xms(uint x)
50 {
51     uint i,j;
52     for(i=0;i<x;i++)
53         for(j=0;j<50000;j++);
54 }
55 int main(void)
56 {
57     uchar i,j;
58      Delay_xms(50);
59      if(GPIOConfig() == -1)
60      {
61          ERR("Can not configure the gpio!\n")
62          return 0;
63      }
64      Init_MAX7219();
65      while(1)
66      {
67           for(j=0;j<38;j++)
68           {
69                for(i=1;i<9;i++)
70                 Write_Max7219(i,codeDisp[j][i-1]);
71                Delay_xms(1000);
72           }  
73      }
74 
75      if(GPIORelease() == -1)
76      {
77          ERR("Release the gpio error!\n")
78          return 0;
79      }
80 }

 

字符驱动程序

对于通过寄存器操作GPIO,我们就需要知道树莓派上各个GPIO端口的寄存器的地址,在树莓派的CPU(bcm2825)的芯片手册上(https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf)可以查到

技术分享

且由于树莓派的IO的空间的起始地址是0xF2000000,并且GPIO的偏移地址为0x200000,那么真实的GPIO的开始地址为0xF2200000,这个数据可以通过<mach/platform.h>这个头文件中的GPIO_BASE获得。

根据上面说列出的信息,就可以编写我们的对于GPIO端口的操作函数了,这里定义了对于GPIO口的function selection, set以及clear三个函数。

在网上的一篇博客中还看到可以通过树莓派提供的一些列的gpiochip的包装的函数进行操作,但是尝试了很久没有成功,驱动程序报错。编写这个程序也可以参考网上可以下载到的bcm2835的对于GPIO操作的一个bcm2835的一个开源的函数库。

 

 1 //0->input  1-<output
 2 static void bcm2835_gpio_fsel(int pin, int functionCode)
 3 {
 4     int registerIndex = pin / 10;
 5     int bit = (pin % 10) * 3;
 6 
 7     unsigned oldValue = s_pGpioRegisters-> GPFSEL[registerIndex];
 8     unsigned mask = 0b111 << bit;
 9     printk("Changing function of GPIO%d from %x to %x\n", 
10            pin,
11            (oldValue >> bit) & 0b111,
12            functionCode);
13 
14     s_pGpioRegisters-> GPFSEL[registerIndex] = 
15         (oldValue & ~mask) | ((functionCode << bit) & mask);
16 }
17 
18 static void bcm2835_gpio_set(int pin)
19 {
20     printk("GPIO set %d\n oldValue=%d", pin, s_pGpioRegisters->GPSET[0]);
21     s_pGpioRegisters-> GPSET[pin / 32] = (1 << (pin % 32));      
22     printk("GPIO set %d\n oldValue=%d", pin, s_pGpioRegisters->GPSET[0]);
23 }
24 
25 static void bcm2835_gpio_clr(int pin)
26 {
27     printk("GPIO clear %d\n oldValue=%d", pin, s_pGpioRegisters->GPCLR[0]);
28     s_pGpioRegisters-> GPCLR[pin / 32] = (1 << (pin % 32));
29     printk("GPIO clear %d\n newValue=%d", pin, s_pGpioRegisters->GPCLR[0]);
30 }

 

在完成对于GPIO的操作函数之后就可以开始编写字符驱动程序了,Linux的字符驱动程序主要以模块的形式装载到内核中,然后应用程序通过文件的操作的方式对于硬件进行操作。编写一个Linux的字符驱动程序主要如下图所示:

技术分享

 

 

如上图所示,首先需要做的是对于驱动进行初始化设置,在模块的初始化函数中编写如下,首先获得一个设备号(静态或者动态获取),然后进行字符设备的初始化,主要是将file_operation这个结构体中的函数和我们的定义的函数进行一个绑定,注册字符设备,最后创建得到设备。然后在进行设备的初始化,这里首先获取的是GPIO寄存器的基地址,然后通过fsel函数设置相关的三个GPIO口的模式为OUT,然后通过这三个GPIO去初始化MAX7219(同上)。 

 1 static struct file_operations MAX7219_cdev_fops = {
 2     .owner = THIS_MODULE,
 3     .open = MAX7219_open,
 4     .write = MAX7219_write,
 5     .release = MAX7219_release,
 6 };
 7 
 8 static int MAX7219_init(void)
 9 {
10     int ret;
11 
12     MAX7219_dev_id = MKDEV(major, 0);
13     if(major)    //static allocate 
14         ret = register_chrdev_region(MAX7219_dev_id, 1, DRIVER_NAME);
15     else    //dynamic allocate
16     {
17         ret = alloc_chrdev_region(&MAX7219_dev_id, 0, 1, DRIVER_NAME);
18         major = MAJOR(MAX7219_dev_id);
19     }
20 
21     if(ret < 0)
22         return ret;
23 
24     cdev_init(&MAX7219_cdev, &MAX7219_cdev_fops);    //initialize character dev
25     cdev_add(&MAX7219_cdev, MAX7219_dev_id, 1);                //register character device
26     MAX7219_class = class_create(THIS_MODULE, DRIVER_NAME);    //create a class
27     device_create(MAX7219_class, NULL, MAX7219_dev_id, NULL, DRIVER_NAME);    //create a dev
28 
29     s_pGpioRegisters = (struct GpioRegisters *)__io_address(GPIO_BASE);
30 
31     printk("address = %x\n", (int)__io_address(GPIO_BASE));
32     
33     //gpio configure
34     bcm2835_gpio_fsel(pinCLK, 1);
35     bcm2835_gpio_fsel(pinCS, 1);
36     bcm2835_gpio_fsel(pinDIN, 1);
37 
38     //initialize the MAX7219
39     Init_MAX7219();
40 
41     printk("MAX7219 init successfully");
42     return 0;
43 }

 

这个内核模块退出的时候,需要通过device_destroy函数将这个设备删除,以便设备号可以提供给其他的设备宋,并且将其从注册中删除。

1 void MAX7219_exit(void)
2 {
3     device_destroy(MAX7219_class, MAX7219_dev_id);
4     class_destroy(MAX7219_class);
5     unregister_chrdev_region(MAX7219_dev_id, 1);
6     printk("MAX7219 exit successfully\n");
7 }

 

然后就需要编写file_operations中的函数,这里主要定义了用的open,write和release(close)函数,对于read,iocnl等等就没有进行定义。

Open函数比较简单,主要就是判断下文件是否已经打开,如果已经被打开了,那么其将不能被再次打开

 1 static int MAX7219_open(struct inode *inode, struct file *flip)
 2 {
 3     printk("Open the MAX7219 device!\n");
 4     if(state != 0)
 5     {
 6         printk("The file is opened!\n");    
 7         return -1;
 8     }
 9     state++;
10     printk("Open MAX7219 successfully!\n");
11     return 0;
12 }

 

 Release函数和Open相反,将打开的文件关闭即可。

 1 static int MAX7219_release(struct inode *inode, struct file *flip)
 2 {
 3     printk("Close the MAX7219 device!\n");
 4     if(state == 1)
 5     {
 6         state = 0;
 7         printk("Close the file successfully!\n");
 8         return 0;
 9     }
10     else
11     {
12         printk("The file has closed!\n");
13         return -1;
14     }
15 }

 

这里主要是write函数,其将用户送来的一个字符需要显示在MAX7219上,其显示的方法和前面的虚拟文件操作基本相同,只是GPIO口操作调用的函数不同。其首先需要将用户空间传递过来的参数通过copy_from_user函数拷贝到内核空间上,然后将调用相关的显示函数进行显示即可。

对于MAX7219的显示函数这里就没有详细叙述,整个过程都和虚拟文件操作一样,只要将上面的接口改成寄存器操作的接口即可。

 1 static ssize_t MAX7219_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
 2 {
 3     printk("Write %s into the MAX7219\n", buf);
 4     int ret, i;
 5     char ch;
 6     if(len == 0)
 7         return 0;
 8     
 9     if(copy_from_user(&ch, (void *)buf, 1))
10         ret = -EFAULT;
11     else
12     {
13         int index;
14         if(ch >= 0 && ch <= 9)
15             index = ch - 0;
16         else if(ch >= A && ch <= Z)
17             index = ch - A + 10;
18         else if(ch >= a && ch <= z)
19             index = ch - a + 10;
20         else
21             index = 36;    //unknown display 中
22         printk("Write character %c, index=%d\n", ch, index);
23         for(i=0;i<8;i++)
24             Write_Max7219(i+1, codeDisp[index][i]);
25 
26         ret = 1;    //write a character
27     }
28 
29     return ret;
30 }

 

编写完最后,编写Makefile文件进行编译,这个和上次实验内容相同,这里不再赘述。需要注意的是内核的模块版本号必须要和编译的相同,否则会出现ukonwn parameters之类的错误。上次做好久把内核删掉的只能重新编译一次内核了。

 1 ARCH        := arm
 2 CROSS_COMPILE    := arm-linux-gnueabi-
 3 
 4 CC := $(CROSS_COMPILE)gcc
 5 LD := $(CROSS_COMPILE)ld
 6 
 7 obj-m := gpio_chdev.o
 8 
 9 KERNELDIR := /home/jack/Documents/course/EmbededSystem/RaspberrySource/modules/lib/modules/4.4.11/build
10 PWD = $(shell pwd)
11 
12 all:
13     make -C $(KERNELDIR) M=$(PWD) modules
14 clean:
15     rm -f *.o *.mod.c *.symvers *.order

 

编写如下的测试程序,以此显示A-Z以及0-9在MAX7219上面。

 1 #include <stdio.h>  
 2 #include <stdlib.h>  
 3 #include <unistd.h>  
 4 #include <sys/ioctl.h>  
 5 #include <sys/time.h>
 6 #include <sys/fcntl.h>
 7 #include <sys/stat.h>  
 8 
 9 void Delay_xms(uint x)
10 {
11     uint i,j;
12     for(i=0;i<x;i++)
13         for(j=0;j<50000;j++);
14 }
15 
16 int main(int argc, char **argv)  
17 {  
18     int fd; 
19     int ret; 
20 
21     fd = open("/dev/MAX7219", O_WRONLY);  
22     if (fd < 0) 
23     {  
24         fprintf(stderr, "Fail to open /dev/MAX7219!\n");
25         exit(1);
26     } 
27     char buff[1];
28     int i=0;
29     for(i=0;i<26;i++)
30     {
31         buff[0] = a + i;
32         if((ret = write(fd, buff, 1))<0)
33         {
34             fprintf(stderr, "Fail to write /dev/MAX7219! %d\n", ret);
35             break;
36         }
37         Delay_xms(1000);
38     }
39     for(i=0;i<10;i++)
40     {
41         buff[0] = 0 + i;
42         if((ret = write(fd, buff, 1))<0)
43         {
44             fprintf(stderr, "Fail to write /dev/MAX7219! %d\n", ret);
45             break;
46         }
47         Delay_xms(1000);
48     }
49     fprintf(stdout, "Write /dev/MAX7219 successfully! %d\n", ret);
50     close(fd);  
51     return 0;  
52 }  

 

 

源代码下载

 

 

 参考链接:

基于树莓派的字符设备内核驱动程序框架编写

树莓派linux启动学习之LED控制

Creating a Basic LED Driver for Raspberry Pi

BCM2835-ARM-Peripherals.pdf

 

 

 

 

 

 

 

 

 

 

 

 

树莓派上的GPIO字符驱动程序

标签:

原文地址:http://www.cnblogs.com/jackwang822/p/5558046.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!