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 硬件原理圖
關(guān)于按鍵侠姑,在DVK521上為:
我又從一份數(shù)據(jù)手冊中看到,PI7身笤,PI8糖声,PI9是沒有外部中斷功能的斤彼。如下圖所示:
還好,目前的按鍵是通過短接帽來連接PI7PI9的蘸泻,那么可以將短接帽拿掉琉苇,使用杜邦線連接PI10PI12。
現(xiàn)在我將Key2連接到PI10上悦施。那么KEY2的中斷引腳為EINT22并扇。
配置sys_config.fex文件:
[key_test_para]
key_test_enable = 1
key2 = port:PI10<0><1><default><default>
3 軟件配置基礎(chǔ)知識(shí)
現(xiàn)在使用的樹莓派A20,是一個(gè)雙核A7的芯片抡诞,而這個(gè)屬于SMP架構(gòu)穷蛹,中斷處理方式也已經(jīng)和原先的理念大有不同。所以還是要知道關(guān)于linux的中斷原理昼汗,可以從網(wǎng)絡(luò)中獲取相關(guān)歷史性技術(shù)知識(shí)肴熏。
3.1 中斷
從A20的數(shù)據(jù)手冊中,可以看到外部中斷數(shù)到了EINT31顷窒。也就是說PIO中斷功能有32個(gè)蛙吏。
3.2 一些關(guān)于IO功能的API函數(shù)
下面列出的API函數(shù)是在 \linux-sunxi\arch\arm\plat-sunxi\Sys_config.c中。
/*
* CSP_GPIO_Request_EX
* 函數(shù)名稱:
*
* 參數(shù)說明:
* main_name 傳進(jìn)的主鍵名稱鞋吉,匹配模塊(驅(qū)動(dòng)名稱)
* sub_name 傳進(jìn)的子鍵名稱鸦做,如果是空,表示全部谓着,否則尋找到匹配的單獨(dú)GPIO
*
* 返回值 :0 : err
* other: success
*
* 說明 :暫時(shí)沒有做沖突檢查
*/
u32 gpio_request_ex(char *main_name, const char *sub_name) /* 設(shè)備申請GPIO函數(shù)擴(kuò)展接口 */
/*
* CSP_GPIO_Set_One_PIN_IO_Status
* Description:
* 修改用戶申請過的GPIO中的某一個(gè)IO口的泼诱,輸入輸出狀態(tài)
* Arguments :
* p_handler : handler
* if_set_to_output_status : 設(shè)置成輸出狀態(tài)還是輸入狀態(tài)
* 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中的某一個(gè)IO口的端口的電平
* Arguments:
* p_handler : handler
* value_to_gpio: 要設(shè)置的電平的電壓
* 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中的某一個(gè)IO口的,PULL狀態(tài)
* Arguments :
* p_handler : handler
* if_set_to_output_status : 所設(shè)置的pull狀態(tài)
* 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中設(shè)置的中斷IO口治筒。
2.gpio_set_one_pin_io_status(),設(shè)置為輸入狀態(tài)改抡。
3.gpio_set_one_pin_pull()矢炼,設(shè)置輸入引腳的上下拉狀態(tài)。
4.request_irq()注冊中斷函數(shù)阿纤。
request_irq()函數(shù):
第一個(gè)參數(shù)為SW_INT_IRQNO_PIO,表示是外部端口的中斷號(hào)夷陋。
第二個(gè)參數(shù)為中斷處理函數(shù)名欠拾。
第三個(gè)參數(shù)為中斷方式胰锌,如IRQ_TYPE_EDGE_RISING,上升沿觸發(fā)藐窄,而IRQ_TYPE_EDGE_FALLING則是下降沿觸發(fā)资昧,IRQF_SHARED為共享。
第四個(gè)參數(shù)為中斷名荆忍。
第五個(gè)參數(shù)為中斷傳遞的數(shù)據(jù)格带。
3.3 中斷處理邏輯
1.獲取IO中斷源信息
由于內(nèi)核使用的是虛擬地址尋址硬件地址,獲取中斷源就需要將IO硬件地址空間映射到虛擬地址上刹枉∵闯可以使用ioremap(PIO_BASE_ADDRESS, PIO_RANGE_SIZE)進(jìn)行映射。
2.屏蔽中斷源
a.讀取中斷源寄存器的狀態(tài)微宝,可以使用readl(映射的IO地址 + PIO_INT_STAT_OFFSET);
b.判斷對應(yīng)的中斷棺亭,使用writel(reg_val&(1<<(CTP_IRQ_NO)),映射的IO地址 + PIO_INT_STAT_OFFSET);清除狀態(tài)位。
寫到這里蟋软,本應(yīng)該很順利镶摘,可是,在驅(qū)動(dòng)程序加載進(jìn)內(nèi)核的時(shí)候岳守,明顯是報(bào)錯(cuò)凄敢。錯(cuò)誤我就不貼出來了,可是我可以將中斷信息附上:
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中斷號(hào)60已經(jīng)注冊進(jìn)內(nèi)核了。我們現(xiàn)在使用的一個(gè)IO中斷是被包含在里面的蒙袍。所以俊卤,需要在內(nèi)核中找到sunxi-gpio是怎么去注冊中斷,而我們就需要將我們的中斷程序內(nèi)容附加到已經(jīng)注冊的中斷上去害幅。
在 marsboard\marsboard-a20-linux-sdk-v1.2\linux-sunxi\drivers\gpio\Gpio-sunxi.c中我們可以找到函數(shù):
/* 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;
}
里面最重要的函數(shù)是:
/**
* generic_handle_irq - Invoke the handler for a particular irq
* @irq: The irq number to handle
*
*/
int generic_handle_irq(unsigned int irq)
最終調(diào)用的是:
/*
* 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已經(jīng)將中斷基本處理做好了以现,我們要做的只是和它共享中斷狠怨。
3.4 實(shí)踐邏輯
sys_config.fex文件配置如下:
從上面的實(shí)驗(yàn)中,已經(jīng)發(fā)現(xiàn)在request_irq中設(shè)置邊沿等等觸發(fā)邑遏,在安裝ko文件的時(shí)候佣赖,都會(huì)報(bào)錯(cuò),從這里看出记盒,在共享中斷的時(shí)候憎蛤,是不允許設(shè)置其他的內(nèi)容的。那么,只能去找A20寄存器中關(guān)于io口中斷的設(shè)置俩檬。在這些設(shè)置已經(jīng)設(shè)置好的情況下萎胰,中斷應(yīng)該就能響應(yīng)了。這里貼出一個(gè)比較簡單的驅(qū)動(dòng)程序:
#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
/*設(shè)置為輸入棚辽,沒有上下拉*/
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");```
測試程序?yàn)椋?
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;
}
也就是說技竟,只需要打開一個(gè)文件即可。
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
測試結(jié)果為:
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屈藐,簡單的測試程序榔组,暫時(shí)就這樣了。下面再去優(yōu)化联逻。
### 3.5 polling查詢按鍵值
使用查詢按鍵值的程序代碼如下:
key.c驅(qū)動(dòng)程序:
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
/*設(shè)置為輸入搓扯,沒有上下拉*/
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命令查詢應(yīng)用程序占用cpu資源:
![按鍵驅(qū)動(dòng)程序資源占用](http://upload-images.jianshu.io/upload_images/3549048-fb05a6115daedca8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
所以,使用這種方式遣妥,在程序設(shè)計(jì)角度來說擅编,明顯是不合理的。所以箫踩,必須要進(jìn)行改進(jìn)爱态。
### 3.6 使用休眠方式
由于3.5的程序中,應(yīng)用程序是一直在讀取內(nèi)核數(shù)據(jù)境钟。這樣的方式明顯將整個(gè)系統(tǒng)的性能降低锦担。而程序設(shè)計(jì)的整體思路是,只需要在內(nèi)核有數(shù)據(jù)的時(shí)候慨削,應(yīng)用層讀取就可以了洞渔。所以呢,可以使用這種方式缚态,應(yīng)用層讀取數(shù)據(jù)磁椒,在內(nèi)核沒有數(shù)據(jù)需要返回的時(shí)候,內(nèi)核可以讓該進(jìn)程異步阻塞玫芦,這樣就不會(huì)占用整個(gè)系統(tǒng)的資源浆熔。
這種技巧,使用的主要是內(nèi)核提供的幾個(gè)函數(shù):
> * wait_event_interruptible
> * wake_up_interruptible
> * DECLARE_WAIT_QUEUE_HEAD
> * init_waitqueue_head
關(guān)于這種操作方式桥帆,可以參考文件:
\marsboard\marsboard-a20-linux-sdk-v1.2\linux-sunxi\drivers\staging\iio\adc\Ad7780.c
我把程序貼在下面:
key.c驅(qū)動(dòng)程序如下:
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
/*設(shè)置為輸入医增,沒有上下拉*/
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資源](http://upload-images.jianshu.io/upload_images/3549048-34240160c4c26f5e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)