SysFs方式下C语言控制GPIO(RK3399)

46 篇文章 6 订阅

 在单片机上异常简单的LED 闪灯程序在Linux OS 下居然会异常麻烦,网络上介绍linux 控制GPIO的文章,各种说法都有。而且大都是shell 命令控制,而不是C语言如何控制。 在树莓PI 中,有一个wiringpi 库,而且资料非常清楚。但是其它厂商的东西就凌乱了。我同时使用瑞芯微的RK3399 PC 板和一块全志低成本的H6 板。方式各不相同。终于搞得明白了点,将内容分享给大家。

 Linux 下的设备驱动模型主要包含:类(class)、总线(bus)、设备(device)、驱动(driver)。SysFs文件系统用来表示设备的结构.将设备的层次结构形象的反应到用户空间中。应用程序通过访问SysFs中的文件来控制硬件设备。例如GPIO,SPI和I2C 等等。

GPIO 端口操作

gpio 的文件存放在/sys/class/gpio 中。不过不同的SOC 有些不同,在RK3399 的ubuntu OS 中,gpio 下的目录为:

gpiochip0

gpiochip32

gpiochip64

gpiochip96

gpiochip128

  /sys/class/gpio/gpiochipX目录保存系统中GPIO寄存器的信息,包括每个寄存器控制引脚的起始编号 base,寄存器名称,引脚总数

计算引脚编号

在SysFs 中是采用gpio+GPIO 引脚编号来控制GPIO 端口的,首先需要将一个物理gpio的名称,转换为一个GPIO 的编号。在RK3399 中,gpio的物理编号为 GPIO0_B2_d 或者GPIO0_A3_d。其中0 是gpiochip 的编号。计算此引脚编号的方法如下:

引脚编号 = 控制引脚的寄存器基数 + 控制引脚寄存器位数

例如:GPIO0_A3_d 的编号为 3

在全志H6 SOC 中,GPIO 的物理名称为PC13,PH3 这样的方式。GPIO 引脚的编号方法为:

以PC13 为例

P=Pin 可以或略

C=端口字母,该字母在字母表中的位置(从0开始)称为端口号

13=硬件号

GPIO 编号=端口号*32+引脚号

PC13=2*32+13=77

例如PH7=17*32+7=231

PL02=11*32+2=354

产生gpio3 文件

获得了引脚编号之后,要向/sys/class/gpio/export写入此编号,比如12号引脚,在Linux shell中可以通过以下命令实现,
echo 3 > /sys/class/gpio/export
  命令成功后生成/sys/class/gpio/gpio3目录,如果没有出现相应的目录,说明此引脚不可导出:

设置GPIO 的方向

direction文件,定义输入输入方向,可以通过下面命令定义为输出

 echo out > /sys/class/gpio/gpio12/direction
direction接受的参数:in, out, high, low。high/low同时设置方向为输出。

设置GPIO 的输出值    

echo 1 >/sys/class/gpio/gpio3/value

echo 0 >/sys/class/gpio/gpio3/value

  C 语言程序

  上面介绍的方法是采用shell 命令来实现的,下面介绍C 语言的程序实现。在RK3399 PC 的GPIO 扩展槽2脚是GPIO0_A3_d ,其引脚编号位 3。在上面连接了一个LED 串联一个150欧姆的电阻到地(扩展槽3脚)

下面的程序是在RK3399 PC 板上调试通过的,请安心引用。

gpiolib.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/stat.h>
#include "gpiolib.h"

int gpio_direction(int gpio, int dir)
{
	int ret = 0;
	char buf[50];
	sprintf(buf, "/sys/class/gpio/gpio%d/direction", gpio);
	int gpiofd = open(buf, O_WRONLY);
	if(gpiofd < 0) {
		perror("Couldn't open IRQ file");
		ret = -1;
	}

	if(dir == 2 && gpiofd){
		if (3 != write(gpiofd, "high", 3)) {
			perror("Couldn't set GPIO direction to out");
			ret = -2;
		}
	}

	if(dir == 1 && gpiofd){
		if (3 != write(gpiofd, "out", 3)) {
			perror("Couldn't set GPIO direction to out");
			ret = -3;
		}
	}
	else if(gpiofd) {
		if(2 != write(gpiofd, "in", 2)) {
			perror("Couldn't set GPIO directio to in");
			ret = -4;
		}
	}

	close(gpiofd);
	return ret;
}

int gpio_setedge(int gpio, int rising, int falling)
{
	int ret = 0;
	char buf[50];
	sprintf(buf, "/sys/class/gpio/gpio%d/edge", gpio);
	int gpiofd = open(buf, O_WRONLY);
	if(gpiofd < 0) {
		perror("Couldn't open IRQ file");
		ret = -1;
	}

	if(gpiofd && rising && falling) {
		if(4 != write(gpiofd, "both", 4)) {
			perror("Failed to set IRQ to both falling & rising");
			ret = -2;
		}
	} else {
		if(rising && gpiofd) {
			if(6 != write(gpiofd, "rising", 6)) {
				perror("Failed to set IRQ to rising");
				ret = -2;
			}
		} else if(falling && gpiofd) {
			if(7 != write(gpiofd, "falling", 7)) {
				perror("Failed to set IRQ to falling");
				ret = -3;
			}
		}
	}

	close(gpiofd);

    return ret;
}

int gpio_export(int gpio)
{
	int efd;
	char buf[50];
	int gpiofd, ret;

	/* Quick test if it has already been exported */
	sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
	efd = open(buf, O_WRONLY);
	if(efd != -1) {
		close(efd);
		return 0;
	}

	efd = open("/sys/class/gpio/export", O_WRONLY);

	if(efd != -1) {
		sprintf(buf, "%d", gpio); 
		ret = write(efd, buf, strlen(buf));
		if(ret < 0) {
			perror("Export failed");
			return -2;
		}
		close(efd);
	} else {
		// If we can't open the export file, we probably
		// dont have any gpio permissions
		return -1;
	}
	return 0;
}

void gpio_unexport(int gpio)
{
	int gpiofd, ret;
	char buf[50];
	gpiofd = open("/sys/class/gpio/unexport", O_WRONLY);
	sprintf(buf, "%d", gpio);
	ret = write(gpiofd, buf, strlen(buf));
	close(gpiofd);
}

int gpio_getfd(int gpio)
{
	char in[3] = {0, 0, 0};
	char buf[50];
	int gpiofd;
	sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
	gpiofd = open(buf, O_RDWR);
	if(gpiofd < 0) {
		fprintf(stderr, "Failed to open gpio %d value\n", gpio);
		perror("gpio failed");
	}

	return gpiofd;
}

int gpio_read(int gpio)
{
	char in[3] = {0, 0, 0};
	char buf[50];
	int nread, gpiofd;
	sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
	gpiofd = open(buf, O_RDWR);
	if(gpiofd < 0) {
		fprintf(stderr, "Failed to open gpio %d value\n", gpio);
		perror("gpio failed");
	}

	do {
		nread = read(gpiofd, in, 1);
	} while (nread == 0);
	if(nread == -1){
		perror("GPIO Read failed");
		return -1;
	}
	
	close(gpiofd);
	return atoi(in);
}

int gpio_write(int gpio, int val)
{	
	char buf[50];
	int nread, ret, gpiofd;
	sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
	gpiofd = open(buf, O_RDWR);
	if(gpiofd > 0) {
		snprintf(buf, 2, "%d", val);
		ret = write(gpiofd, buf, 2);
		if(ret < 0) {
			perror("failed to set gpio");
			return 1;
		}

		close(gpiofd);
		if(ret == 2) return 0;
	}
	return 1;
}


int gpio_select(int gpio)
{
	char gpio_irq[64];
	int ret = 0, buf, irqfd;
	fd_set fds;
	FD_ZERO(&fds);

	snprintf(gpio_irq, sizeof(gpio_irq), "/sys/class/gpio/gpio%d/value", gpio);
	irqfd = open(gpio_irq, O_RDONLY, S_IREAD);
	if(irqfd < 1) {
		perror("Couldn't open the value file");
		return -13;
	}

	// Read first since there is always an initial status
	ret = read(irqfd, &buf, sizeof(buf));

	while(1) {
		FD_SET(irqfd, &fds);
		ret = select(irqfd + 1, NULL, NULL, &fds, NULL);
		if(FD_ISSET(irqfd, &fds))
		{
			FD_CLR(irqfd, &fds);  //Remove the filedes from set
			// Clear the junk data in the IRQ file
			ret = read(irqfd, &buf, sizeof(buf));
			return 1;
		}
	}
}

gpio.h

#ifndef _GPIOLIB_H_

/* returns -1 or the file descriptor of the gpio value file */
int gpio_export(int gpio);
/* Set direction to 2 = high output, 1 low output, 0 input */
int gpio_direction(int gpio, int dir);
/* Release the GPIO to be claimed by other processes or a kernel driver */
void gpio_unexport(int gpio);
/* Single GPIO read */
int gpio_read(int gpio);
/* Set GPIO to val (1 = high) */
int gpio_write(int gpio, int val);
/* Set which edge(s) causes the value select to return */
int gpio_setedge(int gpio, int rising, int falling);
/* Blocks on select until GPIO toggles on edge */
int gpio_select(int gpio);

/* Return the GPIO file descriptor */
int gpio_getfd(int gpio);

#endif //_GPIOLIB_H_

main.c

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "gpiolib.h"

int main(int argc, char **argv)  {
    int gpio_pin = 3;

    gpio_export(gpio_pin);    
    gpio_direction(gpio_pin, 1);

    for(int i = 0; i < 5; i++) {
        printf(">> GPIO %d ON\n", gpio_pin);
        gpio_write(gpio_pin, 1);

        sleep(1);

        printf(">> GPIO %d OFF\n", gpio_pin);
        gpio_write(gpio_pin, 0);

        sleep(1);
    }
    
    return 0;
}

 

  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值