邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛
分类: 嵌入式
2015-08-18 12:55:54
上一篇是不断查询按键的状态,CPU要一直占用。
这节主要利用中断方式,当按键没有被按下时,进入休眠状态;
当按下按键时,唤醒休眠进程,返回按键值。
3. 测试程序中断函数原型点击(此处)折叠或打开
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/delay.h>
- #include <linux/irq.h>
- #include <asm/uaccess.h>
- #include <asm/irq.h>
- #include <asm/io.h>
- #include <asm/arch/regs-gpio.h>
- #include <asm/hardware.h>
- static struct class *thirddrv_class;
- static struct class_device *thirddrv_class_dev;
- volatile unsigned long *gpfcon;
- volatile unsigned long *gpfdat;
- volatile unsigned long *gpgcon;
- volatile unsigned long *gpgdat;
- static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
- /* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */
- static volatile int ev_press = 0;
- struct pin_desc{
- unsigned int pin;
- unsigned int key_val;
- };
- /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
- /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
- static unsigned char key_val;
- struct pin_desc pins_desc[4] = {
- {S3C2410_GPF0, 0x01},
- {S3C2410_GPF2, 0x02},
- {S3C2410_GPG3, 0x03},
- {S3C2410_GPG11, 0x04},
- };
- /*
- * 当按键被按下的时候,会调用这个函数。在这个函数中:1. 确定按键值; 2. 置位按键被按下的标志位,并且唤醒进程。
- */
- static irqreturn_t buttons_irq(int irq, void *dev_id)
- {
- // void *dev_id 这个参数从注册函数传递过来。
- struct pin_desc * pindesc = (struct pin_desc *)dev_id;
- unsigned int pinval;
- pinval = s3c2410_gpio_getpin(pindesc->pin);
- if (pinval)
- {
- /* 松开 */
- key_val = 0x80 | pindesc->key_val;
- }
- else
- {
- /* 按下 */
- key_val = pindesc->key_val;
- }
- ev_press = 1; /* 表示中断发生了 */
- wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
- return IRQ_RETVAL(IRQ_HANDLED);
- }
- // 在打开的函数中,进行中断注册
- static int third_drv_open(struct inode *inode, struct file *file)
- {
- // 注册按键的中断
- // int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
- /* 1. irq: 表示中断号
2. handler: 表示中断处理函数
3. irqflags: 表示中断类型。
4. devname: 表示设备名字,自己定义的。
5. dev_id: 传递给中断处理函数handler的参数。
- */
- request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
- request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
- request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
- request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
- return 0;
- }
- ssize_t third_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
- {
- if (size != 1)
- return -EINVAL;
- /* 如果没有按键动作,则进入休眠,不会运行下面的语句。通过判断ev_press的状态来唤醒进程。当按键按下时,ev_press为1,唤醒休眠进程。*/
- wait_event_interruptible(button_waitq, ev_press);
- /* 程序运行到这里的时候,表示按键已经被按下,进程被唤醒,读取按键值,返回到应用空间, 并将按键状态值清除。*/
- copy_to_user(buf, &key_val, 1);
- ev_press = 0;
- return 1;
- }
- int third_drv_close(struct inode *inode, struct file *file)
- {
- free_irq(IRQ_EINT0, &pins_desc[0]);
- free_irq(IRQ_EINT2, &pins_desc[1]);
- free_irq(IRQ_EINT11, &pins_desc[2]);
- free_irq(IRQ_EINT19, &pins_desc[3]);
- return 0;
- }
- static struct file_operations sencod_drv_fops = {
- .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
- .open = third_drv_open,
- .read = third_drv_read,
- .release = third_drv_close, // 什么时候用???
- };
- int major;
- static int third_drv_init(void)
- {
- major = register_chrdev(0, "third_drv", &sencod_drv_fops);
- thirddrv_class = class_create(THIS_MODULE, "third_drv");
- thirddrv_class_dev = class_device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */
- gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
- gpfdat = gpfcon + 1;
- gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
- gpgdat = gpgcon + 1;
- return 0;
- }
- static void third_drv_exit(void)
- {
- unregister_chrdev(major, "third_drv");
- class_device_unregister(thirddrv_class_dev);
- class_destroy(thirddrv_class);
- iounmap(gpfcon);
- iounmap(gpgcon);
- }
- module_init(third_drv_init);
- module_exit(third_drv_exit);
- MODULE_LICENSE("GPL");
/**
* request_irq - allocate an interrupt line
* @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQ occurs
* @irqflags: Interrupt type flags
* @devname: An ascii name for the claiming device
* @dev_id: A cookie passed back to the handler function
*
* This call allocates interrupt resources and enables the
* interrupt line and IRQ handling. From the point this
* call is made your handler function may be invoked. Since
* your handler function must clear any interrupt the board
* raises, you must take care both to initialise your hardware
* and to set up the interrupt handler in the right order.
*
* Dev_id must be globally unique. Normally the address of the
* device data structure is used as the cookie. Since the handler
* receives this value it makes sense to use it.
*
* If your interrupt is shared you must pass a non NULL dev_id
* as this is required when freeing the interrupt.
*
* Flags:
*
* IRQF_SHARED Interrupt is shared
* IRQF_DISABLED Disable local interrupts while processing
* IRQF_SAMPLE_RANDOM The interrupt can be used for entropy
*
*/
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
// 参数说明:
1. irq: 表示中断号
2. handler: 表示中断处理函数
3. irqflags: 表示中断类型。
4. devname: 表示设备名字,自己定义的。
5. dev_id: 传递给中断处理函数handler的参数。
4. 测试点击(此处)折叠或打开
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdio.h>
- /* thirddrvtest
- */
- int main(int argc, char **argv)
- {
- int fd;
- unsigned char key_val;
- fd = open("/dev/buttons", O_RDWR);
- if (fd < 0)
- {
- printf("can't open!\n");
- }
- while (1)
- {
- read(fd, &key_val, 1); // 问题:这句话将调用驱动函数third_drv_read,而进入休眠状态,那么下面的打印语句还会运行吗??
- printf("key_val = 0x%x\n", key_val); //如果没有按键按下,则进入休眠,这句话不会被打印。
- }
- return 0;
- }
5. 小结
利用中断 + 休眠的方式,来读取按键被按下时的状态。
中断的方式,不需要一直占用CPU。但是应用程序还是一直在查询,只不过当按键没被按下的时候,进入休眠态。
知识点:
(1)中断注册函数
(2)如何注册休眠进程,以及如何唤醒等等。