嵌入式Linux系统中对GPIO操作的方法总结

在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\n", 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\n", strerror (errno)) ;
return 0;
}
return 1;
}


这个是完整的程序,是在基于Raspberry Pi的平台上进行操作的。
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ioctl.h>


#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\n", 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\n", 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) ;

}

注:由于作者的能力有限,所以如有错误之处,欢迎指正。


  • 0
    点赞
  • 0
    评论
  • 6
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

seastars2010

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值