九、【interrupt】按键中断
一、linux内核中中断的使用
1、申请中断
static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
参数:
- irq: 中断号
- handler:中断处理程序
- flags:中断标志,
对应外部中断,flags表示中断触发的方式:
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
对于其他一般中断,flags用于设置中断的处理方式:
#define IRQF_DISABLED 0x00000020
#define IRQF_SAMPLE_RANDOM 0x00000040
#define IRQF_SHARED 0x00000080
- name:自定义的中断名。
- dev:传递给中断处理程序的实参。
返回值:
成功返回0,否则返回负值。
2、中断处理程序
irqreturn_t xxx_handler(int irq, void * dev
参数:
- irq:中断号。
- dev:中断申请函数传过来的参数。
返回值:
返回值为以下三个枚举值,IR_HANDLED表示中断被正确处理了
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
};
3、中断注销
void free_irq(unsigned int irq, void *dev_id)
参数:
- irq:中断号。
- dev_id:中断的结构体
二、查看中断
三、中断号
中断号其实就是一个整数,该整数是内核给每个中断源的唯一编号。
6818主板的中断号定义在s5p6818_irq.h中
1、外部中断号
#define IRQ_GPIO_A_START (IRQ_GPIO_START + PAD_GPIO_A)
#define IRQ_GPIO_B_START (IRQ_GPIO_START + PAD_GPIO_B)
#define IRQ_GPIO_C_START (IRQ_GPIO_START + PAD_GPIO_C)
#define IRQ_GPIO_D_START (IRQ_GPIO_START + PAD_GPIO_D)
#define IRQ_GPIO_E_START (IRQ_GPIO_START + PAD_GPIO_E)
2、其他一般中断的中断号
#define IRQ_PHY_MCUSTOP (0 + 32)
#define IRQ_PHY_DMA0 (1 + 32)
#define IRQ_PHY_DMA1 (2 + 32)
#define IRQ_PHY_CLKPWR_INTREQPWR (3 + 32)
#define IRQ_PHY_CLKPWR_ALIVEIRQ (4 + 32)
#define IRQ_PHY_CLKPWR_RTCIRQ (5 + 32)
#define IRQ_PHY_UART1 (6 + 32) // pl01115_Uart_modem
#define IRQ_PHY_UART0 (7 + 32) // UART0_MODULE
#define IRQ_PHY_UART2 (8 + 32) // UART1_MODULE
#define IRQ_PHY_UART3 (9 + 32) // pl01115_Uart_nodma0
#define IRQ_PHY_UART4 (10 + 32) // pl01115_Uart_nodma1
#define IRQ_PHY_UART5 (11 + 32) // pl01115_Uart_nodma2
#define IRQ_PHY_SSP0 (12 + 32)
#define IRQ_PHY_SSP1 (13 + 32)
#define IRQ_PHY_SSP2 (14 + 32)
#define IRQ_PHY_I2C0 (15 + 32)
#define IRQ_PHY_I2C1 (16 + 32)
#define IRQ_PHY_I2C2 (17 + 32)
#define IRQ_PHY_DEINTERLACE (18 + 32)
#define IRQ_PHY_SCALER (19 + 32)
#define IRQ_PHY_AC97 (20 + 32)
#define IRQ_PHY_SPDIFRX (21 + 32)
#define IRQ_PHY_SPDIFTX (22 + 32)
#define IRQ_PHY_TIMER_INT0 (23 + 32)
#define IRQ_PHY_TIMER_INT1 (24 + 32)
#define IRQ_PHY_TIMER_INT2 (25 + 32)
#define IRQ_PHY_TIMER_INT3 (26 + 32)
#define IRQ_PHY_PWM_INT0 (27 + 32)
#define IRQ_PHY_PWM_INT1 (28 + 32)
#define IRQ_PHY_PWM_INT2 (29 + 32)
#define IRQ_PHY_PWM_INT3 (30 + 32)
#define IRQ_PHY_WDT (31 + 32)
#define IRQ_PHY_MPEGTSI (32 + 32)
#define IRQ_PHY_DPC_P (33 + 32)
#define IRQ_PHY_DPC_S (34 + 32)
#define IRQ_PHY_RESCONV (35 + 32)
#define IRQ_PHY_HDMI (36 + 32)
#define IRQ_PHY_VIP0 (37 + 32)
#define IRQ_PHY_VIP1 (38 + 32)
#define IRQ_PHY_MIPI (39 + 32)
#define IRQ_PHY_VR (40 + 32)
#define IRQ_PHY_ADC (41 + 32)
#define IRQ_PHY_PPM (42 + 32)
#define IRQ_PHY_SDMMC0 (43 + 32)
#define IRQ_PHY_SDMMC1 (44 + 32)
#define IRQ_PHY_SDMMC2 (45 + 32)
#define IRQ_PHY_CODA960_HOST (46 + 32)
#define IRQ_PHY_CODA960_JPG (47 + 32)
#define IRQ_PHY_GMAC (48 + 32)
#define IRQ_PHY_USB20OTG (49 + 32)
#define IRQ_PHY_USB20HOST (50 + 32)
#define IRQ_PHY_CAN0 (51 + 32)
#define IRQ_PHY_CAN1 (52 + 32)
#define IRQ_PHY_GPIOA (53 + 32)
#define IRQ_PHY_GPIOB (54 + 32)
#define IRQ_PHY_GPIOC (55 + 32)
#define IRQ_PHY_GPIOD (56 + 32)
#define IRQ_PHY_GPIOE (57 + 32)
#define IRQ_PHY_CRYPTO (58 + 32)
#define IRQ_PHY_PDM (59 + 32)
#define IRQ_PHY_TMU0 (60 + 32)
#define IRQ_PHY_TMU1 (61 + 32)
#define IRQ_PHY_VIP2 (72 + 32)
3、三个中断共用一个中断处理程序
如何区分是哪个中断发生导致中断处理程序被内核调用的?
(1)通过中断号----某个中断发生,该中断的中断号会传递给中断处理程序的第一个形参
(2)通过注册时,传递给中断处理程序不同 (void * dev)
4、中断处理程序是一个原子过程
中断处理过程是原子的,不能在中断处理程序中使用可能导致阻塞的函数,例如:
ssleep 、copy_to_user/copy_from_user、获取信号量..
不然会导致如下错误:
[ 39.085000] Exception stack(0xc0a93f58 to 0xc0a93fa0)
[ 39.085000] 3f40: ffffffed 00000000
[ 39.085000] 3f60: 00000000 00000000 c0a92000 c0b2c248 c0738290 c0a92000 c0a92000 c0ab0128
[ 39.085000] 3f80: 00000000 00000000 00000019 c0a93fa0 c000f83c c000f840 600d0013 ffffffff
[ 39.085000] [] (__irq_svc+0x40/0x70) from [] (default_idle+0x2c/0x30)
[ 39.085000] [] (default_idle+0x2c/0x30) from [] (cpu_idle+0xd8/0x104)
[ 39.085000] [] (cpu_idle+0xd8/0x104) from [] (start_kernel+0x328/0x334)
[ 39.085000] ---[ end trace 340d61c57173f93b ]---
[ 39.085000] BUG: scheduling while atomic: swapper/0/0/0x00010002---------在原子过程中启动了调度器
内核中提供的延时函数:以下的函数是忙等延时,不会休眠。ssleep是休眠延时,我们中断处理程序中不能使用忙等延时。
udelay()
ndelay()
四、按键驱动程序
key_drv.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct key_gpio_t{
unsigned int irq;
char irqname[20];
unsigned char keyvalue;
};
static struct key_gpio_t key_gpio[]=
{
{IRQ_GPIO_A_START+28,"KEY2_GPIOA28",2},
{IRQ_GPIO_B_START+30,"KEY3_GPIOB30",3},
{IRQ_GPIO_B_START+31,"KEY4_GPIOB31",4},
{IRQ_GPIO_B_START+9, "KEY6_GPIOB9",6},
};
static char keyvalue = 0;
ssize_t key_read(struct file *filp, char __user * buf, size_t size, loff_t *oft)
{
int ret;
if(size !=1)
{
return -EINVAL;
}
ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
if(ret != 0)
{
return (size -ret);
}
keyvalue=0;
return size;
}
static irqreturn_t key_handler(int irq, void * dev)
{
struct key_gpio_t keytmp=*(struct key_gpio_t *)dev;
keyvalue =keytmp.keyvalue;
mdelay(500); //按键防抖
return IRQ_HANDLED;
}
struct file_operations key_misc_fops=
{
.read = key_read,
};
static struct miscdevice key_misc={
.minor = MISC_DYNAMIC_MINOR,
.name = "key_misc",
.fops = &key_misc_fops,
};
static int __init key_init(void)
{
int ret,i;
printk(KERN_INFO"key_init\n");
ret = misc_register(&key_misc);
if(ret < 0)
{
printk(KERN_INFO"key misc register fail.\n");
goto misc_register_err;
}
for(i=0;i<4;i++)
{
ret = request_irq(key_gpio[i].irq, key_handler,IRQF_TRIGGER_FALLING,key_gpio[i].irqname,(void*)&key_gpio[i]);
if(ret < 0)
{
printk(KERN_INFO"request_irq fail.\n");
goto irq_request_err;
}
}
return 0;
irq_request_err:
while(i--)
{
free_irq(key_gpio[i].irq,NULL);
}
misc_register_err:
return 0;
}
static void __exit key_exit(void)
{
int i;
printk(KERN_INFO"key_exit\n");
misc_deregister(&key_misc);
for(i=0;i<4;i++)
{
free_irq(key_gpio[i].irq,(void *)&key_gpio[i]);
}
}
module_init(key_init);
module_exit(key_exit);
MODULE_LICENSE("GPL");
main.c
#include
#include
#include
#include
#include
#include
int main()
{
int fd,ret;
char keyvalue=0;
fd = open("/dev/key_misc",O_RDWR);
if(fd<0)
{
perror("open key_misc error!");
}
while(1)
{
ret=read(fd,&keyvalue,1);
if(ret !=1)
{
perror("read error");
continue;
}
if(keyvalue!=0)
{
printf("keyvalue=%d\n",keyvalue);
}
}
close(fd);
}
相关链接: