驱动-----中断底半部

news/2024/11/8 4:15:00 标签: 嵌入式硬件

之前处理中断在中断服务程序里面,就是我们自己注册了一个中断服务程序,一产生中断就自动调动这个程序。

但是中断的处理越快越好

同步调用:就是等调用的函数执行完毕然后再返回原来的位置。

异步调用:就是不等调用的函数执行完毕,回到原来的位置继续向下执行(线程执行)

中断源调用中断程序后,中断程序再去调度中断底半部,不等调度的程序执行完毕,继续执行原来的程序。这是异步调度。

中断的底半部实现的方式有:

1.软中断

2.tasklet(基于软中断实现)

3.workqueue

1.软中断

2.tasklet

属于中断,处理紧急事件,不能做耗时操作

3.workqueue

不属于中断,处理不紧急事件,可以做耗时的操作

中断上下文:中断相关操作,中断服务程序、软中断、tasklet

进程上下文:进程相关操作,系统调用(open,read,write)、workqueue

上下文:一般指的是代码中保护现场和恢复现场的这个位置的东西。

那么来写一下tasklet代码:

初始化:

200是tasklet_func的参数。

调度:

中断调用底半部tasklet,然后就返回,这时候底半部自己运行。

这样就好了

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <asm/string.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>

#define DEV_NAME "adc1"
#define ADCCON 0x58000000
#define ADCDAT0 0x5800000C
#define CLKCON 0x4C00000C
static volatile unsigned long * adccon;
static volatile unsigned long * adcdat0;
static volatile unsigned long * clkcon;
static wait_queue_head_t wq;
static int condition = 0;
static struct tasklet_struct tsk;

#define MAGIC_NUM 'x'
#define SET_CH 0
#define GET_CH 1

#define SET_CHANNEL _IOW(MAGIC_NUM, SET_CH, unsigned int)
#define GET_CHANNEL _IOR(MAGIC_NUM, GET_CH, unsigned int)

void adc_init(void)
{
	*adccon = (1 << 14) | (49 << 6);
}

int adc_set_channel(unsigned char ch)
{
	if(ch > 7)
		return -EINVAL;	

	*adccon &= ~(0x7 << 3);
	*adccon |= (ch << 3);

	return 0;
}

void adc_start(void)
{
	*adccon |= (1 << 0);
}

unsigned short adc_read(void)
{
	unsigned short data = 0;  //读取数据,启动转换
	data = *adcdat0 & 0x3ff;
	return data;
}

static void tasklet_func(unsigned long arg)
{
	condition = 1;
	wake_up_interruptible(&wq);
	printk("adc tasklet_func arg = %ld\n", arg);
}

static irqreturn_t irq_handler(int irq_num, void * dev_num)
{
	tasklet_schedule(&tsk);
	printk("adc irq_handler irq_num = %d  dev_num = %d\n", irq_num, *(int *)dev_num);
	return IRQ_HANDLED;
}

static int open(struct inode * node, struct file * file)
{
	adc_init();
	printk("adc open...\n");
	return 0;
}

static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{
	//copy_to_user();
	unsigned short value = 0;
	adc_start();
	condition = 0;
	wait_event_interruptible(wq, condition);	
	value = adc_read();
	copy_to_user(buf, &value, sizeof(value));

	printk("adc !!! read...\n");
	return sizeof(value);
}

static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{
	return 0;
}

static long ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{

	int ret = 0;
	unsigned int ch = 0;
	printk("cmd = %d  arg = %ld\n", cmd, arg);
	switch(cmd)
	{
		case SET_CHANNEL:
			copy_from_user(&ch, (unsigned int *)arg, sizeof(ch));
			printk("adc  ch = %d\n", ch);
			ret = adc_set_channel(ch);
			break;
		default:
			ret = -EINVAL;
			break;
	}

	return ret;
}

static int close(struct inode * node, struct file * file)
{
	printk("adc close...\n");
	return 0;
}

static struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.unlocked_ioctl = ioctl,
	.release = close
};

static struct miscdevice misc = 
{
	// major = 10
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEV_NAME,
	.fops = &fops
};

static int __init adc1_init(void)
{
	int ret = 0;
	ret = misc_register(&misc);
	if(ret < 0)
		goto err_misc_register;

	ret = request_irq(IRQ_ADC, irq_handler, IRQF_DISABLED, "irq_adc", &misc.minor);
	if(ret < 0)
		goto err_request_irq;

	adccon = ioremap(ADCCON, sizeof(*adccon));
	adcdat0 = ioremap(ADCDAT0, sizeof(*adcdat0));
	clkcon = ioremap(CLKCON, sizeof(*clkcon));
	*clkcon |= (1 << 15);
	printk("CLKCON = %lx\n", *clkcon);
	init_waitqueue_head(&wq);

	tasklet_init(&tsk, tasklet_func, 200);

	printk("adc1_init !!!  ##########################\n");
	return ret;

err_misc_register:
	printk("adc misc_register failed ###################\n");
	misc_deregister(&misc);

err_request_irq:
	printk("adc request_irq faiadc  ##########################\n");
	disable_irq(IRQ_ADC);
	free_irq(IRQ_ADC, &misc.minor);
	return ret;
}

static void __exit adc_exit(void)
{
	iounmap(adccon);
	iounmap(adcdat0);
	iounmap(clkcon);
	disable_irq(IRQ_ADC);
	free_irq(IRQ_ADC, &misc.minor);
	misc_deregister(&misc);
	printk("adc_exit  ##########################\n");
}

module_init(adc1_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");

运行结果如下:

从打印的这里也可以看出来中断调用底半部中断之后返回,然后才执行底半部中断,没有等底半部中断结束然后返回。是异步调用。

taklet不能耗时,延时俩秒会导致系统奔溃。不能耗时。也不能被打断,就是不能被同类的,或者workqueue打断。可以被硬中断给打断。

workqueue的代码:

调度:

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <asm/string.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/workqueue.h>

#define DEV_NAME "adc1"
#define ADCCON 0x58000000
#define ADCDAT0 0x5800000C
#define CLKCON 0x4C00000C
static volatile unsigned long * adccon;
static volatile unsigned long * adcdat0;
static volatile unsigned long * clkcon;
static wait_queue_head_t wq;
static int condition = 0;
static struct work_struct work;

#define MAGIC_NUM 'x'
#define SET_CH 0
#define GET_CH 1

#define SET_CHANNEL _IOW(MAGIC_NUM, SET_CH, unsigned int)
#define GET_CHANNEL _IOR(MAGIC_NUM, GET_CH, unsigned int)

void adc_init(void)
{
	*adccon = (1 << 14) | (49 << 6);
}

int adc_set_channel(unsigned char ch)
{
	if(ch > 7)
		return -EINVAL;	

	*adccon &= ~(0x7 << 3);
	*adccon |= (ch << 3);

	return 0;
}

void adc_start(void)
{
	*adccon |= (1 << 0);
}

unsigned short adc_read(void)
{
	unsigned short data = 0;  //读取数据,启动转换
	data = *adcdat0 & 0x3ff;
	return data;
}

static void workqueue_func(struct work_struct *work)
{
	ssleep(2);
	condition = 1;
	wake_up_interruptible(&wq);
	printk("adc workqueue_func \n");
}

static irqreturn_t irq_handler(int irq_num, void * dev_num)
{
	schedule_work(&work);
	printk("adc irq_handler irq_num = %d  dev_num = %d\n", irq_num, *(int *)dev_num);
	return IRQ_HANDLED;
}

static int open(struct inode * node, struct file * file)
{
	adc_init();
	printk("adc open...\n");
	return 0;
}

static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{
	//copy_to_user();
	unsigned short value = 0;
	adc_start();
	condition = 0;
	wait_event_interruptible(wq, condition);	
	value = adc_read();
	copy_to_user(buf, &value, sizeof(value));

	printk("adc !!! read...\n");
	return sizeof(value);
}

static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{
	return 0;
}

static long ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{

	int ret = 0;
	unsigned int ch = 0;
	printk("cmd = %d  arg = %ld\n", cmd, arg);
	switch(cmd)
	{
		case SET_CHANNEL:
			copy_from_user(&ch, (unsigned int *)arg, sizeof(ch));
			printk("adc  ch = %d\n", ch);
			ret = adc_set_channel(ch);
			break;
		default:
			ret = -EINVAL;
			break;
	}

	return ret;
}

static int close(struct inode * node, struct file * file)
{
	printk("adc close...\n");
	return 0;
}

static struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.unlocked_ioctl = ioctl,
	.release = close
};

static struct miscdevice misc = 
{
	// major = 10
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEV_NAME,
	.fops = &fops
};

static int __init adc1_init(void)
{
	int ret = 0;
	ret = misc_register(&misc);
	if(ret < 0)
		goto err_misc_register;

	ret = request_irq(IRQ_ADC, irq_handler, IRQF_DISABLED, "irq_adc", &misc.minor);
	if(ret < 0)
		goto err_request_irq;

	adccon = ioremap(ADCCON, sizeof(*adccon));
	adcdat0 = ioremap(ADCDAT0, sizeof(*adcdat0));
	clkcon = ioremap(CLKCON, sizeof(*clkcon));
	*clkcon |= (1 << 15);
	printk("CLKCON = %lx\n", *clkcon);
	init_waitqueue_head(&wq);

	INIT_WORK(&work, workqueue_func);

	printk("adc1_init !!!  ##########################\n");
	return ret;

err_misc_register:
	printk("adc misc_register failed ###################\n");
	misc_deregister(&misc);

err_request_irq:
	printk("adc request_irq faiadc  ##########################\n");
	disable_irq(IRQ_ADC);
	free_irq(IRQ_ADC, &misc.minor);
	return ret;
}

static void __exit adc_exit(void)
{
	iounmap(adccon);
	iounmap(adcdat0);
	iounmap(clkcon);
	disable_irq(IRQ_ADC);
	free_irq(IRQ_ADC, &misc.minor);
	misc_deregister(&misc);
	printk("adc_exit  ##########################\n");
}

module_init(adc1_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");

这里面加入延时俩秒不受影响,因为这是进程。


http://www.niftyadmin.cn/n/5743214.html

相关文章

线性数据结构之栈结构

栈&#xff08;Stack&#xff09;是一种线性数据结构&#xff0c;遵循“后进先出”&#xff08;Last In, First Out&#xff0c;LIFO&#xff09;的原则。栈主要有两种基本操作&#xff1a;push&#xff08;入栈&#xff09;和 pop&#xff08;出栈&#xff09;。在栈中&#x…

linux驱动-i2c子系统框架学习(1)

可以将整个 I2C 子系统用下面的框图来描述&#xff1a; 可以将上面这一 I2C 子系统划分为三个层次&#xff0c;分别为用户空间、内核空间和硬件层&#xff0c;内核空间就包括 I2C 设备驱动层、I2C 核心层和 I2C 适配器驱动层&#xff0c; 本篇主要内容就是介绍 I2C 子系统框架中…

Linux,shell基础,变量,数值运算

linux 一.shell基础1.什么是shell在linux内核与用户之间的解释器程序,通常指/bin/bash2.shell的使用方式1.交互式2.非交互式3.Bash基本特征1.快捷键2.历史命令3.重定向4.管道5.别名4.shell脚本1.规范脚本构成(1) #!指定解释器(2) # 注释信息(作者信息,步骤,思路,用途,变量等)(3…

i2c-tools 4.3 for Android 9.0

i2c-tools 4.3 Android 9.0下编译i2c-tools 4.3 下载源码 cd external wget https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/i2c-tools-4.3.tar.gz tar -zxvf i2c-tools-4.3.tar.gz 增加Android.mk Android.mk LOCAL_PATH : $(call my-dir)include $(C…

Proteus中数码管动态扫描显示不全(已解决)

文章目录 前言解决方法后记 前言 我是直接把以前写的 51 数码管程序复制过来的&#xff0c;当时看的郭天祥的视频&#xff0c;先送段选&#xff0c;消隐后送位选&#xff0c;最后来个 1ms 的延时。 代码在 Proteus 中数码管静态是可以的&#xff0c;动态显示出了问题——显示…

[spring源码]spring配置类解析

解析配置类 在启动Spring时&#xff0c;需要传入一个AppConfig.class给ApplicationContext&#xff0c;ApplicationContext会根据AppConfig类封装为一个BeanDefinition&#xff0c;这种BeanDefinition我们把它称为配置类BeanDefinition AnnotationConfigApplicationContext a…

Vue3版本的uniapp项目运行至鸿蒙系统

新建Vue3版本的uniapp项目 注意&#xff0c;先将HbuilderX升级至最新版本&#xff0c;这样才支持鸿蒙系统的调试与运行&#xff1b; 按照如下图片点击&#xff0c;快速升级皆可。 通过HbuilderX创建 官方文档指导链接 点击HbuilderX中左上角文件->新建->项目 创建vue3…

Git通讲-第二章(2):对象数据库

前言 理解了上篇文章的两大模型&#xff08;快照和不可变对象&#xff09;后&#xff0c;让我们看看Git 的核心——对象数据库&#xff0c;快照存储在 .git/objects 目录中&#xff0c;Git 通过这种方式管理项目的所有历史和数据。 Git对象数据库 下面是 .git/objects 目录的…