1.按键(中断处理)——混杂设备驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/irqreturn.h>
#include <linux/interrupt.h>
#include <mach/irqs.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define GPGCON (0x56000060)
#define GPGDAT (0x56000064)
#define GPGUP (0x56000068)
static unsigned int *regGPGCON;
static unsigned int *regGPGDAT;
static unsigned int *regGPGUP;
int key = 0;
irqreturn_t key_handler(int irq_num, void *dev)
{
if(irq_num == IRQ_EINT8)
{
key = 1;
}
return IRQ_HANDLED;
}
int key_open(struct inode *p_node, struct file *fp)
{
printk("kernel open\n");
return 0;
}
int key_release(struct inode *p_node, struct file *fp)
{
return 0;
}
ssize_t key_write(struct file *fp, const char __user *user_buffer, size_t n, loff_t *offset)
{
int ret;
copy_from_user(&ret,user_buffer,4);
return 4;
}
ssize_t key_read(struct file *fp, char __user *user_buffer, size_t n, loff_t *offset)
{
copy_to_user(user_buffer, &key,4);
key = 0;
return sizeof(key);
}
static struct file_operations fops =
{
.owner = THIS_MODULE,
.open = key_open,
.release = key_release,
.read = key_read,
};
static struct miscdevice key_device =
{
.minor = MISC_DYNAMIC_MINOR,
.fops = &fops,
.name = "key"
};
static int __init key_init(void)
{
int ret;
ret = misc_register(&key_device);
if(ret)
{
printk("misc_register is error");
return ret;
}
ret = request_irq(IRQ_EINT8, key_handler, IRQF_TRIGGER_RISING | IRQF_DISABLED, "key_irq", &key_device);
if(ret)
{
printk("request_irq is error\n");
misc_deregister(&key_device);
}
regGPGCON = ioremap(GPGCON,4);
regGPGDAT = ioremap(GPGDAT, 4);
regGPGUP = ioremap(GPGUP,4);
return 0;
}
static void __exit key_exit(void)
{
iounmap(regGPGCON);
iounmap(regGPGDAT);
iounmap(regGPGUP);
disable_irq(IRQ_EINT8);
free_irq(IRQ_EINT8, &key_device);
misc_deregister(&key_device);
}
module_init(key_init);
module_exit(key_exit);
MODULE_LICENSE("GPL");
2. 按键队列——混杂设备驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/irqreturn.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <mach/irqs.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define GPGCON (0x56000060)
#define GPGDAT (0x56000064)
#define GPGUP (0x56000068)
static unsigned int *regGPGCON;
static unsigned int *regGPGDAT;
static unsigned int *regGPGUP;
static wait_queue_head_t wq;
static int conditon;
int key = 0;
irqreturn_t key_handler(int irq_num, void *dev)
{
if(irq_num == IRQ_EINT8)
{
key = 1;
}
conditon = 1;
wake_up(&wq);
return IRQ_HANDLED;
}
int key_open(struct inode *p_node, struct file *fp)
{
printk("kernel open\n");
return 0;
}
int key_release(struct inode *p_node, struct file *fp)
{
return 0;
}
ssize_t key_write(struct file *fp, const char __user *user_buffer, size_t n, loff_t *offset)
{
return 4;
}
ssize_t key_read(struct file *fp, char __user *user_buffer, size_t n, loff_t *offset)
{
conditon = 0;
wait_event_interruptible(wq, conditon);
copy_to_user(user_buffer, &key,4);
key = 0;
return sizeof(key);
}
static struct file_operations fops =
{
.owner = THIS_MODULE,
.open = key_open,
.release = key_release,
.read = key_read,
};
static struct miscdevice key_device =
{
.minor = MISC_DYNAMIC_MINOR,
.fops = &fops,
.name = "key"
};
static int __init key_driver_init(void)
{
int ret;
ret = misc_register(&key_device);
if(ret)
{
printk("misc_register is error");
return ret;
}
ret = request_irq(IRQ_EINT8, key_handler, IRQF_TRIGGER_RISING | IRQF_DISABLED, "key_irq", &key_device);
if(ret)
{
printk("request_irq is error\n");
misc_deregister(&key_device);
}
init_waitqueue_head(&wq);
regGPGCON = ioremap(GPGCON,4);
regGPGDAT = ioremap(GPGDAT, 4);
regGPGUP = ioremap(GPGUP,4);
return 0;
}
static void __exit key_exit(void)
{
iounmap(regGPGCON);
iounmap(regGPGDAT);
iounmap(regGPGUP);
disable_irq(IRQ_EINT8);
free_irq(IRQ_EINT8, &key_device);
misc_deregister(&key_device);
}
module_init(key_driver_init);
module_exit(key_exit);
MODULE_LICENSE("GPL");
3. ADC中断队列——混杂设备驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/irqreturn.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <mach/irqs.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define ADCCON (0x58000000)
#define CLKCON (0x4C00000C)
#define ADCDAT0 (0x5800000C)
static unsigned int *regADCCON;
static unsigned int *regADCDAT0;
static unsigned int *regCLKCON;
static wait_queue_head_t wq;
static int condition;
unsigned int adc = 0;
void wake_up_queue(void)
{
condition = 1;
wake_up(&wq);
}
irqreturn_t adc_handler(int irq_num, void *dev)
{
printk("adc interrupt!\n");
if(irq_num == IRQ_ADC)
{
adc = *regADCDAT0 & 0x3ff;
}
wake_up_queue();
return IRQ_HANDLED;
}
int adc_open(struct inode *p_node, struct file *fp)
{
printk("kernel open\n");
return 0;
}
int adc_release(struct inode *p_node, struct file *fp)
{
return 0;
}
ssize_t adc_write(struct file *fp, const char __user *user_buffer, size_t n, loff_t *offset)
{
int ret;
copy_from_user(&ret,user_buffer,4);
return 4;
}
ssize_t adc_read(struct file *fp, char __user *user_buffer, size_t n, loff_t *offset)
{
condition = 0;
*regADCCON |= (1 << 0);//打开adc
wait_event_interruptible(wq, condition);
copy_to_user(user_buffer, &adc, 4);
return sizeof(adc);
}
static struct file_operations fops =
{
.owner = THIS_MODULE,
.open = adc_open,
.release = adc_release,
.read = adc_read,
};
static struct miscdevice adc_device =
{
.minor = MISC_DYNAMIC_MINOR,
.fops = &fops,
.name = "adc"
};
void adc_init(void)
{
unsigned int t;
t = *regADCCON;
t |= (1 << 14); //使能预分频
t &= ~(0xff << 6);
t |= (49 << 6);//给预分频值
t &= ~(0x07 << 3);// 模拟输入通道选择AIN0(引脚)
t &= ~(1 << 2);// 正常工作模式
t &= ~(1 << 1);//使能ADC转换
*regADCCON = t;
}
static int __init adc_driver_init(void)
{
int ret;
ret = misc_register(&adc_device);
if(ret)
{
printk("misc_register is error");
return ret;
}
ret = request_irq(IRQ_ADC, adc_handler, IRQF_DISABLED, "adc_irq", &adc_device);
if(ret)
{
printk("request_irq is error\n");
misc_deregister(&adc_device);
}
init_waitqueue_head(&wq);//初始化队列
regADCCON = ioremap(ADCCON,4);
regCLKCON = ioremap(CLKCON,4);
regADCDAT0 = ioremap(ADCDAT0, 4);
*regCLKCON |= (1 << 15);
adc_init();
return 0;
}
static void __exit adc_exit(void)
{
iounmap(regADCCON);
iounmap(regADCDAT0);
iounmap(regCLKCON);
disable_irq(IRQ_ADC);
free_irq(IRQ_ADC, &adc_device);
misc_deregister(&adc_device);
}
module_init(adc_driver_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");
4. 中断顶半部和底半部
中断(Interrupt)是计算机系统中处理异步事件的一种机制,当硬件设备需要CPU进行处理时,会发出中断信号。中断处理通常会分为顶半部(Top Half)和底半部(Bottom Half),以优化中断处理的效率。
顶半部(Top Half)
顶半部是中断处理的第一部分,通常会在中断发生时立即执行。其主要特点如下:
- 高优先级执行:顶半部代码在中断上下文中执行,具有最高的优先级,以确保快速响应硬件设备的请求。
- 最小化处理:顶半部的处理时间应尽可能短,因为它会阻塞其他中断的处理。顶半部通常只执行必要的最小工作,比如读取硬件寄存器,确认中断源,保存一些状态信息等。
- 不可被其他中断打断:在某些系统中,顶半部的执行是不可中断的,这意味着在顶半部执行期间,不会处理其他中断,保证了顶半部的快速完成。
底半部(Bottom Half)
底半部是在顶半部之后执行的中断处理的第二部分。其主要特点如下:
- 延迟执行:底半部的处理在稍后时间执行,不需要立即完成,通常在内核的上下文中以较低优先级运行。
- 复杂处理:底半部适合执行较复杂、耗时的操作,比如数据传输、缓冲区处理、与用户空间的交互等。
- 可被其他中断打断:与顶半部不同,底半部的执行可以被其他中断打断,因为它的优先级较低。
实现方式
在Linux系统中,底半部的实现有多种方式,包括软中断(Softirq)、任务队列(Tasklet)和工作队列(Workqueue)。这些机制允许将较复杂和耗时的处理任务推迟到合适的时间进行,以减少中断处理对系统的影响。
总结
顶半部和底半部的划分有助于优化中断处理的效率。顶半部用于快速响应和处理最小的中断任务,而底半部则处理较为复杂和耗时的操作,确保系统能够及时响应硬件中断的同时,不会因中断处理而导致系统性能下降。