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

前言

主要是在嵌入式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

时间: 2024-10-05 20:32:43

树莓派上的GPIO字符驱动程序的相关文章

树莓派4硬件---GPIO篇

树莓派拿到手已经两个多月了,其实从最开始的期待安装好ROS,到前几天完成了ROS的源码编译安装,对linux的调教也时花了些时间的.现在终于想起来,树莓派上还有GPIO,还没有用过了.说干就干,开始.以下操作都是在树莓派已经安装好了官方的系统,也已经升级到了最新版了.总共有40个GPIO,实际管脚图为: 在计算机中,通常用高.低两个电压来表示二进制的1和0.树莓派也是如此.GPIO用相同的方式来表示数据.每个GPIO的PIN都能处于输入或输出状态.当处于输出状态时,系统可以把1或0传给该PIN.

小白的linux字符驱动程序

关于如何编译一个测试型的字符驱动程序,网上一搜还是很多的在此给出一个不错的教程http://blog.chinaunix.net/uid-11829250-id-337300.html 我主要是在搜索ioctl的时候才有自己编写一个字符驱动的想法,因为刚工作的时候就看到有同事在用ioctl,当时在网上搜了下ioctl也没怎么明白.现在才发现原来ioctl就是对应的设备驱动程序里的ioctl函数.好了,废话就不多说了.写这篇文档的主要意义在于给后来编写驱动程序的新手们提示几个可能会遇到的问题,希望

树莓派文档翻译 - 使用 - GPIO: 树莓派A和B

https://www.raspberrypi.org/documentation/usage/gpio/README.md 2016/6/25 GPIO: 树莓派A和B ##介绍GPIO和在树莓派上进行物理编程 树莓派的强大特性就是那一排在板子周围的GPIO(一般目的的输入输出)插针,就在黄色的视频输出插孔的旁边. GPIO pins 这些pin是pi和外界世界的物理接口.最简单,你可以把他们当作可以打开和关闭的开关(输入)或者树莓派可以打开或者关闭(输出).26个插针的17个是GPIO插针,

在树莓派上用Python控制LED

所需材料 一个已经安装配置好了的树莓派 连接控制树莓派所用的其他必须设备 200Ω电阻 x 8 led x 8 面包板及连接线若干 电路连接 电路图 按照电路图所示,在面包板上进行连接. 编写程序 安装RPi.GPIO 首先得确定RPi.GPIO已安装.最新的系统已经自带了,如果没有的可以使用命令 sudo apt-get update sudo apt-get install python-dev python-rpi.gpio 来安装 编写模块 用文本编辑器新建一个led.py文件.(高手们

跑在树莓派上智能家居雏形

跑在树莓派上智能家居雏形 简介 一个以微信为终端的好玩的小东西 可以实现的功能 可以实现以手机微信端对树莓派终端进行实时监控.摄像头云台操纵.闯入报警.温度检测.灯光控制.自动光线控制等功能 设备图片 运行截图 需要用到的所有硬件 路由器 树莓派主板 树莓派电源(5V 2A) 至少 8g tf卡 (推荐class 10,8g足矣) 支持ouv的摄像头(罗技C170) 花生棒及电源(或用花生壳内网版代替) 乐高积木(小颗粒) 两根网线 温度传感器(DHT11) 光线传感器(光敏电阻模块) 人体红外

在树莓派上使用火焰,声音,震动,光敏传感器

作为一个软件工程专业的学生,对传感器等硬件的使用一直不太顺手,而在树莓派使用Python的RPi.GPIO,进行传感器等硬件的使用却是非常方便.而且使用树莓派这个网络功能强大的控制中心,其在物联网方面的使用也将更加广泛. 这次我主要使用了火焰,声音,震动,光敏这四个传感器进行了简单的测试,对其进行扩展组合使用就靠大家的想法了.话不多说,下面开始. 传感器 火焰传感器 用途: 各种火焰,火源探测 模块特色: 1. 可以检测火焰或者波长在760纳米-1100纳米范围内的光源,打火机测试火焰距离为80

在树莓派上安装YCM(YouCompleteMe)

? 太折腾了!如果你搜索到了这篇文章,建议直接使用SpaceVim作为替代品. 0x0.第一个坑--不支持Python ? 我的树莓派的系统是基于Debian Stretch的Raspbian Stretch.使用apt-get安装的vim不支持python脚本.你可以通过输入: 1 vim --version | grep python 来检查,如果发现python/python3前面出现了加号,恭喜你,你可以跳过本步骤了.它说明你的vim支持python.如果都是减号,你需要重新编译安装vi

树莓派上使用蚂蚁矿机挖矿

家中以前一直使用的电脑+矿机挖矿,电脑24小时开着不说,声音还特大: 前两天折腾了一把,将挖矿整体转到了树莓派上,而且整个都放到了书柜上. 世界一下子安静了(电表也安静了)~~~~ 树莓派开机后,先安装CGMiner的关联组件(已经先执行过Update & Upgrade操作) sudo apt-get install libusb-1.0-0-dev libusb-1.0-0 libcurl4-openssl-dev libncurses5-dev libudev-dev screen lib

[转]在树莓派上搭建LAMP服务

之前介绍过树莓派上LNMP环境的搭建方法,本文将详细介绍如何在树莓派上配置LAMP服务. 为LAMP,是最流行的服务器配置之一,LAMP的含义是: Linux - 操作系统 Apache - 网络服务器(HTTP)软件 Mysql - 数据库服务 PHP or Perl - 编程语言 这种配置对于大多数树莓派用户来说,可能过于重量级了,但是大多数用户能学到服务器的配置过程,是一种学习配置服务器的好方法.我可能会在以后写一篇轻量级服务器的配置文章. 所有的配置都在命令行下完成.这可能会比点鼠标难一