Programación de interrupciones de Linux

Programación de interrupciones de Linux

  Interrupción: se refiere a la ocurrencia de algún evento anormal durante el funcionamiento de la CPU. La CPU debe suspender primero el trabajo actual y luego procesar el evento anormal recién generado. Después del procesamiento, regresa al evento suspendido y continúa ejecutándose. . Por ejemplo, estamos usando un teléfono móvil para el chat de video de WeChat, y de repente alguien llama. En este momento, el teléfono móvil maneja la llamada entrante haciendo sonar el tono de llamada para notificar al usuario que la llamada está llegando.
  La interrupción es para hacer frente a los eventos que pueden ocurrir en el futuro.Los eventos de interrupción también se denominan eventos anormales. Con el procesamiento de interrupciones, la eficiencia del procesamiento de la CPU se puede mejorar en gran medida.
  En las microcomputadoras de un solo chip, a menudo también usamos interrupciones para manejar algunos eventos de emergencia, lo que nos ayuda a responder rápidamente a algunos eventos en tiempo real. Por lo tanto, cuando escribimos la función de servicio de interrupción, el código debe ser lo más conciso posible y no se debe tratar el ciclo infinito.Si hay muchas cosas que procesar, el bit de bandera debe establecerse en la interrupción y luego el código lógico se debe poner en la función principal para implementar.
  En el kernel de Linux, generalmente dividimos las interrupciones en una mitad superior y una mitad inferior. La mitad superior es principalmente para procesar códigos que consumen poco tiempo (como configurar banderas en un microcontrolador) y comenzar la mitad inferior de los códigos; la mitad inferior es principalmente para procesar códigos que consumen mucho tiempo y completar el procesamiento de eventos después de la respuesta a la interrupción.

1. Interrupción externa bajo Linux

  Para usar una interrupción externa, debe completar la configuración de los tres elementos de la interrupción: número de interrupción (irq), función de servicio de interrupción, modo de activación de interrupción (activación de nivel, activación de borde).

1.1 Funciones de interfaz relacionadas

  • Obtenga el número de interrupción gpio_to_irq

  Se proporciona una función conveniente en el kernel de Linux para obtener el número de interrupción del pin.

int gpio_to_irq(unsigned gpio)
función función: obtener el
valor de retorno del número de interrupción: devolver con éxito el número de interrupción irq correspondiente a GPIO

  • Registrar interrupción request_irq

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
función función: registrar interrupción
parámetros formales: irq --número de interrupción, valor de retorno de la función gpio_to_irq.
    handler -- función de servicio de interrupción.
     Prototipo de función de servicio: typedef irqreturn_t (*irq_handler_t)(int, void *);
    banderas: método de activación de interrupciones.
     #define IRQF_TRIGGER_RISING 0x00000001 //flanco ascendente
     #define IRQF_TRIGGER_FALLING 0x00000002 //flanco ascendente
     #define IRQF_TRIGGER_HIGH 0x00000004 //nivel alto
    #define IRQF_TRIGGER_LOW 0x00000008 //nivel bajo

    #define IRQF_ SHARED 0x00000080 //Nombre de interrupción compartida
   -- indicador de registro de interrupción.
   dev -- el parámetro pasado a la función de servicio de interrupción.
Valor devuelto: 0 se devuelve en caso de éxito y otros valores se devuelven en caso de error.

  • función de servicio de interrupción

typedef irqreturn_t (*irq_handler_t)(int, void *);
Función función:
parámetros formales de la función de servicio de interrupción : el primer parámetro es el número de interrupción; el segundo parámetro es el parámetro pasado por la función de registro
valor de retorno de desarrollo:
   enum irqreturn {     IRQ_NONE = (0 << 0), //Si no es la interrupción, devuelve este valor, solo usa     IRQ_HANDLED en la interrupción compartida = (1 << 0), //Ejecuta correctamente el programa de interrupción para devolver este valor, de uso común     IRQ_WAKE_THREAD = (1 < < 1), // significa activar el subproceso del controlador de interrupciones   };



  Nota: irqreturn_t (*irq_handler_t)(int, void *); las funciones con suspensión no pueden aparecer en la función, como la función msleep; esta función debe devolver un valor.

  • dar de baja free_irq

free_irq(unsigned int irq, void *dev_id)
función función: cerrar sesión
parámetro de interrupción : irq --número de interrupción, valor de retorno de la función gpio_to_irq.
   dev -- el parámetro pasado a la función de servicio de interrupción. Tiene que ser igual que al registrarse

2. Cola de trabajo

  La función de procesamiento de interrupciones se divide en la mitad superior de la interrupción y la mitad inferior de la interrupción. La implementación de la mitad superior del código es la función de servicio de interrupción, mientras que la cola de trabajo puede completar la mitad inferior del código.

2.1 Introducción a la cola de trabajo

  En el sistema operativo, si necesitamos procesar un trabajo, a menudo necesitamos crear una tarea para unirnos a la cola de programación del kernel. Una tarea corresponde a una función de procesamiento. Si desea procesar diferentes transacciones, debe crear varias tareas diferentes. Las tareas son la unidad básica de programación de la CPU. Cuanto mayor sea el número de tareas, mayor será el costo de programación. El mecanismo de cola de trabajo simplifica el mecanismo básico de creación y procesamiento de tareas. Una cola de trabajo corresponde al procesamiento de una tarea de tarea de entidad. Se pueden montar varias entidades de trabajo en la cola de trabajo, y cada trabajo puede corresponder a una función de procesamiento de trabajo diferente. Es decir, el usuario solo necesita crear una cola de trabajo y luego se pueden completar múltiples colas de trabajo con diferentes funciones de procesamiento.
   La cola de trabajo también tiene un mecanismo para posponer la ejecución del trabajo, la cola de trabajo puede posponer el trabajo y entregarlo a un hilo del kernel para su ejecución, es decir, la segunda mitad se puede ejecutar en el contexto del proceso. Lo más importante es que la cola de trabajo permite reprogramarse o incluso dormir.
  El procesamiento de la cola de trabajo depende de la tarea. Una cola de cola de trabajo creará tareas asociadas, una cola de trabajo montará varios trabajos para su procesamiento y cada trabajo tiene una función de procesamiento de trabajos. Cuando se programa la cola de trabajo, es decir, se ejecuta su tarea asociada, durante cada período de programación de tareas, se tomará un trabajo de la cola de trabajo para su procesamiento. Cuando se inicializa el módulo de cola de trabajo, se creará una cola de trabajo predeterminada del sistema y los usuarios pueden agregar trabajo a la cola para su ejecución según sea necesario.

2.2 Interfaz de funciones relacionadas con el trabajo

  • Estructura de trabajo struct work_struct
#include <linux/workqueue.h>
struct work_struct {
    
    
	atomic_long_t data;
	struct list_head entry;
	work_func_t func;  /*工作处理函数*/
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};

  En la estructura de trabajo, el miembro que debemos cuidar es la función de procesamiento de trabajo: work_func_t func , en términos simples, un trabajo corresponderá a una función de procesamiento. El prototipo de la función de procesamiento de trabajos es el siguiente:

#include <linux/workqueue.h>
typedef void (*work_func_t)(struct work_struct *work);
  • Inicializar trabajo INIT_WORK

#define INIT_WORK(_work, _func)
función de función: inicializa el trabajo, realiza
parámetros formales en forma de macro : _work --puntero de estructura de trabajo
   _func --función de procesamiento de trabajo

  • horario de trabajo horario_trabajo

int horario_trabajo(struct trabajo_estructura *trabajo)

2.3 Pasos de uso de la cola de trabajo

  1. Defina la estructura de trabajo struct work_struct e inicialice el trabajo INIT_WORK;
  2. Escriba la función de procesamiento de trabajo void (*work_func_t)(struct work_struct *work);
  3. Programar el trabajo en un lugar adecuado (generalmente en la mitad superior de la interrupción);

2.4 Ejemplo de uso de la cola de trabajo

  Tomemos el botón como ejemplo para realizar la detección del botón en el modo de interrupción, procesar la mitad inferior del código a través de la cola de trabajo y realizar el registro del dispositivo con el marco de dispositivos misceláneos.
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

  • K1-GPX3_2
  • K2 --GPX3_3
  • K3 --GPX3_4
  • K4 --GPX3_5
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/workqueue.h>

#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
struct work_struct key_work;/*工作结构体*/
struct _KEY
{
    
    
	unsigned int gpio;/*按键引脚*/
	char name[20];/*按键名*/
	int irq;/*中断号*/
	int key_num;/*按键编号*/
};
static struct _KEY KEY_GPIO_PIN[]=
{
    
    
	{
    
    EXYNOS4_GPX3(2),"key1",0,1},
	{
    
    EXYNOS4_GPX3(3),"key2",0,2},
	{
    
    EXYNOS4_GPX3(4),"key3",0,3},	
	{
    
    EXYNOS4_GPX3(5),"key4",0,4},	
};
static struct _KEY *key_p;
static unsigned int key_val;
/*工作服务函数*/
void key_work_func(struct work_struct *work)
{
    
    
	msleep(30);/*按键消抖*/
	if(gpio_get_value(key_p->gpio)==0)
	{
    
    
		printk(" key%d 按下\n",key_p->key_num);
	}
	else 
	{
    
    
		printk(" key%d 松开\n",key_p->key_num);
	}
	key_val=key_p->key_num;
}
/*中断服务函数*/
static irqreturn_t key_irq_handler(int irq, void *dev)
{
    
    
	key_p=(struct _KEY *)dev;
	schedule_work(&key_work);/*调度工作*/
	return IRQ_HANDLED;/*中断正常处理*/
}
static int key_open(struct inode *inode, struct file *file)
{
    
    
	printk("设备打开成功\n");
	return 0;
}
static ssize_t key_read(struct file *file, char __user *buf, size_t cnt, loff_t *seek)
{
    
    
	int res=copy_to_user(buf,&key_val, 4);
	key_val=0;
	return 4-res;
}
static int key_release(struct inode *inode, struct file *file)
{
    
    
	printk("设备关闭成功\n");
	return 0;
}
/*文件操作集合*/
static struct file_operations key_fops=
{
    
    
	.owner= THIS_MODULE, /*当前模块文件操作集合所有者*/
	.open=key_open,/*open函数接口*/
	.read=key_read,/*read函数接口*/
	.release=key_release,/*close函数接口*/
};
/*
字符设备注册:主设备+次设备号
主设备  --用来区分类(杂项设备、输入设备)
次设备号  --对应哪个设备
杂项设备的主设备号固定为:10
*/
static struct miscdevice key_miscdev = {
    
    
	.minor	= MISC_DYNAMIC_MINOR,/*次设备号255由内核分配*/
	.name	= "tiny4412_key",/*设备节点名字,会在/dev下生成*/
	.fops	= &key_fops,/**/
};

static int __init tiny4412_key_module_init(void)
{
    
    
	int i=0;
	int res;
    printk("hello,驱动注册成功\n");
	/*初始化工作*/
	INIT_WORK(&key_work,key_work_func);
	/*注册中断*/
	for(i=0;i<sizeof(KEY_GPIO_PIN)/sizeof(KEY_GPIO_PIN[0]);i++)
	{
    
    
		KEY_GPIO_PIN[i].irq=gpio_to_irq(KEY_GPIO_PIN[i].gpio);/*获取中断号*/
		res=request_irq(KEY_GPIO_PIN[i].irq,key_irq_handler,IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
												KEY_GPIO_PIN[i].name,&KEY_GPIO_PIN[i]);
		if(res)
		{
    
    
			printk("%d中断注册失败\n",i);
			return -1;
		}
	}
	/*注册杂项设备*/
	misc_register(&key_miscdev);
    return 0;
}

static void __exit tiny4412_key_module_cleanup(void)
{
    
    
	int i=0;
	/*注销中断*/
	for(i=0;i<sizeof(KEY_GPIO_PIN)/sizeof(KEY_GPIO_PIN[0]);i++)
	{
    
    
		free_irq(KEY_GPIO_PIN[i].irq,&KEY_GPIO_PIN[i]);
	}
	/*注销杂项设备*/
	misc_deregister(&key_miscdev);
	printk("Good-bye, 驱动注销成功\n");
}

module_init(tiny4412_key_module_init);/*驱动入口函数:注册驱动时调用*/
module_exit(tiny4412_key_module_cleanup);/*驱动出口函数:注销驱动时调用*/

MODULE_LICENSE("GPL");/*驱动许可证*/

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_44453694/article/details/126812705
Recomendado
Clasificación