树莓派 GPIO Input 部分高级应用技巧

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树莓派 GPIO Input 部分高级应用技巧相关的知识,希望对你有一定的参考价值。

参考技术A

此篇为 《树莓派使用基础》 中,RPI.GPIO Input功能高级应用技巧。GPIO的简单使用请参考博文 树莓派3B+ GPIO输入输出使用

目前有几种途径可以在程序中获得 GPIO 的输入信息。

如果在输入针脚上没有连接任何元件,那么它将是“浮动(float)”的。换句话说,因为没有连接任何元件,在按下按钮或开关之前,读取的值是没有意义的。由于电源的波动,获取到的值可能会有很大的变化。

为了解决这个问题,我们需要使用 上拉/下拉电阻 。这样,我们就可设定输入的默认值了。在这里,可以使用硬件或软件对电阻进行上拉/下拉。使用硬件方式,将一个 10K 的电阻连接在输入通道与 3.3V(上拉)或 0V(下拉)之间是常用的做法。而 RPi.GPIO 也允许您通过软件的方式对配置 Broadcom SOC 来达到目的:

或者

通道编号是基于所使用的编号系统所指定的(BOARD 或 BCM)。

边缘的定义为电信号从 LOW 到 HIGH(上升临界值)或从 HIGH 到 LOW(下降临界值)状态的改变。正常情况下,对于输入的值来说,我们更关心的是输入的状态是否发生了改变。这种状态上的改变是很重要的。

为了避免程序在忙于处理其它的事物时而错过了按下按钮的操作,这里有两种方法可以解决:

在检测到边缘时执行线程回调函数

注意,可以输入 GPIO.RISING、GPIO.FALLING、GPIO.BOTH 对边缘进行检测。这种方式的优点是占用 CPU 资源很少,因此系统可以有充裕的资源处理其它事物。

RPi.GPIO 在第二条线程中执行回调函数。这意味着回调函数可以同您的主程序同时运行,并且可以立即对边缘进行响应。例如:

如果需要多个回调函数:

注意,在该示例中,回调函数为顺序运行而不是同时运行。这是因为当前只有一个进程供回调使用,而回调的运行顺序是依据它们被定义的顺序。

可能会注意到,每次按钮按下时,回调操作被调用不止一次。这种现象被称作“开关抖动(switch bounce)”。这里有两种方法解决开关抖动问题:

或者

由于某种原因,若不希望程序检测边缘事件,可以将它停止:

原文链接
参考链接

四 . 树莓派A20 GPIO中断程序编写(1基本处理)

参考技术A

关于按键,在DVK521上为:

我又从一份数据手册中看到,PI7,PI8,PI9是没有外部中断功能的。如下图所示:

还好,目前的按键是通过短接帽来连接PI7 PI9的,那么可以将短接帽拿掉,使用杜邦线连接PI10 PI12。

现在我将Key2连接到PI10上。那么KEY2的中断引脚为EINT22。

配置sys_config.fex文件:

现在使用的树莓派A20,是一个双核A7的芯片,而这个属于SMP架构,中断处理方式也已经和原先的理念大有不同。所以还是要知道关于linux的中断原理,可以从网络中获取相关历史性技术知识。

从A20的数据手册中,可以看到外部中断数到了EINT31。也就是说PIO中断功能有32个。

下面列出的API函数是在 \\linux-sunxi\\arch\\arm\\plat-sunxi\\Sys_config.c中。

1.gpio_request_ex(),获取sys_config.fex中设置的中断IO口。
2.gpio_set_one_pin_io_status(),设置为输入状态。
3.gpio_set_one_pin_pull(),设置输入引脚的上下拉状态。
4.request_irq()注册中断函数。

request_irq()函数:
第一个参数为SW_INT_IRQNO_PIO,表示是外部端口的中断号。
第二个参数为中断处理函数名。
第三个参数为中断方式,如IRQ_TYPE_EDGE_RISING,上升沿触发,而IRQ_TYPE_EDGE_FALLING则是下降沿触发,IRQF_SHARED为共享。
第四个参数为中断名。
第五个参数为中断传递的数据。

1.获取IO中断源信息
由于内核使用的是虚拟地址寻址硬件地址,获取中断源就需要将IO硬件地址空间映射到虚拟地址上。可以使用ioremap(PIO_BASE_ADDRESS, PIO_RANGE_SIZE)进行映射。

2.屏蔽中断源
a.读取中断源寄存器的状态,可以使用readl(映射的IO地址 + PIO_INT_STAT_OFFSET);
b.判断对应的中断,使用writel(reg_val&(1<<(CTP_IRQ_NO)),映射的IO地址 + PIO_INT_STAT_OFFSET);清除状态位。

写到这里,本应该很顺利,可是,在驱动程序加载进内核的时候,明显是报错。错误我就不贴出来了,可是我可以将中断信息附上:

从这里,可以看出来,PIO中断号60已经注册进内核了。我们现在使用的一个IO中断是被包含在里面的。所以,需要在内核中找到sunxi-gpio是怎么去注册中断,而我们就需要将我们的中断程序内容附加到已经注册的中断上去。

在 marsboard\\marsboard-a20-linux-sdk-v1.2\\linux-sunxi\\drivers\\gpio\\Gpio-sunxi.c中我们可以找到函数:

里面最重要的函数是:

最终调用的是:

然而,它又被赋值了:

在一定程度的意义上,gpio-sunxi.c已经将中断基本处理做好了,我们要做的只是和它共享中断。

sys_config.fex文件配置如下:

从上面的实验中,已经发现在request_irq中设置边沿等等触发,在安装ko文件的时候,都会报错,从这里看出,在共享中断的时候,是不允许设置其他的内容的。那么,只能去找A20寄存器中关于io口中断的设置。在这些设置已经设置好的情况下,中断应该就能响应了。这里贴出一个比较简单的驱动程序:

int main(int argc,char *argv[])

int fd;
int val;
fd = open("/dev/key_device",O_RDWR);
if(fd < 0)
printf("---open file error----\\r\\n");
return -1;

ifeq ($(KERNELRELEASE),)
KERNEL_DIR=/home/wityuan/Downloads/MarsBoard-A20-Linux-SDK-V1.2/linux-sunxi
PWD=$(shell pwd)

modules:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
arm-linux-gnueabihf-gcc -o key key.c

modules_install:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install
clean:
rm -rf *.ko *.o .tmp_versions .mod.c modules.order Module.symvers . .cmd
else
obj-m:=key.o
endif

root@marsboard:~# ./key_test
---script.bin key get ok,value:1----
key irq Interrupt
==IRQ_EINT22=
key irq Interrupt
==IRQ_EINT22=
key irq Interrupt
==IRQ_EINT22=
key irq Interrupt
==IRQ_EINT22=
key irq Interrupt
==IRQ_EINT22=
key irq Interrupt
==IRQ_EINT22=
key irq Interrupt
==IRQ_EINT22=
key irq Interrupt
==IRQ_EINT22=
key irq Interrupt
==IRQ_EINT22=
^Ckey irq Interrupt
Other Interrupt
----key close----

root@marsboard:~#
root@marsboard:~#

/* EINT type PIO controller registers */

/* EINT type defines */

static int int_cfg_addr[] = PIO_INT_CFG0_OFFSET,
PIO_INT_CFG1_OFFSET,
PIO_INT_CFG2_OFFSET,
PIO_INT_CFG3_OFFSET;

/* Setup GPIO irq mode (FALLING, RISING, BOTH, etc */

)

/* Enable GPIO interrupt for pin */

)

/* Disable GPIO interrupt for pin */

)

/* Set GPIO pin mode (input, output, etc) /
/
GPIO port has 4 cfg 32bit registers (8 pins each) /
/
First port cfg register addr = port_num * 0x24 */

)

static script_gpio_set_t info;
static unsigned key_handler1;
static unsigned key_handler2;

static struct class *key_class;
static struct device *key_device;
static unsigned int key_major;

static unsigned int key_value;

static void *__iomem gpio_addr = NULL;

static int key_open(struct inode *inode, struct file *filp);
static ssize_t key_read (struct file *, char __user *, size_t, loff_t *);
static ssize_t key_write (struct file *filp, const char __user *buf, size_t len, loff_t *off);
static int key_close(struct inode *inode, struct file *filp);

struct file_operations key_operations =
.owner = THIS_MODULE,
.open = key_open,
.read = key_read,
.write = key_write,
.release = key_close,
;

struct key_str
char *name;
int val;
;

struct key_str g_key_str[2]="key1",0x1,"key2",2;

static irqreturn_t key_irq_handler1(int irq, void *dev_id)

int err;
int reg_val = 0;
int ret_val = 0;

static irqreturn_t key_irq_handler2(int irq, void *dev_id)

static ssize_t key_read (struct file *file, char __user *buf, size_t len, loff_t *off)

unsigned int value = 0;
value = copy_to_user(buf,&key_value,4);

static int key_open(struct inode *inode, struct file *filp)

int err = 0;
int key_test_enabled = 0;
int ret = 0;

static ssize_t key_write (struct file *filp, const char __user *buf, size_t len, loff_t *off)

static int key_close(struct inode *inode, struct file *filp)

SUNXI_MASK_GPIO_IRQ(gpio_addr,IRQ_EINT22);
SUNXI_MASK_GPIO_IRQ(gpio_addr,IRQ_EINT23);

static int __init key_init(void)

key_major = register_chrdev(0, "key_chrdev", &key_operations);

static void __exit key_exit(void)

if (gpio_addr)
iounmap(gpio_addr);

module_init(key_init);
module_exit(key_exit);

MODULE_DESCRIPTION("Driver for key");
MODULE_AUTHOR("wit_yuan");
MODULE_LICENSE("GPL");

ifeq ($(KERNELRELEASE),)
KERNEL_DIR=/home/wityuan/Downloads/MarsBoard-A20-Linux-SDK-V1.2/linux-sunxi
PWD=$(shell pwd)

modules:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
arm-linux-gnueabihf-gcc -o key key.c

modules_install:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install
clean:
rm -rf *.ko *.o .tmp_versions .mod.c modules.order Module.symvers . .cmd
else
obj-m:=key.o

endif

int main(int argc,char *argv[])

int fd;
int val;
fd = open("/dev/key_device",O_RDWR);
if(fd < 0)
printf("---open file error----\\r\\n");
return -1;

/* EINT type PIO controller registers */

/* EINT type defines */

static wait_queue_head_t key_data_avail;
static unsigned int key_done = 0;

static int int_cfg_addr[] = PIO_INT_CFG0_OFFSET,
PIO_INT_CFG1_OFFSET,
PIO_INT_CFG2_OFFSET,
PIO_INT_CFG3_OFFSET;

/* Setup GPIO irq mode (FALLING, RISING, BOTH, etc */

)

/* Enable GPIO interrupt for pin */

)

/* Disable GPIO interrupt for pin */

)

/* Set GPIO pin mode (input, output, etc) /
/
GPIO port has 4 cfg 32bit registers (8 pins each) /
/
First port cfg register addr = port_num * 0x24 */

)

static script_gpio_set_t info;
static unsigned key_handler1;
static unsigned key_handler2;

static struct class *key_class;
static struct device *key_device;
static unsigned int key_major;

static unsigned int key_value;

static void *__iomem gpio_addr = NULL;

static int key_open(struct inode *inode, struct file *filp);
static ssize_t key_read (struct file *, char __user *, size_t, loff_t *);
static ssize_t key_write (struct file *filp, const char __user *buf, size_t len, loff_t *off);
static int key_close(struct inode *inode, struct file *filp);

struct file_operations key_operations =
.owner = THIS_MODULE,
.open = key_open,
.read = key_read,
.write = key_write,
.release = key_close,
;

struct key_str
char *name;
int val;
;

struct key_str g_key_str[2]="key1",0x1,"key2",2;

static irqreturn_t key_irq_handler1(int irq, void *dev_id)

int err;
int reg_val = 0;
int ret_val = 0;

static irqreturn_t key_irq_handler2(int irq, void *dev_id)

static ssize_t key_read (struct file *file, char __user *buf, size_t len, loff_t *off)

unsigned int value = 0;

static int key_open(struct inode *inode, struct file *filp)

int err = 0;
int key_test_enabled = 0;
int ret = 0;

static ssize_t key_write (struct file *filp, const char __user *buf, size_t len, loff_t *off)

static int key_close(struct inode *inode, struct file *filp)

SUNXI_MASK_GPIO_IRQ(gpio_addr,IRQ_EINT22);
SUNXI_MASK_GPIO_IRQ(gpio_addr,IRQ_EINT23);

static int __init key_test_init(void)

key_major = register_chrdev(0, "key_chrdev", &key_operations);

static void __exit key_test_exit(void)

if (gpio_addr)
iounmap(gpio_addr);

module_init(key_test_init);
module_exit(key_test_exit);

MODULE_DESCRIPTION("Driver for key");
MODULE_AUTHOR("wit_yuan");
MODULE_LICENSE("GPL");

ifeq ($(KERNELRELEASE),)
KERNEL_DIR=/home/wityuan/Downloads/MarsBoard-A20-Linux-SDK-V1.2/linux-sunxi
PWD=$(shell pwd)

modules:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
arm-linux-gnueabihf-gcc -o key key.c

modules_install:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install
clean:
rm -rf *.ko *.o .tmp_versions .mod.c modules.order Module.symvers . .cmd
else
obj-m:=key.o

endif

int main(int argc,char *argv[])

int fd;
int val;
fd = open("/dev/key_device",O_RDWR);
if(fd < 0)
printf("---open file error----\\r\\n");
return -1;

以上是关于树莓派 GPIO Input 部分高级应用技巧的主要内容,如果未能解决你的问题,请参考以下文章

Python面向树莓派使用RPi.GPIO库

Python面向树莓派使用RPi.GPIO库

Python面向树莓派使用RPi.GPIO库

将断电的树莓派 gpio 引脚与电路的其余部分(通电)隔离

四 . 树莓派A20 GPIO中断程序编写(1基本处理)

控制 GPIO 的 Android 套接字