四 . 树莓派A20 GPIO中断程序编写(1基本处理)
1 参考资料
- \marsboard\marsboard-a20-linux-sdk-v1.2\linux-sunxi\arch\arm\plat-sunxi\include\plat\Irqs.h
- \marsboard\marsboard-a20-linux-sdk-v1.2\linux-sunxi\drivers\input\touchscreen\Gt818_ts.c
2 硬件原理图
关于按键,在DVK521上为:
按键
我又从一份数据手册中看到,PI7,PI8,PI9是没有外部中断功能的。如下图所示:
中断功能
还好,目前的按键是通过短接帽来连接PI7PI9的,那么可以将短接帽拿掉,使用杜邦线连接PI10PI12。
现在我将Key2连接到PI10上。那么KEY2的中断引脚为EINT22。
配置sys_config.fex文件:
[key_test_para]
key_test_enable = 1
key2 = port:PI10<0><1><default><default>
3 软件配置基础知识
现在使用的树莓派A20,是一个双核A7的芯片,而这个属于SMP架构,中断处理方式也已经和原先的理念大有不同。所以还是要知道关于linux的中断原理,可以从网络中获取相关历史性技术知识。
3.1 中断
从A20的数据手册中,可以看到外部中断数到了EINT31。也就是说PIO中断功能有32个。
3.2 一些关于IO功能的API函数
下面列出的API函数是在 \linux-sunxi\arch\arm\plat-sunxi\Sys_config.c中。
/*
* CSP_GPIO_Request_EX
* 函数名称:
*
* 参数说明:
* main_name 传进的主键名称,匹配模块(驱动名称)
* sub_name 传进的子键名称,如果是空,表示全部,否则寻找到匹配的单独GPIO
*
* 返回值 :0 : err
* other: success
*
* 说明 :暂时没有做冲突检查
*/
u32 gpio_request_ex(char *main_name, const char *sub_name) /* 设备申请GPIO函数扩展接口 */
/*
* CSP_GPIO_Set_One_PIN_IO_Status
* Description:
* 修改用户申请过的GPIO中的某一个IO口的,输入输出状态
* Arguments :
* p_handler : handler
* if_set_to_output_status : 设置成输出状态还是输入状态
* gpio_name : 要操作的GPIO的名称
*/
__s32 gpio_set_one_pin_io_status(u32 p_handler, __u32 if_set_to_output_status,
const char *gpio_name)
/*
* CSP_GPIO_Write_One_PIN_Value
* Description:
* 修改用户申请过的GPIO中的某一个IO口的端口的电平
* Arguments:
* p_handler : handler
* value_to_gpio: 要设置的电平的电压
* gpio_name : 要操作的GPIO的名称
*/
__s32 gpio_write_one_pin_value(u32 p_handler, __u32 value_to_gpio,
const char *gpio_name)
/*
* CSP_GPIO_Set_One_PIN_Pull
* Description:
* 修改用户申请过的GPIO中的某一个IO口的,PULL状态
* Arguments :
* p_handler : handler
* if_set_to_output_status : 所设置的pull状态
* gpio_name : 要操作的GPIO的名称
*/
__s32 gpio_set_one_pin_pull(u32 p_handler, __u32 set_pull_status,
const char *gpio_name)
3.2 按键中断中用到的API
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为共享。
第四个参数为中断名。
第五个参数为中断传递的数据。
3.3 中断处理逻辑
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);清除状态位。
写到这里,本应该很顺利,可是,在驱动程序加载进内核的时候,明显是报错。错误我就不贴出来了,可是我可以将中断信息附上:
root@marsboard:~# cat /proc/interrupts
CPU0 CPU1
29: 3989 2679 GIC arch_timer
30: 0 0 GIC arch_timer
32: 0 0 GIC axp_mfd
33: 286 0 GIC serial
39: 1396 0 GIC sunxi-i2c.0
40: 0 0 GIC sunxi-i2c.1
41: 0 0 GIC sunxi-i2c.2
54: 0 0 GIC timer0
55: 12 0 GIC aw_clock_event
56: 2 0 GIC sunxi-rtc alarm
59: 35778 0 GIC dma_irq
60: 0 0 GIC sunxi-gpio
64: 0 0 GIC sunxi-mmc
69: 5876 0 GIC nand
71: 0 0 GIC ehci_hcd:usb2
72: 0 0 GIC ehci_hcd:usb4
76: 7001 0 GIC sunxi lcd0
77: 0 0 GIC sunxi lcd1
78: 0 0 GIC g2d
79: 3495 0 GIC sunxi scaler0
80: 0 0 GIC sunxi scaler1
85: 0 0 GIC cedar_dev
87: 419 0 GIC eth0
88: 0 0 GIC sw_ahci
92: 0 0 GIC ace_dev
96: 0 0 GIC ohci_hcd:usb3
97: 0 0 GIC ohci_hcd:usb5
101: 0 0 GIC mali_gp_irq_handlers
102: 0 0 GIC mali_mmu_irq_handlers
103: 0 0 GIC mali_pp_irq_handlers
104: 0 0 GIC mali_mmu_irq_handlers
106: 0 0 GIC mali_pp_irq_handlers
107: 0 0 GIC mali_mmu_irq_handlers
IPI0: 0 0 Timer broadcast interrupts
IPI1: 1564 4112 Rescheduling interrupts
IPI2: 0 0 Function call interrupts
IPI3: 4 20 Single function call interrupts
IPI4: 0 0 CPU stop interrupts
IPI5: 0 0 CPU backtrace
Err: 0
从这里,可以看出来,PIO中断号60已经注册进内核了。我们现在使用的一个IO中断是被包含在里面的。所以,需要在内核中找到sunxi-gpio是怎么去注册中断,而我们就需要将我们的中断程序内容附加到已经注册的中断上去。
在 marsboard\marsboard-a20-linux-sdk-v1.2\linux-sunxi\drivers\gpio\Gpio-sunxi.c中我们可以找到函数:
/* IRQ handler - redirect interrupts to virtual irq chip */
static irqreturn_t sunxi_gpio_irq_handler(int irq, void *devid)
{
__u32 status = 0;
int i = 0;
struct sunxi_gpio_chip *sgpio = devid;
status = readl(sgpio->gaddr + PIO_INT_STAT_OFFSET);
for (i = 0; i < EINT_NUM; i++) {
if ((status & (1 << i)) &&
(gpio_eint_list[i].gpio >= 0)) {
status &= ~(1 << i);
SUNXI_CLEAR_EINT(sgpio->gaddr, i);
generic_handle_irq(sgpio->irq_base + i);
}
}
if (status)
return IRQ_NONE;
return IRQ_HANDLED;
}
里面最重要的函数是:
/**
* generic_handle_irq - Invoke the handler for a particular irq
* @irq: The irq number to handle
*
*/
int generic_handle_irq(unsigned int irq)
最终调用的是:
/*
* Architectures call this to let the generic IRQ layer
* handle an interrupt. If the descriptor is attached to an
* irqchip-style controller then we call the ->handle_irq() handler,
* and it calls __do_IRQ() if it's attached to an irqtype-style controller.
*/
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
desc->handle_irq(irq, desc);
}
然而,它又被赋值了:
/* caller has locked the irq_desc and both params are valid */
static inline void __irq_set_handler_locked(unsigned int irq,
irq_flow_handler_t handler)
{
struct irq_desc *desc;
desc = irq_to_desc(irq);
desc->handle_irq = handler;
}
在一定程度的意义上,gpio-sunxi.c已经将中断基本处理做好了,我们要做的只是和它共享中断。
3.4 实践逻辑
sys_config.fex文件配置如下:
sys_config.fex配置
从上面的实验中,已经发现在request_irq中设置边沿等等触发,在安装ko文件的时候,都会报错,从这里看出,在共享中断的时候,是不允许设置其他的内容的。那么,只能去找A20寄存器中关于io口中断的设置。在这些设置已经设置好的情况下,中断应该就能响应了。这里贴出一个比较简单的驱动程序:
#include "linux/init.h"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/leds.h>
#include <plat/sys_config.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <mach/irqs.h>
#include <mach/system.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/gpio.h>
/* EINT type PIO controller registers */
#define PIO_INT_CFG0_OFFSET 0x200
#define PIO_INT_CFG1_OFFSET 0x204
#define PIO_INT_CFG2_OFFSET 0x208
#define PIO_INT_CFG3_OFFSET 0x20c
#define PIO_INT_STAT_OFFSET (0x214)
#define IRQ_EINT22 22
#define PIO_BASE_ADDRESS SW_PA_PORTC_IO_BASE
#define PIO_RANGE_SIZE (0x400)
#define PIO_INT_CTRL_OFFSET (0x210)
/* EINT type defines */
#define POSITIVE_EDGE 0x0
#define NEGATIVE_EDGE 0x1
#define HIGH_LEVEL 0x2
#define LOW_LEVEL 0x3
#define DOUBLE_EDGE 0x4
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 */
#define SUNXI_SET_GPIO_IRQ_TYPE(addr, offs, mode) ({ \
__u32 reg_bit = offs % 8; \
__u32 reg_num = offs / 8; \
__u32 reg_val = readl(addr + int_cfg_addr[reg_num]); \
reg_val &= (~(0xf << (reg_bit * 4))); \
reg_val |= (mode << (reg_bit * 4)); \
writel(reg_val, addr + int_cfg_addr[reg_num]); \
})
/* Enable GPIO interrupt for pin */
#define SUNXI_UNMASK_GPIO_IRQ(addr, irq) ({ \
__u32 reg_val = readl(addr + PIO_INT_CTRL_OFFSET); \
reg_val |= (1 << irq); \
writel(reg_val, addr + PIO_INT_CTRL_OFFSET); \
})
/* Disable GPIO interrupt for pin */
#define SUNXI_MASK_GPIO_IRQ(addr, irq) ({ \
__u32 reg_val = readl(addr + PIO_INT_CTRL_OFFSET); \
reg_val &= ~(1 << irq); \
writel(reg_val, addr + PIO_INT_CTRL_OFFSET); \
})
/* 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 */
#define SUNXI_SET_GPIO_MODE(addr, port, pin, mode) ({ \
__u32 reg_val = 0; \
__u32 pin_idx = pin >> 3; \
void *raddr = addr + (((port)-1)*0x24 + ((pin_idx)<<2) + 0x00); \
reg_val = readl(raddr); \
reg_val &= ~(0x07 << (((pin - (pin_idx<<3))<<2))); \
reg_val |= mode << (((pin - (pin_idx<<3))<<2)); \
writel(reg_val, raddr); \
})
static script_gpio_set_t info;
static unsigned key_handler;
static struct class *key_class;
static struct device *key_device;
static unsigned int key_major;
static void *__iomem gpio_addr = NULL;
static int key_open(struct inode *inode, struct file *filp);
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,
.write = key_write,
.release = key_close,
};
struct key_str{
char *name;
int val;
};
struct key_str *g_key_str={"my_key",2};
static irqreturn_t key_irq_handler(int irq, void *dev_id)
{
int reg_val;
//clear the IRQ_EINT22 interrupt pending
reg_val = readl(gpio_addr + PIO_INT_STAT_OFFSET);
printk("key irq Interrupt\r\n");
#if 1
if (reg_val & (1 << (IRQ_EINT22))) {
printk("==IRQ_EINT22=\r\n");
writel(reg_val & (1 << (IRQ_EINT22)),
gpio_addr + PIO_INT_STAT_OFFSET);
} else {
printk("Other Interrupt\r\n");
return IRQ_NONE;
}
#endif
return IRQ_HANDLED;
}
static int key_open(struct inode *inode, struct file *filp)
{
int err = 0;
int key_test_enabled = 0;
int ret = 0;
err = script_parser_fetch("key_test_para", "key_test_enable", &key_test_enabled,
sizeof(key_test_enabled)/sizeof(int));
if(!err){
printk("---script.bin key get ok,value:%d----\n",key_test_enabled);
}
else
{
printk("---script.bin key get false----\n");
return -1;
}
err = script_parser_fetch("key_test_para", "key2",
(int *)&info,
sizeof(script_gpio_set_t));
if (err) {
printk("----script.bin get io error----\r\n");
return -1;
}
/* reserve gpio for led */
key_handler = gpio_request_ex("key_test_para", "key2");
if (!key_handler) {
printk("----script.bin can't requst handler----\r\n");
return -1;
}
#if 0
/*设置为输入,没有上下拉*/
err = gpio_set_one_pin_io_status(key_handler,0,"key2");
if (err) {
printk("----set io input error----\r\n");
return -1;
}
#endif
err = gpio_set_one_pin_pull(key_handler,1,"key2");
if (err) {
printk("----set io pull error----\r\n");
return -1;
}
if (!gpio_addr) {
gpio_addr = ioremap(PIO_BASE_ADDRESS, PIO_RANGE_SIZE);
}
if(!gpio_addr)
{
printk("-----address error-----\r\n");
}
err = request_irq(SW_INT_IRQNO_PIO, key_irq_handler,
IRQF_SHARED, "gpio_pin_2", g_key_str);
if (err < 0) {
printk(" request irq error:%d\n",err);
return -1;
}
/*set the gpio register*/
SUNXI_SET_GPIO_IRQ_TYPE(gpio_addr,IRQ_EINT22,POSITIVE_EDGE);
SUNXI_UNMASK_GPIO_IRQ(gpio_addr,IRQ_EINT22);
SUNXI_SET_GPIO_MODE(gpio_addr,9,10,6);/*PI10 EINT22 settiings*/
return 0;
}
static ssize_t key_write (struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
return 0;
}
static int key_close(struct inode *inode, struct file *filp)
{
SUNXI_MASK_GPIO_IRQ(gpio_addr,IRQ_EINT22);
free_irq(SW_INT_IRQNO_PIO, g_key_str);
printk("----key close----\r\n");
return 0;
}
static int __init key_init(void)
{
key_major = register_chrdev(0, "key_chrdev", &key_operations);
key_class = class_create(THIS_MODULE, "key_class");
if(!key_class){
unregister_chrdev(key_major, "key_chrdev");
printk("----key_chrdev error----\r\n");
return -1;
}
key_device = device_create(key_class, NULL, MKDEV(key_major,0),
NULL, "key_device");
if(!key_device){
class_destroy(key_class);
unregister_chrdev(key_major, "key_chrdev");
printk("----key_device error----\r\n");
return -1;
}
printk("----key init ok----\r\n");
return 0;
}
static void __exit key_exit(void)
{
if (gpio_addr) {
iounmap(gpio_addr);
}
if (key_handler)
gpio_release(key_handler, 1);
device_destroy(key_class, MKDEV(key_major, 0));
class_destroy(key_class);
unregister_chrdev(key_major, "key_chrdev");
printk("---driver exit---\r\n");
}
module_init(key_init);
module_exit(key_exit);
MODULE_DESCRIPTION("Driver for key");
MODULE_AUTHOR("wit_yuan");
MODULE_LICENSE("GPL");```
测试程序为:
include "stdio.h"
include <sys/types.h>
include <sys/stat.h>
include <fcntl.h>
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;
}
while(1);
return 0;
}
也就是说,只需要打开一个文件即可。
Makefile文件为:
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:~#
ok,简单的测试程序,暂时就这样了。下面再去优化。
### 3.5 polling查询按键值
使用查询按键值的程序代码如下:
key.c驱动程序:
include "linux/init.h"
include <linux/kernel.h>
include <linux/module.h>
include <linux/leds.h>
include <plat/sys_config.h>
include <linux/major.h>
include <linux/fs.h>
include <linux/device.h>
include <asm/io.h>
include <asm/uaccess.h>
include <linux/interrupt.h>
include <linux/ioport.h>
include <asm/irq.h>
include <asm/io.h>
include <mach/irqs.h>
include <mach/system.h>
include <asm/uaccess.h>
include <mach/hardware.h>
include <linux/gpio.h>
/* EINT type PIO controller registers */
define PIO_INT_CFG0_OFFSET 0x200
define PIO_INT_CFG1_OFFSET 0x204
define PIO_INT_CFG2_OFFSET 0x208
define PIO_INT_CFG3_OFFSET 0x20c
define PIO_INT_STAT_OFFSET (0x214)
define PIO_INT_DATA_OFFSET (0x130)
define IRQ_EINT22 22
define IRQ_EINT23 23
define PIO_BASE_ADDRESS SW_PA_PORTC_IO_BASE
define PIO_RANGE_SIZE (0x400)
define PIO_INT_CTRL_OFFSET (0x210)
/* EINT type defines */
define POSITIVE_EDGE 0x0
define NEGATIVE_EDGE 0x1
define HIGH_LEVEL 0x2
define LOW_LEVEL 0x3
define DOUBLE_EDGE 0x4
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 */
define SUNXI_SET_GPIO_IRQ_TYPE(addr, offs, mode) ({ \
__u32 reg_bit = offs % 8; \
__u32 reg_num = offs / 8; \
__u32 reg_val = readl(addr + int_cfg_addr[reg_num]); \
reg_val &= (~(0xf << (reg_bit * 4))); \
reg_val |= (mode << (reg_bit * 4)); \
writel(reg_val, addr + int_cfg_addr[reg_num]); \
})
/* Enable GPIO interrupt for pin */
define SUNXI_UNMASK_GPIO_IRQ(addr, irq) ({ \
__u32 reg_val = readl(addr + PIO_INT_CTRL_OFFSET); \
reg_val |= (1 << irq); \
writel(reg_val, addr + PIO_INT_CTRL_OFFSET); \
})
/* Disable GPIO interrupt for pin */
define SUNXI_MASK_GPIO_IRQ(addr, irq) ({ \
__u32 reg_val = readl(addr + PIO_INT_CTRL_OFFSET); \
reg_val &= ~(1 << irq); \
writel(reg_val, addr + PIO_INT_CTRL_OFFSET); \
})
/* 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 */
define SUNXI_SET_GPIO_MODE(addr, port, pin, mode) ({ \
__u32 reg_val = 0; \
__u32 pin_idx = pin >> 3; \
void *raddr = addr + (((port)-1)*0x24 + ((pin_idx)<<2) + 0x00); \
reg_val = readl(raddr); \
reg_val &= ~(0x07 << (((pin - (pin_idx<<3))<<2))); \
reg_val |= mode << (((pin - (pin_idx<<3))<<2)); \
writel(reg_val, raddr); \
})
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;
struct key_str *key_t = (struct key_str *)dev_id;
//clear the IRQ_EINT22 interrupt pending
reg_val = readl(gpio_addr + PIO_INT_STAT_OFFSET);
if (reg_val & (1 << (IRQ_EINT22))) {
//printk("==IRQ_EINT22=\r\n");
writel(reg_val & (1 << (IRQ_EINT22)),
gpio_addr + PIO_INT_STAT_OFFSET);
ret_val = readl(gpio_addr + PIO_INT_DATA_OFFSET);
if(!(ret_val&(1<<10)))
{
//printk("key1 pressed \r\n");
key_value |= key_t->val;
}
else
{
//printk("key1 released \r\n");
key_value &= ~key_t->val;
}
//printk("key%d irq Interrupt,%d \r\n",key_t->val,key_value);
}
return IRQ_HANDLED;
}
static irqreturn_t key_irq_handler2(int irq, void *dev_id)
{
int reg_val;
int ret_val;
//clear the IRQ_EINT23 interrupt pending
reg_val = readl(gpio_addr + PIO_INT_STAT_OFFSET);
struct key_str *key_t = (struct key_str *)dev_id;
//clear the IRQ_EINT22 interrupt pending
reg_val = readl(gpio_addr + PIO_INT_STAT_OFFSET);
if 1
if (reg_val & (1 << (IRQ_EINT23))) {
//printk("==IRQ_EINT23=\r\n");
writel(reg_val & (1 << (IRQ_EINT23)),
gpio_addr + PIO_INT_STAT_OFFSET);
ret_val = readl(gpio_addr + PIO_INT_DATA_OFFSET);
if(!(ret_val&(1<<11)))
{
//printk("key2 pressed \r\n");
key_value |= key_t->val;
}
else
{
//printk("key2 released \r\n");
key_value &= ~key_t->val;
}
//printk("key%d irq Interrupt,%d \r\n",key_t->val,key_value);
}
endif
return IRQ_HANDLED;
}
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);
return value;
}
static int key_open(struct inode *inode, struct file *filp)
{
int err = 0;
int key_test_enabled = 0;
int ret = 0;
err = script_parser_fetch("key_test_para", "key_test_enable", &key_test_enabled,
sizeof(key_test_enabled)/sizeof(int));
if(!err){
printk("---script.bin key get ok,value:%d----\n",key_test_enabled);
}
else
{
printk("---script.bin key get false----\n");
return -1;
}
err = script_parser_fetch("key_test_para", "key1",
(int *)&info,
sizeof(script_gpio_set_t));
if (err) {
printk("----script.bin get io error----\r\n");
return -1;
}
err = script_parser_fetch("key_test_para", "key2",
(int *)&info,
sizeof(script_gpio_set_t));
if (err) {
printk("----script.bin get io error----\r\n");
return -1;
}
/* reserve gpio for led */
key_handler1 = gpio_request_ex("key_test_para", "key1");
if (!key_handler1) {
printk("----script.bin can't requst handler----\r\n");
return -1;
}
/* reserve gpio for led */
key_handler2 = gpio_request_ex("key_test_para", "key2");
if (!key_handler2) {
printk("----script.bin can't requst handler----\r\n");
return -1;
}
if 1
/*设置为输入,没有上下拉*/
err = gpio_set_one_pin_io_status(key_handler1,0,"key1");
if (err) {
printk("----set io input 1 error----\r\n");
return -1;
}
err = gpio_set_one_pin_io_status(key_handler2,0,"key2");
if (err) {
printk("----set io input 2 error----\r\n");
return -1;
}
endif
err = gpio_set_one_pin_pull(key_handler1,1,"key1");
if (err) {
printk("----set io pull error----\r\n");
return -1;
}
err = gpio_set_one_pin_pull(key_handler2,1,"key2");
if (err) {
printk("----set io pull error----\r\n");
return -1;
}
if (!gpio_addr) {
gpio_addr = ioremap(PIO_BASE_ADDRESS, PIO_RANGE_SIZE);
}
if(!gpio_addr)
{
printk("-----address error-----\r\n");
}
err = request_irq(SW_INT_IRQNO_PIO, key_irq_handler1,
IRQF_SHARED, "gpio_pin_1", &g_key_str[0]);
if (err < 0) {
printk(" request irq 1 error:%d\n",err);
return -1;
}
err = request_irq(SW_INT_IRQNO_PIO, key_irq_handler2,
IRQF_SHARED, "gpio_pin_2", &g_key_str[1]);
if (err < 0) {
printk(" request irq error:%d\n",err);
return -1;
}
/*set the gpio 1 register*/
SUNXI_SET_GPIO_IRQ_TYPE(gpio_addr,IRQ_EINT22,DOUBLE_EDGE);
SUNXI_UNMASK_GPIO_IRQ(gpio_addr,IRQ_EINT22);
SUNXI_SET_GPIO_MODE(gpio_addr,9,10,6);/*PI10 EINT22 settiings*/
/*set the gpio 2 register*/
SUNXI_SET_GPIO_IRQ_TYPE(gpio_addr,IRQ_EINT23,DOUBLE_EDGE);
SUNXI_UNMASK_GPIO_IRQ(gpio_addr,IRQ_EINT23);
SUNXI_SET_GPIO_MODE(gpio_addr,9,11,6);/*PI11 EINT23 settiings*/
return 0;
}
static ssize_t key_write (struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
return 0;
}
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);
free_irq(SW_INT_IRQNO_PIO, &g_key_str[0]);
free_irq(SW_INT_IRQNO_PIO, &g_key_str[1]);
printk("----key close----\r\n");
return 0;
}
static int __init key_init(void)
{
key_major = register_chrdev(0, "key_chrdev", &key_operations);
key_class = class_create(THIS_MODULE, "key_class");
if(!key_class){
unregister_chrdev(key_major, "key_chrdev");
printk("----key_chrdev error----\r\n");
return -1;
}
key_device = device_create(key_class, NULL, MKDEV(key_major,0),
NULL, "key_device");
if(!key_device){
class_destroy(key_class);
unregister_chrdev(key_major, "key_chrdev");
printk("----key_device error----\r\n");
return -1;
}
printk("----key init ok----\r\n");
return 0;
}
static void __exit key_exit(void)
{
if (gpio_addr) {
iounmap(gpio_addr);
}
if (key_handler1)
gpio_release(key_handler1, 1);
if (key_handler2)
gpio_release(key_handler2, 1);
device_destroy(key_class, MKDEV(key_major, 0));
class_destroy(key_class);
unregister_chrdev(key_major, "key_chrdev");
printk("---driver exit---\r\n");
}
module_init(key_init);
module_exit(key_exit);
MODULE_DESCRIPTION("Driver for key");
MODULE_AUTHOR("wit_yuan");
MODULE_LICENSE("GPL");
Makefile程序:
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
key_test.c测试程序:
include "stdio.h"
include <sys/types.h>
include <sys/stat.h>
include <fcntl.h>
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;
}
while(1)
{
read(fd,&val,1);
if(val!=0)
printf("val:%0x\r\n",val);
}
return 0;
}
使用top命令查询应用程序占用cpu资源:
![按键驱动程序资源占用](https://img.haomeiwen.com/i3549048/fb05a6115daedca8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
所以,使用这种方式,在程序设计角度来说,明显是不合理的。所以,必须要进行改进。
### 3.6 使用休眠方式
由于3.5的程序中,应用程序是一直在读取内核数据。这样的方式明显将整个系统的性能降低。而程序设计的整体思路是,只需要在内核有数据的时候,应用层读取就可以了。所以呢,可以使用这种方式,应用层读取数据,在内核没有数据需要返回的时候,内核可以让该进程异步阻塞,这样就不会占用整个系统的资源。
这种技巧,使用的主要是内核提供的几个函数:
> * wait_event_interruptible
> * wake_up_interruptible
> * DECLARE_WAIT_QUEUE_HEAD
> * init_waitqueue_head
关于这种操作方式,可以参考文件:
\marsboard\marsboard-a20-linux-sdk-v1.2\linux-sunxi\drivers\staging\iio\adc\Ad7780.c
我把程序贴在下面:
key.c驱动程序如下:
include "linux/init.h"
include <linux/kernel.h>
include <linux/module.h>
include <linux/leds.h>
include <plat/sys_config.h>
include <linux/major.h>
include <linux/fs.h>
include <linux/device.h>
include <asm/io.h>
include <asm/uaccess.h>
include <linux/interrupt.h>
include <linux/ioport.h>
include <asm/irq.h>
include <asm/io.h>
include <mach/irqs.h>
include <mach/system.h>
include <asm/uaccess.h>
include <mach/hardware.h>
include <linux/gpio.h>
include <linux/sched.h>
include <linux/wait.h>
/* EINT type PIO controller registers */
define PIO_INT_CFG0_OFFSET 0x200
define PIO_INT_CFG1_OFFSET 0x204
define PIO_INT_CFG2_OFFSET 0x208
define PIO_INT_CFG3_OFFSET 0x20c
define PIO_INT_STAT_OFFSET (0x214)
define PIO_INT_DATA_OFFSET (0x130)
define IRQ_EINT22 22
define IRQ_EINT23 23
define PIO_BASE_ADDRESS SW_PA_PORTC_IO_BASE
define PIO_RANGE_SIZE (0x400)
define PIO_INT_CTRL_OFFSET (0x210)
/* EINT type defines */
define POSITIVE_EDGE 0x0
define NEGATIVE_EDGE 0x1
define HIGH_LEVEL 0x2
define LOW_LEVEL 0x3
define DOUBLE_EDGE 0x4
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 */
define SUNXI_SET_GPIO_IRQ_TYPE(addr, offs, mode) ({ \
__u32 reg_bit = offs % 8; \
__u32 reg_num = offs / 8; \
__u32 reg_val = readl(addr + int_cfg_addr[reg_num]); \
reg_val &= (~(0xf << (reg_bit * 4))); \
reg_val |= (mode << (reg_bit * 4)); \
writel(reg_val, addr + int_cfg_addr[reg_num]); \
})
/* Enable GPIO interrupt for pin */
define SUNXI_UNMASK_GPIO_IRQ(addr, irq) ({ \
__u32 reg_val = readl(addr + PIO_INT_CTRL_OFFSET); \
reg_val |= (1 << irq); \
writel(reg_val, addr + PIO_INT_CTRL_OFFSET); \
})
/* Disable GPIO interrupt for pin */
define SUNXI_MASK_GPIO_IRQ(addr, irq) ({ \
__u32 reg_val = readl(addr + PIO_INT_CTRL_OFFSET); \
reg_val &= ~(1 << irq); \
writel(reg_val, addr + PIO_INT_CTRL_OFFSET); \
})
/* 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 */
define SUNXI_SET_GPIO_MODE(addr, port, pin, mode) ({ \
__u32 reg_val = 0; \
__u32 pin_idx = pin >> 3; \
void *raddr = addr + (((port)-1)*0x24 + ((pin_idx)<<2) + 0x00); \
reg_val = readl(raddr); \
reg_val &= ~(0x07 << (((pin - (pin_idx<<3))<<2))); \
reg_val |= mode << (((pin - (pin_idx<<3))<<2)); \
writel(reg_val, raddr); \
})
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;
struct key_str *key_t = (struct key_str *)dev_id;
//clear the IRQ_EINT22 interrupt pending
reg_val = readl(gpio_addr + PIO_INT_STAT_OFFSET);
if (reg_val & (1 << (IRQ_EINT22))) {
//printk("==IRQ_EINT22=\r\n");
writel(reg_val & (1 << (IRQ_EINT22)),
gpio_addr + PIO_INT_STAT_OFFSET);
ret_val = readl(gpio_addr + PIO_INT_DATA_OFFSET);
if(!(ret_val&(1<<10)))
{
//printk("key1 pressed \r\n");
key_value |= key_t->val;
}
else
{
//printk("key1 released \r\n");
key_value &= ~key_t->val;
}
key_done = 1;
wake_up_interruptible(&key_data_avail);
//printk("key%d irq Interrupt,%d \r\n",key_t->val,key_value);
}
return IRQ_HANDLED;
}
static irqreturn_t key_irq_handler2(int irq, void *dev_id)
{
int reg_val;
int ret_val;
//clear the IRQ_EINT23 interrupt pending
reg_val = readl(gpio_addr + PIO_INT_STAT_OFFSET);
struct key_str *key_t = (struct key_str *)dev_id;
//clear the IRQ_EINT22 interrupt pending
reg_val = readl(gpio_addr + PIO_INT_STAT_OFFSET);
if 1
if (reg_val & (1 << (IRQ_EINT23))) {
//printk("==IRQ_EINT23=\r\n");
writel(reg_val & (1 << (IRQ_EINT23)),
gpio_addr + PIO_INT_STAT_OFFSET);
ret_val = readl(gpio_addr + PIO_INT_DATA_OFFSET);
if(!(ret_val&(1<<11)))
{
//printk("key2 pressed \r\n");
key_value |= key_t->val;
}
else
{
//printk("key2 released \r\n");
key_value &= ~key_t->val;
}
key_done = 1;
wake_up_interruptible(&key_data_avail);
//printk("key%d irq Interrupt,%d \r\n",key_t->val,key_value);
}
endif
return IRQ_HANDLED;
}
static ssize_t key_read (struct file *file, char __user *buf, size_t len, loff_t *off)
{
unsigned int value = 0;
key_done = 0;
wait_event_interruptible(key_data_avail,key_done);
value = copy_to_user(buf,&key_value,4);
return value;
}
static int key_open(struct inode *inode, struct file *filp)
{
int err = 0;
int key_test_enabled = 0;
int ret = 0;
err = script_parser_fetch("key_test_para", "key_test_enable", &key_test_enabled,
sizeof(key_test_enabled)/sizeof(int));
if(!err){
printk("---script.bin key get ok,value:%d----\n",key_test_enabled);
}
else
{
printk("---script.bin key get false----\n");
return -1;
}
err = script_parser_fetch("key_test_para", "key1",
(int *)&info,
sizeof(script_gpio_set_t));
if (err) {
printk("----script.bin get io error----\r\n");
return -1;
}
err = script_parser_fetch("key_test_para", "key2",
(int *)&info,
sizeof(script_gpio_set_t));
if (err) {
printk("----script.bin get io error----\r\n");
return -1;
}
/* reserve gpio for led */
key_handler1 = gpio_request_ex("key_test_para", "key1");
if (!key_handler1) {
printk("----script.bin can't requst handler----\r\n");
return -1;
}
/* reserve gpio for led */
key_handler2 = gpio_request_ex("key_test_para", "key2");
if (!key_handler2) {
printk("----script.bin can't requst handler----\r\n");
return -1;
}
if 1
/*设置为输入,没有上下拉*/
err = gpio_set_one_pin_io_status(key_handler1,0,"key1");
if (err) {
printk("----set io input 1 error----\r\n");
return -1;
}
err = gpio_set_one_pin_io_status(key_handler2,0,"key2");
if (err) {
printk("----set io input 2 error----\r\n");
return -1;
}
endif
err = gpio_set_one_pin_pull(key_handler1,1,"key1");
if (err) {
printk("----set io pull error----\r\n");
return -1;
}
err = gpio_set_one_pin_pull(key_handler2,1,"key2");
if (err) {
printk("----set io pull error----\r\n");
return -1;
}
if (!gpio_addr) {
gpio_addr = ioremap(PIO_BASE_ADDRESS, PIO_RANGE_SIZE);
}
if(!gpio_addr)
{
printk("-----address error-----\r\n");
}
err = request_irq(SW_INT_IRQNO_PIO, key_irq_handler1,
IRQF_SHARED, "gpio_pin_1", &g_key_str[0]);
if (err < 0) {
printk(" request irq 1 error:%d\n",err);
return -1;
}
err = request_irq(SW_INT_IRQNO_PIO, key_irq_handler2,
IRQF_SHARED, "gpio_pin_2", &g_key_str[1]);
if (err < 0) {
printk(" request irq error:%d\n",err);
return -1;
}
/*set the gpio 1 register*/
SUNXI_SET_GPIO_IRQ_TYPE(gpio_addr,IRQ_EINT22,DOUBLE_EDGE);
SUNXI_UNMASK_GPIO_IRQ(gpio_addr,IRQ_EINT22);
SUNXI_SET_GPIO_MODE(gpio_addr,9,10,6);/*PI10 EINT22 settiings*/
/*set the gpio 2 register*/
SUNXI_SET_GPIO_IRQ_TYPE(gpio_addr,IRQ_EINT23,DOUBLE_EDGE);
SUNXI_UNMASK_GPIO_IRQ(gpio_addr,IRQ_EINT23);
SUNXI_SET_GPIO_MODE(gpio_addr,9,11,6);/*PI11 EINT23 settiings*/
return 0;
}
static ssize_t key_write (struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
return 0;
}
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);
free_irq(SW_INT_IRQNO_PIO, &g_key_str[0]);
free_irq(SW_INT_IRQNO_PIO, &g_key_str[1]);
printk("----key close----\r\n");
return 0;
}
static int __init key_test_init(void)
{
key_major = register_chrdev(0, "key_chrdev", &key_operations);
key_class = class_create(THIS_MODULE, "key_class");
if(!key_class){
unregister_chrdev(key_major, "key_chrdev");
printk("----key_chrdev error----\r\n");
return -1;
}
key_device = device_create(key_class, NULL, MKDEV(key_major,0),
NULL, "key_device");
if(!key_device){
class_destroy(key_class);
unregister_chrdev(key_major, "key_chrdev");
printk("----key_device error----\r\n");
return -1;
}
printk("----key init ok----\r\n");
init_waitqueue_head(&key_data_avail);
return 0;
}
static void __exit key_test_exit(void)
{
if (gpio_addr) {
iounmap(gpio_addr);
}
if (key_handler1)
gpio_release(key_handler1, 1);
if (key_handler2)
gpio_release(key_handler2, 1);
device_destroy(key_class, MKDEV(key_major, 0));
class_destroy(key_class);
unregister_chrdev(key_major, "key_chrdev");
printk("---driver exit---\r\n");
}
module_init(key_test_init);
module_exit(key_test_exit);
MODULE_DESCRIPTION("Driver for key");
MODULE_AUTHOR("wit_yuan");
MODULE_LICENSE("GPL");
Makefile程序如下:
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
key_test.c测试程序如下:
include "stdio.h"
include <sys/types.h>
include <sys/stat.h>
include <fcntl.h>
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;
}
while(1)
{
read(fd,&val,1);
if(val!=0)
printf("val:%0x\r\n",val);
}
return 0;
}
再使用top命令查询:
![按键程序基本不占用cpu资源](https://img.haomeiwen.com/i3549048/34240160c4c26f5e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)