在Linux系统里操作GPIO的方法有以下几个:
1. 写驱动的方式
需要自己编写linux驱动,在驱动里操控GPIO,应用通过驱动提供的节点来控制GPIO。优点是结构清晰,可扩展性强,比较灵活。缺点是实现的过程比较繁琐。
编写驱动可能会用到的kernel函数:
检查GPIO是不是有效的:
int gpio_is_valid(int number);
设备GPIO的方向(输入/输出):
/* set as input or output, returning 0 or negative errno */
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
请求GPIO的资源:
/* request GPIO, returning 0 or negative errno.
* non-null labels may be useful for diagnostics.
*/
int gpio_request(unsigned gpio, const char *label);
清除GPIO资源:
/* release previously-claimed GPIO */
void gpio_free(unsigned gpio);
读取GPIO的状态:
/* GPIO INPUT: return zero or nonzero */
int gpio_get_value(unsigned gpio);
设备GPIO的状态:
/* GPIO OUTPUT */
void gpio_set_value(unsigned gpio, int value);
详情请参考linuxdir/documentation/gpio.txt
2. 通过linux提供的用户空间
通过在用户空间上来操作GPIO,控制入口在:/sys/class/gpio/。不过需要打开kernel中的相应的选项:Device Drivers->GPIO Support->/sys/class/gpio/... (sysfs nterface)。
优点是简单方便,缺点是可扩展性差。
往export中写入GPIO的号,将会打开Kernel提供的GPIO的用户空间入口。
例如: "echo 19 > export" 将会创建一个 "gpio19" 的文件夹。
往unexport文件中写入GPIO号,将会关闭之前用export打开的入口。
例如: "echo 19 > unexport" 将会回收"gpio19"的文件夹。
GPIO文件夹中的属性有以下几个(如/sys/class/gpio/gpio19)
/sys/class/gpio/gpioN/
"direction" 用来设置或读取GPIO的方向。可以设置成"in" 或者 "out".
"value" 用来设置或读取GPIO的值:0或者1. "echo 1 > value"将会把GPION设置成高电平。
"edge" 用来设置中断类型。可以是"none", "rising", "falling", 或者
"both". 只有中断类型可以配置的GPIO才会有这个属性。
"active_low" 设置低电平有效。可以是0或1。
详情请参考linuxdir/documentation/gpio.txt
3. 直接操作/dev/mem
可以直接对/dev/mem节点的内存进行读写来操作GPIO。不过需要参考CPU的datasheet,找到相应的GPIO的寄存器地址。
优点是方便,灵活,缺点是GPIO的寄存器地址不易得到。
下面的函数可以得到GPIO的起始地址,然后可以参考datasheet,对GPIO的方向,功能,和值进行设备。只需要对下面函数中得到的gpio的地址进行操作即可。
int GPIOSetup()
{
int fd = -1;
if ((fd = open ("/dev/mem", O_RDWR | O_SYNC) ) < 0)
{
printf ("wiringPiSetup: Unable to open /dev/mem: %s
", strerror (errno)) ;
return 0;
}
// GPIO:
gpio = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_BASE) ;
if ((int32_t)gpio == -1)
{
printf("wiringPiSetup: mmap (GPIO) failed: %s
", strerror (errno)) ;
return 0;
}
return 1;
}
这个是完整的程序,是在基于Raspberry Pi的平台上进行操作的。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BCM2708_PERI_BASE 0x20000000
#define GPIO_BASE (BCM2708_PERI_BASE + 0x00200000)
#define BLOCK_SIZE
(4*1024)
#define INPUT
0
#define OUTPUT
1
#define LOW
0
#define HIGH
1
static volatile uint32_t *gpio ;
static uint8_t gpioToGPSET [] =
{
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
} ;
// gpioToGPCLR:
// (Word) offset to the GPIO Clear registers for each GPIO pin
static uint8_t gpioToGPCLR [] =
{
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
} ;
// gpioToGPFSEL:
// Map a BCM_GPIO pin to it's Function Selection
// control port. (GPFSEL 0-5)
// Groups of 10 - 3 bits per Function - 30 bits per port
static uint8_t gpioToGPFSEL [] =
{
0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,3,3,
4,4,4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,5,5,
} ;
// gpioToShift
// Define the shift up for the 3 bits per pin in each GPFSEL port
static uint8_t gpioToShift [] =
{
0,3,6,9,12,15,18,21,24,27,
0,3,6,9,12,15,18,21,24,27,
0,3,6,9,12,15,18,21,24,27,
0,3,6,9,12,15,18,21,24,27,
0,3,6,9,12,15,18,21,24,27,
} ;
int GPIOSetup()
{
int fd = -1;
if ((fd = open ("/dev/mem", O_RDWR | O_SYNC) ) < 0)
{
printf ("wiringPiSetup: Unable to open /dev/mem: %s
", strerror (errno)) ;
return 0;
}
// GPIO:
gpio = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_BASE) ;
if ((int32_t)gpio == -1)
{
printf("wiringPiSetup: mmap (GPIO) failed: %s
", strerror (errno)) ;
return 0;
}
return 1;
}
int GPIOSetPinMode(int nPin, int mode)
{
int fSel = 0, shift = 0;
fSel = gpioToGPFSEL [nPin] ;
shift = gpioToShift [nPin] ;
if (mode == INPUT)
*(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) ; // Sets bits to zero = input
else if (mode == OUTPUT)
*(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (1 << shift) ;
return 0;
}
int GPIOSetPin(int nPin, int value)
{
if (value == LOW)
*(gpio + gpioToGPCLR [nPin]) = 1 << (nPin & 31) ;
else
*(gpio + gpioToGPSET [nPin]) = 1 << (nPin & 31) ;
}
注:由于作者的能力有限,所以如有错误之处,欢迎指正。