如何检测Linux板上GPIO的引脚变化 [英] how to detect a pin change of a GPIO on Linux board
问题描述
我在基于ARM的linux板(imx233 CPU)上使用3.12内核.我的目的是检测GPIO的引脚变化(1到0).
我可以不断调用以下函数(在while(1)循环中)读取引脚值
int GPIO_read_value(int pin){
int gpio_value = 0;
char path[35] = {'\0'};
FILE *fp;
sprintf(path, "/sys/class/gpio/gpio%d/value", pin);
if ((fp = fopen(path,"rb+")) == NULL){ //echo in > direction
//error
}
fscanf(fp, "%d", &gpio_value);
fclose(fp);
return gpio_value;
}
但是这会给CPU带来过多的负载.我不使用usleep
或nanosleep
,因为引脚更改发生的时间很短,这会导致我错过该事件.
据我所知,不可能使用poll()
.是否可以使用任何类似于poll()
的功能来检测GPIO的引脚变化?
以防万一,如果我做错了,这是我的poll()
用法,它不能检测到引脚变化
struct pollfd pollfds;
int fd;
int nread, result;
pollfds.fd = open("/sys/class/gpio/gpio51/value", O_RDWR);
int timeout = 20000; /* Timeout in msec. */
char buffer[128];
if( pollfds.fd < 0 ){
printf(" failed to open gpio \n");
exit (1);
}
pollfds.events = POLLIN;
printf("fd opens..\n");
while (1)
{
result = poll (&pollfds, 0, timeout);
switch (result)
{
case 0:
printf ("timeout\n");
break;
case -1:
printf ("poll error \n");
exit (1);
default:
printf("something is happening..\n");
if (pollfds.revents & POLLIN)
{
nread = read (pollfds.fd, buffer, 8);
if (nread == 0) {
printf ("result:%d\n", nread);
exit (0);
} else {
buffer[nread] = 0;
printf ("read %d from gpio: %s", nread, buffer);
}
}
}
}
close(fd);
https://developer.ridgerun.com/wiki/上的代码index.php/Gpio-int-test.c 在poll()
下工作正常,我需要为中断定义上升沿/下降沿,并对定义进行一些修复.它解决了我的问题,但是,对我和其他一些人来说,听到/知道其他选择方法可能会很有帮助.
我以前从未见过此板,但是我猜想PIC已经完全实现了该板的功能(通常就是这样),但是您必须在GPIO中另外配置中断控制器(通常就是这样). 某些部分应作为内核模块完成,然后您必须将有关中断的信息传递给应用程序.
执行此操作的示例方法是将以下内容实现为内核模块:
-
设置GPIO控制器以启用特定端口和级别上的中断 (如何执行此操作,您可以在这里找到: http://cache.freescale .com/files/dsp/doc/ref_manual/IMX23RM.pdf 37.2.3.3输入中断操作)
-
在PIC中启用GPIO中断(操作方法: http://lwn.net/images/pdf/LDD3/ch10.pdf 第10章)
- 实现中断处理例程(我将在下面进行一些说明)
- 为模块实现ioctl接口.
和其余的应用程序:
- 可以与中断配合使用的功能.
从内核向应用程序传递有关中断的信息的最简单方法是通过内核端的信号量. 在模块中,您可以实现一个ioctl,它将一直休眠直到发生中断. 因此,应用程序将调用此ioctl,并且其线程将被阻塞,直到发生中断为止.
在内部模块中,中断例程应检查应用程序线程是否现在被阻塞,以及up()信号是否被阻塞.
编辑*****
此CPU的SSP具有SPI的工作模式.为什么不使用它?
I am using 3.12 kernel on an ARM based linux board (imx233 CPU). My purpose is to detect pin change of a GPIO (1 to 0).
I can read the pin value constantly calling the below function (in a while(1) loop)
int GPIO_read_value(int pin){
int gpio_value = 0;
char path[35] = {'\0'};
FILE *fp;
sprintf(path, "/sys/class/gpio/gpio%d/value", pin);
if ((fp = fopen(path,"rb+")) == NULL){ //echo in > direction
//error
}
fscanf(fp, "%d", &gpio_value);
fclose(fp);
return gpio_value;
}
But it causes too much load to the CPU. I don't use usleep
or nanosleep
, because the pin change happens for a very short of a time that would cause me to miss the event.
As far as I find out, it is not possible to use poll()
. Is there any poll()
like function that I can use to detect a pin change of a GPIO?
EDIT: Just in case, if I am doing something wrong, here is my poll()
usage that does not detect the pin change
struct pollfd pollfds;
int fd;
int nread, result;
pollfds.fd = open("/sys/class/gpio/gpio51/value", O_RDWR);
int timeout = 20000; /* Timeout in msec. */
char buffer[128];
if( pollfds.fd < 0 ){
printf(" failed to open gpio \n");
exit (1);
}
pollfds.events = POLLIN;
printf("fd opens..\n");
while (1)
{
result = poll (&pollfds, 0, timeout);
switch (result)
{
case 0:
printf ("timeout\n");
break;
case -1:
printf ("poll error \n");
exit (1);
default:
printf("something is happening..\n");
if (pollfds.revents & POLLIN)
{
nread = read (pollfds.fd, buffer, 8);
if (nread == 0) {
printf ("result:%d\n", nread);
exit (0);
} else {
buffer[nread] = 0;
printf ("read %d from gpio: %s", nread, buffer);
}
}
}
}
close(fd);
EDIT2: the code on https://developer.ridgerun.com/wiki/index.php/Gpio-int-test.c works fine with poll()
I needed to define the rising/falling edge for the interrupt and a little bit fix on the definition. It solves my problem, however, it might be good for me and some other people to hear/know the alternative methods.
I have never seen this board before, however I guess PIC is fully implemented for this board (usually is like that) but you have to configure interrupt additionally in GPIO controller (usually is like that). Some part should be done as a kernel module, then you have to pass information about interrupt to you application.
Example way to do this is to implement following thing as a kernel module:
setup GPIO controller to enable interrupt on particular port and level (how to do this you can find here: http://cache.freescale.com/files/dsp/doc/ref_manual/IMX23RM.pdf 37.2.3.3 Input Interrupt Operation)
enable GPIO interrupt in PIC (how to do this: http://lwn.net/images/pdf/LDD3/ch10.pdf Chapter10)
- implement interrupt handling routine (I will describe a little bit below)
- implement ioctl interfaces for your module.
and a rest in your application:
- a function that can coosomeoneperate with interrupt.
Simplest way of passing information about interrupt from kernel to app is by semaphore on kernel side. in module you can implement an ioctl that will sleep until interrupt happen. So application will call this ioctl and its thread will be blocked until interrupt happen.
Inside module, interrupt routine should check if application thread is now blocked, and if so up() semaphore.
EDIT*****
This CPU has SSP that has working mode for SPI. Why dont use it ??
这篇关于如何检测Linux板上GPIO的引脚变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!