標籤:des style blog color io os 使用 ar for
將key.c中的timer機制、key_wq.c中的workqueue機制改成tasklet機制,完成中斷的下半部
需要特別注意:tasklet中不可休眠,其上下文是中斷,而workqueue是可以休眠的,wq的上下文是核心線程
所以這裡並沒有去除抖動,如果需要延時防震,timer或者workqueue更合適
如果需要休眠,就不能選擇tasklet
#include "key.h"#define S3C_ADDR_BASE0xF6000000#define S3C_ADDR(x)(S3C_ADDR_BASE + (x))#define S3C2410_PA_UART(0x50000000)#define S3C2410_PA_GPIO(0x56000000)#define S3C_VA_UARTS3C_ADDR(0x01000000)/* UART */#define S3C24XX_PA_UARTS3C2410_PA_UART#define S3C24XX_VA_UARTS3C_VA_UART#define S3C24XX_PA_GPIOS3C2410_PA_GPIO#define S3C24XX_VA_GPIO((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)#define S3C2410_GPIOREG(x) ((x) + S3C24XX_VA_GPIO)#define S3C2410_GPBCON S3C2410_GPIOREG(0x10)#define S3C2410_GPBDAT S3C2410_GPIOREG(0x14)#define S3C2410_GPBUP S3C2410_GPIOREG(0x18)#define S3C2410_GPFCON S3C2410_GPIOREG(0x50)#define S3C2410_GPFDAT S3C2410_GPIOREG(0x54)#define S3C2410_GPFUP S3C2410_GPIOREG(0x58)#define S3C2410_EXTINT0 S3C2410_GPIOREG(0x88)#define S3C2410_EXTINT1 S3C2410_GPIOREG(0x8C)#define S3C2410_EXTINT2 S3C2410_GPIOREG(0x90)#define S3C2410_CPUIRQ_OFFSET (16)#define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET)/* main cpu interrupts */#define IRQ_EINT0 S3C2410_IRQ(0) /* 16 */#define IRQ_EINT1 S3C2410_IRQ(1)/* 17 */#define IRQ_EINT2 S3C2410_IRQ(2)/* 18 */#define IRQ_EINT4t7 S3C2410_IRQ(4) /* 20 */#define IRQ_EINT4 S3C2410_IRQ(36) /* 52 */#define IRQF_DISABLED0x00000020#define IRQF_SHARED0x00000080#define IRQF_PROBE_SHARED0x00000100#define __IRQF_TIMER0x00000200#define IRQF_PERCPU0x00000400#define IRQF_NOBALANCING0x00000800#define IRQF_IRQPOLL0x00001000#define IRQF_ONESHOT0x00002000#define IRQF_NO_SUSPEND0x00004000#define IRQF_FORCE_RESUME0x00008000#define IRQF_NO_THREAD0x00010000#define IRQF_EARLY_RESUME0x00020000typedef struct gpioRes{int irqNum;/* 中斷號 */unsigned int ctrlReg;/* 控制寄存器,用於設定複用為GPIO */unsigned int ctrlBit; /* 控制寄存器的哪一位,用於複用為GPIO */unsigned int trigReg;/* 中斷方式寄存器,設定中斷的觸發方式 */unsigned int trigBit;/* 中斷方式寄存器哪一位,設定中斷的觸發方式 */unsigned int irqFlag;/* 共用還是不共用,註冊中斷的flag */char irqName[32];/* 中斷名稱 */unsigned int gpfPin;/* GPF的第幾個pin */char Reserved[10];/* 保留 */}gpioRes;unsigned int pressCnt[4] = {0, 0, 0, 0};#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))static int kobox_key_open(struct inode *inode, struct file *file){return 0;}static int kobox_key_release(struct inode *inode, struct file *file){return 0;}static long kobox_key_ioctl(struct file *file, unsigned int cmd, unsigned long arg){return 0;}static int kobox_key_read(struct file *file, char __user *buff, size_t count, loff_t *pos){printk("Enter [%s][%d]\n", __FUNCTION__,__LINE__);copy_to_user(buff, &pressCnt[0], sizeof(pressCnt));return 0;}/*GPF相關寄存器:GPFCON 0x56000050 R/W Configures the pins of port F 0x0GPFDAT 0x56000054 R/W The data register for port F Undef.GPFUP 0x56000058 R/W Pull-up disable register for port F 0x000K1: GPF1 -EINT1: GPF1 [3:2] 00 = Input01 = Output 10 = EINT[1] 11 = ReservedK2: GPF4 -EINT4: GPF4 [9:8] 00 = Input01 = Output 10 = EINT[4] 11 = ReservedK3: GPF2 -EINT2: GPF2 [5:4] 00 = Input01 = Output 10 = EINT2] 11 = ReservedK4: GPF0 -EINT0: GPF0 [1:0] 00 = Input01 = Output 10 = EINT[0] 11 = Reserved*/gpioRes key_gpio_res[4] ={{IRQ_EINT1, S3C2410_GPFCON, 2, S3C2410_EXTINT0, 5, NULL, "key1", 1},/* key1 */{IRQ_EINT4, S3C2410_GPFCON, 8, S3C2410_EXTINT0, 17, IRQF_SHARED, "key2", 4},/* key2 */{IRQ_EINT2, S3C2410_GPFCON, 4, S3C2410_EXTINT0, 9, NULL, "key3", 2},/* key3 */{IRQ_EINT0, S3C2410_GPFCON, 0, S3C2410_EXTINT0, 1, NULL, "key4", 0},/* key4 */};#define KEY_TIMER_DELAY1 (HZ/50) //按鍵按下去抖延時20毫秒 #define KEY_TIMER_DELAY2 (HZ/10) //按鍵抬起去抖延時100毫秒#define KEY_COUNT 4struct tasklet_struct gpio_key_tasklet;static void gpio_key_tasklet_handler(unsigned long data);static void set_gpio_as_eint(void){int i;unsigned uiVal = 0;for(i=0; i<ARRAY_SIZE(key_gpio_res); i++){uiVal = readl(key_gpio_res[i].ctrlReg);uiVal &= ~(0x01 << key_gpio_res[i].ctrlBit);uiVal |= (0x01 << (key_gpio_res[i].ctrlBit + 1));writel(uiVal, key_gpio_res[i].ctrlReg);}return;}static void set_gpio_as_gpio(void){int i;unsigned uiVal = 0;for(i=0; i<ARRAY_SIZE(key_gpio_res); i++){uiVal = readl(key_gpio_res[i].ctrlReg);uiVal &= ~(0x01 << key_gpio_res[i].ctrlBit);uiVal &= ~(0x01 << (key_gpio_res[i].ctrlBit + 1));writel(uiVal, key_gpio_res[i].ctrlReg);}return;}static irqreturn_t kobox_gpio_irq_handle(int irq, void *dev_id){int key;disable_irq_nosync(irq);printk("irq = %d\n", irq);if(dev_id)printk("dev_id:%s\n", dev_id);switch(irq){case IRQ_EINT1:key = 0;break;case IRQ_EINT4:key = 1;break;case IRQ_EINT2:key = 2;break;case IRQ_EINT0:key = 3;break;default:printk("invalid irq:%d\n", irq); return IRQ_HANDLED; }/* 去抖:延時100ms後,在buttons_timer中讀取按鍵狀態,如果還是按下的,就說明是被正常按下的使用timer是一種方式,後面再採用工作隊列、tasklet中的方式來處理*/tasklet_schedule(&gpio_key_tasklet);enable_irq(irq);return IRQ_RETVAL(IRQ_HANDLED);}/*GPF相關寄存器:GPFCON 0x56000050 R/W Configures the pins of port F 0x0GPFDAT 0x56000054 R/W The data register for port F Undef.GPFUP 0x56000058 R/W Pull-up disable register for port F 0x000K1: GPF1 -EINT1: GPF1 [3:2] 00 = Input01 = Output 10 = EINT[1] 11 = ReservedK2: GPF4 -EINT4: GPF4 [9:8] 00 = Input01 = Output 10 = EINT[4] 11 = ReservedK3: GPF2 -EINT2: GPF2 [5:4] 00 = Input01 = Output 10 = EINT2] 11 = ReservedK4: GPF0 -EINT0: GPF0 [1:0] 00 = Input01 = Output 10 = EINT[0] 11 = Reserved*//* 該函數返回0表示按鍵被按下,返回非0表示沒有再被按下,認為這是電平毛刺導致的,是雜訊訊號所以,該函數返回0,表示有按鍵被按下,返回非0表示是抖動*/static int get_gpio_portf_value(unsigned int pin){int ret;unsigned int uiVal = 0;printk("I AM @ [%s][%d], pin:%d\n", __FUNCTION__,__LINE__, pin);uiVal = readl(S3C2410_GPFDAT);ret = (0x1 << pin) & uiVal;printk("I AM @ [%s][%d], ret:%d\n", __FUNCTION__,__LINE__, ret);return ret;}static void gpio_key_tasklet_handler(unsigned long data){int i = 0;int ret;unsigned int pin;/* 中斷後100ms才會導致,執行該函數 */printk("i am at [%s][%d]\n", __FUNCTION__, __LINE__);//msleep(100);/* 將引腳由EINTX設定會GPIO */set_gpio_as_gpio();for(i=0; i<ARRAY_SIZE(key_gpio_res); i++){pin = key_gpio_res[i].gpfPin;/* 讀取對應引腳GPIO的值,返回0表示按鍵真正被按下,返回1表示抖動 */ret = get_gpio_portf_value(pin);if(0 == ret){pressCnt[i]++;printk("key0 pressed: pressCnt[%d]:%d\n", i, pressCnt[i]);}}/* 將引腳設定回EINTX */set_gpio_as_eint();return;}static int request_irq_for_gpio(void){int i;int ret;unsigned uiVal;int nouse;for(i=0; i<ARRAY_SIZE(key_gpio_res);i++){/* 設定中斷觸發方式:下降沿有效,觸發中斷,以便根據GPIO的值來判斷是否仍在按下 */uiVal = readl(key_gpio_res[i].trigReg);uiVal |= (0x1 << (key_gpio_res[i].trigBit));uiVal &= ~(0x1 << (key_gpio_res[i].trigBit + 1));writel(uiVal, key_gpio_res[i].trigReg);/* 註冊中斷 */ret = request_irq(key_gpio_res[i].irqNum, kobox_gpio_irq_handle, key_gpio_res[i].irqFlag, key_gpio_res[i].irqName, (void *)key_gpio_res[i].irqName);if(ret)printk("[func:%s][line:%d] request_irq failed, ret:%d!\n", __FUNCTION__,__LINE__,ret);elseprintk("[func:%s][line:%d] request_irq ok, irq:%d!\n", __FUNCTION__,__LINE__, key_gpio_res[i].irqNum);tasklet_init(&gpio_key_tasklet, gpio_key_tasklet_handler, NULL);}return 0;}struct file_operations kobox_key_operations = {.owner = THIS_MODULE,.open = kobox_key_open,.read = kobox_key_read,.release = kobox_key_release,.unlocked_ioctl = kobox_key_ioctl,};//GPB0int major;int minor;struct cdev cdev;struct class *kobox_key_class;struct device *pstdev = NULL;#define GPIO_KEY_NAME "kobox_key"int __init key_drv_init(void){int error;dev_t dev;printk("#####enter key_drv_init!\n");major = register_chrdev(0, GPIO_KEY_NAME, &kobox_key_operations);if (major < 0){printk(" can't register major number\n");return major;}/* create class */kobox_key_class = class_create(THIS_MODULE, GPIO_KEY_NAME);if(IS_ERR(kobox_key_class)){printk("class_create failed!\n");goto fail;}/* create /dev/kobox_gpio */pstdev = device_create(kobox_key_class, NULL, MKDEV(major, 0), NULL, GPIO_KEY_NAME);if(!pstdev){printk("device_create failed!\n");goto fail1;}/* set gpf0/1/2/4 as extern interrupt pins */set_gpio_as_eint();request_irq_for_gpio();printk("#####key_drv_init ok!\n");return 0;fail1:class_destroy(kobox_key_class);fail:unregister_chrdev(major, GPIO_KEY_NAME);return -1;}void __exit key_drv_exit(void){printk("exit gpio drv!\n");device_destroy(kobox_key_class, MKDEV(major, 0));class_destroy(kobox_key_class);unregister_chrdev(major, GPIO_KEY_NAME);return;}module_init(key_drv_init);module_exit(key_drv_exit);MODULE_LICENSE("GPL");
kobox -- key_tasklet.c -v1