Linux下對GPIO的操作控制(基於GPIO子系統)

 

 

概述

以前學習了LED和按鍵驅動,實際上,在Linux中實現這些設備驅動,有一種更爲推薦的方法,就是GPIO子系統和Input子系統。GPIO子系統可以控制IO的初始化、輸出高低電平值,讀取IO的輸入電平;Input子系統處理輸入事務,任何輸入設備的驅動程序都可以通過Input輸入子系統提供的接口註冊到內核,利用子系統提供的功能來與用戶空間交互。例如控制LED、讀取按鍵、觸摸屏、鼠標都可以通過這些子系統接口實現。

GIPO子系統介紹

gpio子系統相關描述可在內核源碼 Documentation/gpio 瞭解。
在/sys/class/gpio目錄下展示該子系統設備。如果沒有請在編譯內核的時候加入 Device Drivers-> GPIO Support ->/sys/class/gpio/… (sysfs interface)。

export 和 unexport:把某個GPIO導出用戶空間和取消導出用戶空間。

  • 例如:echo 19 > export,把GPIO19導出到用戶空間。
  • GPION,其中N值的計算方式:
    假設一組端口有32個GPIO,N=index = GPIOx_y= (x-1)*32 +y;
    例如一:GPIOB_6,對應的N值爲(2-1)*32+6=38;
    例如二:GPIOD_3,對應的N值爲(4-1)*32+3=99。

進入子系統設備目錄,包含一些常用屬性:

direction:gpio端口的方向,內容可以是 in 或者 out;

  • 示例:echo out > /sys/class/gpio/pioD3/direction
  • 如果寫入 low 或者 hight,則不僅設置爲輸出,還同時設置了輸出電平(本小點要了解當前內核是否支持)

value:gpio引腳的電平,0爲低電平,非0爲高電平。

  • 如果配置爲輸入,則該文件可寫;
  • 如果配置爲中斷,則可以調用poll(2)函數監聽該中斷,中斷觸發後poll(2)函數就會返回。

edge: 中斷的觸發方式,該文件有4個值。

  • none:輸入,非中斷
  • rising:中斷輸入,上升沿觸發
  • falling:中斷輸入,下降沿觸發
  • both:中斷輸入,邊沿觸發
    gpio子系統與led子系統是不同,但是類似。
  • led只能輸出,gpio能輸出也能輸入
  • gpio輸入還支持中斷功能
  • 使用gpio前,需要從內核空間暴露到用戶空間
    注意:在用戶空間控制使用gpio時,注意不要濫用和內核一些綁定好、已經有合適內核啓動的引腳衝突。參考 Documentation/driver-api/gpio/drivers-on-gpio.rst

示例:通過GPIO子系統控制LED(GPIOA29)

system函數進行操作:

void main(void)
{
	if(access("/sys/class/gpio/pioA29/value",F_OK) != 0)
	{
		/*使能LED設備*/
		system("echo 29 > /sys/class/gpio/export"); 	
	}
	/*設置LED輸出*/
	system("echo out > /sys/class/gpio/pioA29/direction");
	/*打開LED*/
	system("echo '1' > /sys/class/gpio/pioA29/value");	
	sleep(1);
	/*關閉LED*/
	system("echo '0' > /sys/class/gpio/pioA29/value");
}

文件操作實現:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
	FILE *p=NULL;
	int i=0;
	p = fopen("/sys/class/gpio/export","w");
	fprintf(p,"%d",29);
	fclose(p);
	p = fopen("/sys/class/gpio/pioA29/direction","w");
	fprintf(p,"out");
	fclose(p);
	for(i=0;i<100;i++)
	{
		p = fopen("/sys/class/gpio/pioA29/value","w");
		fprintf(p,"%d",1);
		sleep(1);
		fclose(p);
		p = fopen("/sys/class/gpio/pioA29/value","w");
		fprintf(p,"%d",0);
		sleep(1);
		fclose(p);
	}
	p = fopen("/sys/class/gpio/unexport","w");
	fprintf(p,"%d",38);
	fclose(p);
	return 0;
}

示例:通過GPIO子系統中斷讀取IO值(GPIOC2)

#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
 /*按鍵按下時,打按鍵值*/
int main()
{
	FILE *fp=NULL; /*定義一個文件指針*/
	if(access("/sys/class/gpio/pioC2",F_OK) != 0)
	{
		/*使能GPIO子系統設備*/
		fp=fopen("/sys/class/gpio/export","w");
		if(fp == NULL)
		{
			perror("open export fail\r\n");
			return -1;
		}
		fprintf(fp,"%d",66);
		fclose(fp);
	}
	/*設置爲輸入*/
	fp=fopen("/sys/class/gpio/pioC2/direction","w");
	if(fp == NULL)
	{
		perror("open direction fail\r\n");
		return -1;
	}
	fprintf(fp,"in");
	fclose(fp);
	
	/*設置中斷觸發邊沿*/
	fp=fopen("/sys/class/gpio/pioC2/edge","w");
	if(fp == NULL)
	{
		perror("open edge fail\r\n");
		return -1;
	}
	fprintf(fp,"both");
	fclose(fp);
	
	/*對IO值進行poll偵聽*/
	int fd=open("/sys/class/gpio/pioC2/value",O_RDONLY);
    if(fd<0)
    {
        perror("open '/sys/class/gpio/pioC2/value' failed!\n");  
        return -1;
    }
    struct pollfd fds[1];
    fds[0].fd=fd;
    fds[0].events=POLLPRI;
    while(1)
    {
        if(poll(fds,1,0)==-1)
        {
            perror("poll failed!\n");
            return -1;
        }
        if(fds[0].revents&POLLPRI)
        {
            if(lseek(fd,0,SEEK_SET)==-1)
            {
                perror("lseek failed!\n");
                return -1;
            }
            char buffer[16];
            int len;
            if((len=read(fd,buffer,sizeof(buffer)))==-1)
            {
                perror("read failed!\n");
                return -1;
            }
            buffer[len]=0;
            printf("%s",buffer);
        }
    }
}
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章