中断
下半部机制
软中断
softirq_action
tasklet
工作队列
设备树
fmql:
代码
目的
使能key对应GPIO的中断,中断服务函数为使用定时器延时15ms;定时器处理函数为检测key的状态
设备树修改
fmql不用把system.dtb放到SD卡。修改设备树后要在FMQL-Linux-SDK-project-20230801文件夹下,重新编译。生成image.ub和uboot.elf,然后FSBL.out和user.bit 、uboot.elf生成BOOT.bin。把BOOT.bin和image.ub放到SD卡下即可。
irq.c
/* device-tree :
* key {
* compatible = "alientek,key";
* status = "okay";
* key-gpio = <&gpio0 12 GPIO_ACTIVE_LOW>;
*
* interrupt-parent=<&gpio0>;
* interrupts=<12 IRQ_TYPE_EDGE_BOTH>; // GPIO0_12
* };
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/cdev.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/kern_levels.h>
// #include <linux/semaphore.h>
#include <linux/of_irq.h> // add
#include <linux/irq.h> // add
#define KEY_COUNT 1
#define KEY_NAME "key"
/* 定义key状态 */
enum key_status {
KEY_PRESS = 0, // 按键按下
KEY_RELEASE , // 按键松开
KEY_KEEP , // 按键状态保持
};
struct key_dev {
dev_t devid; // 设备号
struct cdev cdev; // cdev
struct class *class; // class
struct device *device; // devie
// int major; // major
// int minor; // minor
// struct device_node *nd; // device_node
int key_gpio; // key_gpio
int irq_num; // 中断号
// int period; /* period (ms) */
struct timer_list timer; /* timer */
spinlock_t spinlock; /* spinlock */
};
static struct key_dev key;
static int status = KEY_KEEP;
static int key_open(struct inode * inode, struct file *filp){
return 0;
}
static ssize_t key_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *offt){
unsigned long flags;
int ret;
spin_lock_irqsave(&key.spinlock, flags);
ret = copy_to_user(buf, &status, sizeof(int)); // key_status --> APP
status = KEY_KEEP; // reset key_status
spin_unlock_irqrestore(&key.spinlock, flags);
return ret;
}
static ssize_t key_write(struct file *filp, const char __user *buf,
size_t cnt, loff_t *offt){
return 0;
}
static int key_release(struct inode * inode, struct file *filp){
return 0;
}
static void key_timer_function(struct timer_list * unused){
static int last_val = 1;
unsigned long flags;
int current_val;
spin_lock_irqsave(&key.spinlock, flags);
current_val = gpio_get_value(key.key_gpio); // 0: press; 1: release
if(current_val == 0 && last_val)
status = KEY_PRESS;
else if(current_val == 1 && !last_val)
status = KEY_RELEASE;
else
status = KEY_KEEP;
last_val = current_val;
spin_unlock_irqrestore(&key.spinlock, flags);
}
static irqreturn_t key_interrupt(int irq, void *dev_id)
{
/* 按键防抖
* 定时器延时15ms
*/
mod_timer(&key.timer, jiffies + msecs_to_jiffies(15));
return IRQ_HANDLED;
}
static int key_parse_dt(void)
{
struct device_node *nd;
const char *str;
int ret;
nd = of_find_node_by_path("/key");
if(NULL == nd){
printk(KERN_ERR "key: Failed to get key node\r\n");
return -EINVAL;
}
ret = of_property_read_string(nd, "status", &str);
if(!ret){
if(strcmp(str,"okay"))
return -EINVAL;
}
ret = of_property_read_string(nd, "compatible", &str);
if(ret < 0)
return ret;
if(strcmp(str, "alientek,key")){
printk(KERN_ERR "key: Compatible match failed\r\n");
return -EINVAL;
}
key.key_gpio = of_get_named_gpio(nd, "key-gpio", 0);
if(!gpio_is_valid(key.key_gpio)) {
printk(KERN_ERR "key: Failed to get key-gpio\r\n");
return -EINVAL;
}
key.irq_num = irq_of_parse_and_map(nd, 0);
if(!key.irq_num)
return -EINVAL;
return 0;
}
static int key_gpio_init(void)
{
unsigned long irq_flags;
int ret;
gpio_direction_input(key.key_gpio);
irq_flags = irq_get_trigger_type(key.irq_num);
if(IRQF_TRIGGER_NONE == irq_flags)
irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
ret = request_irq(key.irq_num, key_interrupt, irq_flags, "PS Key0 IRQ", NULL);
if(ret){
gpio_free(key.key_gpio);
return ret;
}
return 0;
}
static struct file_operations key_fops = { // 设备操作函数
.owner = THIS_MODULE,
.open = key_open,
.release = key_release,
.read = key_read,
.write = key_write,
};
static int __init key_init(void){
int ret = 0;
spin_lock_init(&key.spinlock); // 初始化自旋锁
ret = key_parse_dt();
if(ret) return ret;
ret = key_gpio_init();
if(ret) return ret;
/* 注册字符设备驱动 */
key.cdev.owner = THIS_MODULE;
cdev_init(&key.cdev, &key_fops); // 初始化cdev
ret = alloc_chrdev_region(&key.devid, 0, KEY_COUNT, KEY_NAME);
if(ret)
goto out1;
ret = cdev_add(&key.cdev, key.devid, KEY_COUNT); // 添加cdev
if(ret)
goto out2;
key.class = class_create(THIS_MODULE, KEY_NAME); // 创建类
if(IS_ERR(key.class)){
ret = PTR_ERR(key.class);
goto out3;
}
key.device = device_create(key.class, NULL, key.devid, NULL, KEY_NAME); //创建设备
if(IS_ERR(key.device)){
ret = PTR_ERR(key.device);
goto out4;
}
timer_setup(&key.timer, key_timer_function, 0);
return 0;
out4:
class_destroy(key.class);
out3:
cdev_del(&key.cdev);
out2:
unregister_chrdev_region(key.devid, KEY_COUNT);
out1:
free_irq(key.irq_num, NULL);
gpio_free(key.key_gpio);
return ret;
}
static void __exit key_exit(void){
/* delete timer */
del_timer_sync(&key.timer);
// 注销: 设备,类,cdev,设备号
// 释放GPIO
device_destroy(key.class, key.devid);
class_destroy(key.class);
cdev_del(&key.cdev);
unregister_chrdev_region(key.devid, KEY_COUNT);
free_irq(key.irq_num, NULL);
gpio_free(key.key_gpio);
}
module_init(key_init);
module_exit(key_exit);
MODULE_AUTHOR("Skylar <Skylar@33.com>");
MODULE_DESCRIPTION("FMQL IRQ");
MODULE_LICENSE("GPL");
keyAPP.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
/*
* @description : main主程序
* @param - argc : argv数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd, ret;
int key_val;
/* 传递两个参数 */
if(argc != 2){
printf("Usage:\n"
"\t.irqAPP /dev/key \n"
);
return -1;
}
fd = open(argv[1], O_RDWR);
if(fd < 0){
printf("ERROR: %s file open failed\r\n", argv[1]);
return -1;
}
/* 循环读取设备 */
for(;;){
readl(fs, &key_val, sizeof(int));
if(key_val == 0)
printf("Key Press\r\n");
else if(key_val == 1)
printf("Key Release\r\n");
}
close(fd);
return 0;
}
运行
因为我用的板子上没有按键,所以就复制一下正点原子的: