精华内容
下载资源
问答
  • 电子-3中级中断法无盲点平面显示字.rar,单片机/嵌入式51单片机
  • 中断法矩阵键盘keil源程序,需要的可以看看…比较简洁……
  • 利用分子束外延技术,基于控制快门开关顺序的生长中断法,在GaSb衬底上生长了10周期和20周期的InAs(10 monolayer, 10 ML)/GaSb(10 ML) Ⅱ型超晶格材料。实验中,基于软件模拟对生长参数进行调控分析,实现了As-Sb高效的...
  • 矩阵键盘 中断法 msp430 C程序
  • 编写主函数实验代码(轮询法)配置方法(中断法)1. 配置LED2.配置延时3.配置按键4.配置中断5.编写主函数实验代码(中断法)注意事项: 简介 实验名称:LED闪烁+按键控制蜂鸣器 实验现象:两个LED小灯闪烁,按下按键...

    简介

    实验名称:LED闪烁+按键控制蜂鸣器
    实验现象:两个LED小灯闪烁,按下按键KEY1蜂鸣器响,再按一下蜂鸣器不不响
    硬件需求:STM32单片机,LED灯,按键,蜂鸣器

    配置方法(轮询法)

    1. 配置LED

    实现LED初始化函数LED_Init();

    2.配置延时

    实现延时函数:void delay_ms(u16 nms)

    3.配置按键

    实现按键初始化函数:void KEY_Init(void);
    实现按键扫描函数:uint8_t KEY_Scan(uint8_t mode)

    4.编写主函数

    最重要的就是主函数了,不能采用普通的延时做法:LED1亮–延时–LED1灭–LED2亮–延时–LED2灭–延时–扫描按键。这样的做法会使单片机处理信息阻塞,导致按键不灵敏。
    应该使用轮询法:单片机有个小延时,比如10ms,到时间后变量i++。i达到设定值LED闪烁,其他时间扫描按键实现蜂鸣器响/不响。

    实验代码(轮询法)

    KEY和LED的代码在前面都有,没有变化,这里只贴出主函数。

    int main(void)
    {
    	uint32_t i=0;
    	LED_Init();
    	KEY_Init(); 
    	LED1 = 1;
    	LED2 = 1;
    	while(1)
    	{
    		switch(i)
    		{
    			case 100:LED1 = 0;LED2 = 1;break;
    			case 200:LED1 = 1;LED2 = 0;i=0;break;
    		}
    		i++;
    		SysTick_delay_ms(10);
    		if(KEY_Scan(0)==2)BEEP=!BEEP;
    	}
     }
    
    

    主函数就是如此简单,就不唠叨了。主要就是while中不断扫描按键,当i的值达到100和200时实现LED翻转。重点是理解轮询法。

    配置方法(中断法)

    1. 配置LED

    实现LED初始化函数LED_Init();

    2.配置延时

    实现延时函数:void delay_ms(u16 nms)

    3.配置按键

    实现按键初始化函数:void KEY_Init(void);
    (这里就不需要按键扫描了)

    4.配置中断

    实现中断函数:void MyEXTI_Config(void);//优先级和端口要初始化
    实现中断服务函数:void EXTI9_5_IRQHandler(void);//根据硬件配置

    5.编写主函数

    实验代码(中断法)

    主函数:

    int main(void)
    {
    	uint32_t i=0;
    	LED_Init();
    	KEY_Init(); 
    	MyEXTI_Config();	//多了这句,配置中断
    	LED1 = 1;
    	LED2 = 1;
    	while(1)
    	{
    		switch(i)
    		{
    			case 100:LED1 = 0;LED2 = 1;break;
    			case 200:LED1 = 1;LED2 = 0;i=0;break;
    		}
    		i++;
    		SysTick_delay_ms(10);
    		//这里不需要按键扫描了
    	}
     }
    

    主函数中加入了中断配置函数,删除了按键扫描函数。
    按键控制蜂鸣器的功能放入了中断服务函数,如下:

    void EXTI9_5_IRQHandler(void)
    {
    	u32 i;
    	for(i=0;i<100000;i++);
    //	SysTick_delay_ms(30);
    	if(KEY0==0)
    //	if(EXTI_GetITStatus(EXTI_Line5) != RESET)
    	{
    		BEEP=!BEEP;
    	}
    	EXTI_ClearITPendingBit(EXTI_Line5);
    }
    

    注意事项:

    主函数中的while使用了滴答延时函数,在中断中就不能使用滴答延时函数,否则会造成程序卡死。中断中使用的是for循环实现延时。如果控制效果不佳,可以根据硬件延长或缩短for循环时间。

    程序已经过调试验证
    完整程序传送门:
    https://download.csdn.net/download/m0_46195580/13757707

    展开全文
  • 本篇是linux下按键设备驱动,采用的中断法,也是属于字符设备类的驱动,一起来动手吧。下面的话,老朋友可以跳过了直接从《需求描述》章节看起,新朋友可以试着看看。 前言 在嵌入式行业,有很多从业者。我们...

    本篇是linux下按键设备驱动,采用的中断法,也是属于字符设备类的驱动,一起来动手吧。下面的话,老朋友可以跳过了直接从《需求描述》章节看起,新朋友可以试着看看。

     

    前言

    在嵌入式行业,有很多从业者。我们工作的主旋律是拿开源代码,拿厂家代码,完成产品的功能,提升产品的性能,进而解决各种各样的问题。或者是维护一个模块或方向,一搞就是好几年。

     

    时间长了,中年润发现我们对从零开始编写驱动、应用、算法、系统、协议、文件系统等缺乏经验。没有该有的广度和深度。中年润也是这样,工作了很多年,都是针对某个问题点修修补补或者某个模块的局部删删改改。很少有机会去独自从零开始编写一整套完整的代码。

     

    当然,这种现状对于企业来说是比较正常的,可以降低风险。但是对于员工本身,如果缺乏必要的规划,很容易工作多年却还是停留在单点的层面,而丧失了提升到较高层面的机会。随着时间的增长很容易丧失竞争力。

     

    另外,根据中年润的经验,绝大多数公司对于0-5年经验从业者的定位主要是积极的问题解决者。而对于5-10经验从业者的定位主要是积极的系统规划者和引领者。在这种行业规则下,中年润认为,每个从业者都应该问自己一句,“5年后,我是否具备系统化把控软件的能力呢?”。

     

    当前的这种行业现状,如果我们不做出一点改变,是没有办法突破的。有些东西,仅仅知道是不够的,还需要深思熟虑的思考和必要的训练,简单来说就是要知行合一。

     

    也许有读者会有疑惑?这不就是重复造轮子么?我们确实是在重复造轮子,因为别人会造轮子那是别人的能力,我们自己会造轮子是我们自己的能力。在行业中,有太多的定制化需求是因为轮子本身有原生性缺陷,我们无法直接使用,或者需要对其进行改进,或者需要抽取开源代码的主体思想和框架,根据公司的需要定制自己的各项功能。设想,如果我们具备这种能力,必然会促使我们在行业中脱颖而出,而不是工作很多年一直在底层搬砖。底层搬砖没什么不好,问题是当有更廉价更激情的劳动力涌进来的时候,我们这些老的搬砖民工也就失去了价值。我们不会天天重复造轮子,我们需要通过造几个轮子使得自己具备造轮子的能力,从而更好的适应这个环境,适应这个世界。

     

    针对当前行业现状,中年润经过深思熟虑,想为大家做点实实在在的事情,希望能够帮助大家在巩固基础的同时提升系统化把控软件的能力。当然,中年润的水平也有限,有些观点也只是一家之谈,希望大家独立思考,谨慎采用,如果写的有错误或者不对的地方还请读者们批评斧正,我们一起共同进步。

     

    在这里简单介绍下中年润,中年润现在就职于一家大型国际化公司,工作经验6年,硕士毕业。曾经担任过组内的项目主管,项目经理,也曾经组建过新团队,带领大家冲锋陷阵。在工作中,有做的不错的地方,也有失误的地方,有激情的时刻,也有失落的时刻。现在偏安一隅,专心搞技术,目前个人规划的技术方向是嵌入式和AI基础设施建设,以及嵌入式和AI的融合发展。

     

    最后,说了这么多,中年润希望,在未来的日子里和未知的领域里,你我同行,为我们的美好生活而努力奋斗。

     

    总体目标

    本篇文章的目标是介绍如何从自顶向下从零编写linux下的按键字符设备驱动(采用中断法)。着力从总体思路,需求端,分析端,实现端,详尽描述一个完整需求的开发流程,是中年润多年经验的提炼,希望读者能够有所收获。最后的实战目标,请读者尽量完成,这样读者才能形成自己的思路。

    本示例采用arm920架构,天祥电子生产的tx2440a开发板,核心为三星的s3c2440。Linux版本为2.6.31,是已经移植好的版本。编译器为arm920t-eabi-4.1.2.tar。

     

    总体思路

    总体思路是严格遵循需求的开发流程来,不遗漏任何思考环节。读者在阅读时请先跟中年润的思路走一遍,然后再抛弃中年润的思路,按照自己的思路走一遍,如果遇到困难请先自己思考,实在不会再来参考中年润的思路和实现。

     

    中年润在写代码的的总体思路如下:

    需求描述—能够详细完整的描述一个需求。

    需求分析—根据需求描述,提取可供实现的功能,需要有定量或者定性的指标。(从宏观上确定需要什么功能)。

    需求分解—根据需求分析,考虑要实现需求所需要做的工作(根据宏观确定的功能,拆分成小的可单独实现的功能)。

    编写思路—根据需求分解从总体上描述应该如何编写代码,(解决怎么在宏观上实现)。

    详细步骤—根据编写思路,落实具体步骤,(解决怎么在微观上实现)。

    编写框架—根据编写思路,实现总体框架(实现编写思路里主体框架,细节内容留在具体代码里编写)。

    具体代码—根据编写框架和详细步骤,编写每一个函数里所需要实现的小功能,主要是实现驱动代码,测试代码。

    Makefile—用来编译驱动代码。

    目录结构—用来说明当完成编码后的结果。

    测试步骤—说明如何对驱动进行测试,主要是加载驱动模块,执行测试代码。

    执行结果—观察执行结果是否符合预期。

    结果总结—回顾本节的知识点,api,结构体。

    实战目标—说明如何根据本文档训练。

    请大家尽量按照自顶向下的学习思路来学习和实战,因为我们所有工作的动力都是我们心中的需求。这些步骤仅仅是我们达到目标所要走过的路。目录起到提纲挈领的重要作用,写的时候要实时看下提纲,看有没有偏离自己的方向。

     

    需求描述

    使用linux提供的字符设备api接口,编写一个按键字符设备驱动和一个测试代码,能够在输入./button命令后,按下按键时,在串口输出按下了哪个按键。同时用户不希望用户线程占用太多的cpu资源。

     

    需求分析

    对于按键字符设备驱动来说,测试代码就是我们的用户,因此我们可以通过分析测试代码的逻辑来分析我们的需求。

     

    首先梳理用户的工作流程,用户的工作流程如下:

    1用户调用open系统调用打开/dev/mybutton设备节点,获取fd

    2用户拿到fd之后,调用read系统调用读取fd中的值                                                                     

    3如果没有数据,希望用户进程睡眠,不希望大量占用cpu资源

    4如果有数据,直接返回数据

    5如果按下或松开按键,在中断中保存当前的数据,并唤醒用户线程,用户线程直接拿数据。

     

    根据用户的工作流程,我们可以梳理出驱动所要做的工作,下图说明了驱动所要做的主体,跟用户层是一一对应的。

    梳理完用户工作流程后,我们再来看下用户进程和中断之间的交互,这样对于为什么编写某些函数会有直观的感受。

    需求分解

    根据《需求分析》和用户的操作,我们需要做以下几件工作。

    1用户调用open系统调用打开/dev/mybutton设备节点,获取fd

    1.3   需要提供一个设备节点/dv/mybutton,

    1.4   需要提供一个操作设备节点的open函数

    2用户拿到fd之后,调用read系统调用读取fd中的值

    2.1 需要提供操作设备节点的read函数,用来将读取的数据返回     

    3如果没有数据,希望用户进程睡眠,不希望大量占用cpu资源

    3.1 需要能判断当前有无数据,需要在无数据时进入休眠状态

    4如果有数据,直接返回数据

    4.1 需要能判断当前有无数据,需要在有数据时能立即返回数据,并标记当前数据状态为无

    4.2 需要一个有无数据的标志,在产生数据时标记有,在读取完毕后标记无

    5如果按下或松开按键,在中断中保存当前的数据,并唤醒用户线程,用户线程直接拿数据。

    5.1 需要一个中断处理函数

    5.2 需要能够检测当前按下的是哪个按键

    5.3 需要在中断中保存数据,并标记当前数据状态为有

    5.4 需要唤醒用户进程

    5.5 在中断函数中保存数据后,read函数能够直接操作被保存的数据并返回给用户

     

    用一张图来总结上述驱动所要做的工作,如下图所示:

    编写思路

    编写思路主要用来搭建代码框架,解决在宏观上如何用代码实现驱动的功能。

    确定目标:实现需求分解中的所有函数

    Init函数,exit函数,open函数,read函,close函数,中断处理函数(在详细步骤中编写)

     

    确定基本思路:

    0搭建基础框架

    0.1编写代码框架,头文件

    0.2编写空的出口函数

    0.3编写空的入口函数

    0.4修饰出口函数,修饰入口函数,声明LICENSE

     

    在入口函数中所做的工作如下

    1入口函数

    1.1注册一个字符设备,名字为s3c_button

    1.1.1定义file_operations结构体

    1.1.2编写空的打开函数,关闭函数,读函数

    1.2创建一个类,名字为button-int,(决定了/sys/class下目录的名字)

    1.3创建一个设备节点,名字为mybutton(决定了/dev下文件的名字)

    1.4映射gpio的物理地址为虚拟地址,一个是控制寄存器地址,一个是数据寄存器地址

     

    在出口函数中所作的工作如下

    2出口函数

    2.1卸载字符设备

    2.2删除设备节点

    2.3销毁这个类

    2.4取消地址映射

     

    将上述步骤编写完成就得到了《编写框架》章节中的代码

     

    详细步骤

    详细步骤主要用来在代码框架里填充必要的细节代码,解决在微观上如何用代码实现驱动各个小功能。

     

    0搭建基础框架

    0.1编写代码框架,头文件

    0.2编写空的出口函数

    0.3编写空的入口函数

    0.4修饰出口函数,修饰入口函数,声明LICENSE

     

    在入口函数中所做的工作如下

    1入口函数

    1.1注册一个字符设备,名字为s3c_button

    1.1.1定义file_operations结构体

    1.1.2编写空的打开函数,关闭函数,读函数

    1.2创建一个类,名字为button-int,(决定了/sys/class下目录的名字)

    1.3创建一个设备节点,名字为mybutton(决定了/dev下文件的名字)

    1.4映射gpio的物理地址为虚拟地址,一个是控制寄存器地址,一个是数据寄存器地址

     

    在出口函数中所作的工作如下

    2出口函数

    2.1卸载字符设备

    2.2删除设备节点

    2.3销毁这个类

    2.4取消地址映射

     

    3编写file_operations里的操作函数

    3.1编写打开函数,注册中断和中断处理函数

    3.2编写关闭函数,释放中断

    3.3编写读函数,读取按下的键值

    3.4编写中断处理函数,判断当前是哪个按键按下,以及当前是按下还是松开

    所有函数的基本流程图如下图示所示:

    编写框架

    根据编写思路,实现总体框架(实现编写思路里主体框架,细节内容留在具体代码里编写)。

    /* 本文件名字为button_drv_int_skel.c*/
    /* 本文件是依照button-中断法驱动<编写思路>章节编写,本文件
     * 的目的是编写代码框架,不做具体细节的编写
     */
    
    /* 0.1编写代码框架,头文件 */
    #include <linux/module.h>
    #include <linux/ioport.h>
    #include <linux/io.h>
    #include <linux/platform_device.h>
    #include <linux/init.h>
    #include <linux/serial_core.h>
    #include <linux/serial.h>
    #include <linux/irq.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <asm/uaccess.h>
    #include <mach/hardware.h>
    #include <mach/regs-gpio.h>
    #include <mach/gpio-fns.h>
    #include <plat/regs-serial.h>
    
    volatile unsigned long * gpfconf;
    volatile unsigned long * gpfdat;
    static struct class *my_button_cls;
    static struct device * my_button_dev;
    static int major;
    
    /* 1.1.2编写空的打开函数 */
    static int button_open (struct inode * inode, struct file * filep)
    {
    	return 0;	
    }
    
    /* 1.1.2编写空的关闭函数 */
    static int button_release (struct inode * inode, struct file * filep)
    {
    	return 0;
    }
    
    /* 1.1.2编写空的读函数 */
    static ssize_t button_read (struct file * filep, char __user * buff, size_t size, loff_t * pos)
    {
    	return 0;
    }
    
    /* 1.1.1定义file_operations结构体 */
    static struct file_operations button_fops = {
    	.owner  = THIS_MODULE,
    	.open   = button_open,
    	.read   = button_read,
    	.release  = button_release,
    	
    };
    
    /* 0.3编写空的入口函数 */
    static int my_button_init(void)
    {
    	int ret = 0;
    	/* 1.1注册一个字符设备,名字为s3c_button */
    	major = register_chrdev(0,"s3c_button",&button_fops);
    	/* 1.2创建一个类,名字为button-int,(决定了/sys/class下目录的名字) */
    	my_button_cls = class_create(THIS_MODULE,"button-int");
    	/* 1.3创建一个设备节点,名字为mybutton(决定了/dev下文件的名字) */
    	my_button_dev = device_create(my_button_cls,NULL,MKDEV(major,0),NULL,"mybutton");
    	/* 1.4映射gpio的物理地址为虚拟地址,一个是控制寄存器地址,一个是数据寄存器地址 */
    	gpfconf = (volatile unsigned long *)ioremap(0x56000050,16);	
    	//获得数据寄存器地址
    	gpfdat  = gpfconf + 1;
    
    	return ret;
    }
    
    /* 0.2编写空的出口函数 */
    static void my_button_exit(void)
    {
    	/* 2.1卸载字符设备 */
    	unregister_chrdev(major," s3c_button");
    	/* 2.2删除设备节点 */
    	device_unregister(my_button_dev);
    	/* 2.3销毁这个类 */
    	class_destroy(my_button_cls);
    	/* 2.4取消地址映射 */
    	iounmap(gpfconf);
    	return;
    }
    
    /* 0.4修饰出口函数,修饰入口函数,声明LICENSE */
    module_init(my_button_init);
    module_exit(my_button_exit);
    MODULE_LICENSE("GPL");
    

    驱动代码

    根据《编写框架》《详细步骤》章节,编写每一个函数里所需要实现的小功能。

    /* 本文件名字为button_drv_int.c*/
    /* 本文件是依照button-中断法驱动<详细步骤>章节编写,本文件
     * 的目的是编写具体操作函数,不做框架介绍
     */
    
    /* 0.1编写代码框架,头文件 */
    #include <linux/module.h>
    #include <linux/ioport.h>
    #include <linux/io.h>
    #include <linux/platform_device.h>
    #include <linux/init.h>
    #include <linux/serial_core.h>
    #include <linux/serial.h>
    #include <linux/irq.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <asm/uaccess.h>
    #include <mach/hardware.h>
    #include <mach/regs-gpio.h>
    #include <mach/gpio-fns.h>
    #include <plat/regs-serial.h>
    
    volatile unsigned long * gpfconf;
    volatile unsigned long * gpfdat;
    static struct class *my_button_cls;
    static struct device * my_button_dev;
    static int major;
    
    DECLARE_WAIT_QUEUE_HEAD(button_waitq);
    
    //引脚描述符
    struct pin_desc{
    	int pin;//代表是哪个引脚
    	int key_val;//代表是按下还是松开
    };
    
    //引脚描述符,按引脚的不同定义不同的值,在中断中进行处理
    struct pin_desc pin_desc[4] = 
    {
    	{S3C2410_GPF4_EINT4,0x1},
    	{S3C2410_GPF5_EINT5,0x2}, 
    	{S3C2410_GPF6_EINT6,0x3},
    	{S3C2410_GPF7_EINT7,0x4},
    };
    
    //标志是否进入中断
    static int do_press_loos;
    
    /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
    /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
    /* 主要是为了区分是按下还是松开 */
    static unsigned char key_val;
    
    irqreturn_t button_irq(int irq, void * devid)
    {
    	int pin_val;
    	struct pin_desc * pin_readed;
    	/*
    	 *	1 获取当前引脚的电平值
    	 *	2 根据电平值判断当前是按下还是松开
    	 *		松开为高电平,返回0x8x
    	 *		按下为低电平,返回0x0x
    	 *	3 标记已有数据
    	 *	4 唤醒处在内核态的用户进程
    	 */		
    	pin_readed = (struct pin_desc *)devid;
    	//获取某个引脚是高还是低
    	pin_val = s3c2410_gpio_getpin(pin_readed->pin);
    		
    	if (pin_val) //松开是高电平
    	{
    		key_val = 0x80 | pin_readed->key_val;
    	}
    	else		//按下为低电平
    	{
    		//把当前按键的键值给一个全局静态变量,在read函数里给用户
    		key_val = pin_readed->key_val;
    	}
    	
    	//标记中断已经触发
    	do_press_loos = 1;
    
    	//唤醒用户的读进程
    	wake_up_interruptible(&button_waitq);
    
    	return IRQ_RETVAL(IRQ_HANDLED);
    }
    
    /* 1.1.2编写空的打开函数 */
    static int button_open (struct inode * inode, struct file * filep)
    {
    	/* 3.1编写打开函数,注册中断和中断处理函数 */
    	//request_irq里已经帮忙将GPF4-GPF7设置成中断引脚了
    	
    	int ret;
    	/* 注册外部中断4,类型为下降沿中断,名字为S1,
    	   中断处理函数为button_irq
    	   传入中断处理函数中的参数为pin_desc[0] */
    	ret = request_irq(IRQ_EINT4,button_irq,IRQ_TYPE_EDGE_FALLING,"S1",
    						(void *)&pin_desc[0]);
    	if (ret)
    		goto errout;
    	ret = request_irq(IRQ_EINT5,button_irq,IRQ_TYPE_EDGE_FALLING,"S2",
    						(void *)&pin_desc[1]);
    	if (ret)
    		goto errout;
    	ret = request_irq(IRQ_EINT6,button_irq,IRQ_TYPE_EDGE_FALLING,"S3",
    						(void *)&pin_desc[2]);
    	if (ret)
    		goto errout;
    	ret = request_irq(IRQ_EINT7,button_irq,IRQ_TYPE_EDGE_FALLING,"S4",
    						(void *)&pin_desc[3]);
    	if (ret)
    		goto errout;
    
    	return 0;
    errout:
    	return ret;	
    }
    
    /* 1.1.2编写空的关闭函数 */
    static int button_release (struct inode * inode, struct file * filep)
    {
    	/* 3.2编写关闭函数,释放中断 */
    	free_irq(IRQ_EINT4, &pin_desc[0]);
    	free_irq(IRQ_EINT5, &pin_desc[1]);
    	free_irq(IRQ_EINT6, &pin_desc[2]);
    	free_irq(IRQ_EINT7, &pin_desc[3]);
    	return 0;
    }
    
    /* 1.1.2编写空的读函数 */
    static ssize_t button_read (struct file * filep, char __user * buff, size_t size, loff_t * pos)
    {
    	int ret = 0;
    	/* 3.3编写读函数,读取按下的键值,以及当前是按下还是松开 */
    	if (size != 1)
    		return -EINVAL;
    	//根据do_press_loos判断,如果没有中断,直接进入休眠
    	wait_event_interruptible(button_waitq, do_press_loos);
    	//如果被唤醒拷贝数据到用户空间
    	ret = copy_to_user(buff, &key_val, sizeof(key_val));
    	if (ret) {
    		return -EFAULT;
    	}
    	//读取数据完毕后需要将标志位清零,表示暂时无数据可读
    	do_press_loos = 0;
    	return 1;	
    }
    
    /* 1.1.1定义file_operations结构体 */
    static struct file_operations button_fops = {
    	.owner  = THIS_MODULE,
    	.open   = button_open,
    	.read   = button_read,
    	.release  = button_release,
    	
    };
    
    /* 0.3编写空的入口函数 */
    static int my_button_init(void)
    {
    	int ret = 0;
    	/* 1.1注册一个字符设备,名字为s3c_button */
    	major = register_chrdev(0,"s3c_button",&button_fops);
    	/* 1.2创建一个类,名字为button-int,(决定了/sys/class下目录的名字) */
    	my_button_cls = class_create(THIS_MODULE,"button-int");
    	/* 1.3创建一个设备节点,名字为mybutton(决定了/dev下文件的名字) */
    	my_button_dev = device_create(my_button_cls,NULL,MKDEV(major,0),NULL,"mybutton");
    	/* 1.4映射gpio的物理地址为虚拟地址,一个是控制寄存器地址,一个是数据寄存器地址 */
    	gpfconf = (volatile unsigned long *)ioremap(0x56000050,16);	
    	//获得数据寄存器地址
    	gpfdat  = gpfconf + 1;
    
    	return ret;
    }
    
    /* 0.2编写空的出口函数 */
    static void my_button_exit(void)
    {
    	/* 2.1卸载字符设备 */
    	unregister_chrdev(major," s3c_button");
    	/* 2.2删除设备节点 */
    	device_unregister(my_button_dev);
    	/* 2.3销毁这个类 */
    	class_destroy(my_button_cls);
    	/* 2.4取消地址映射 */
    	iounmap(gpfconf);
    	return;
    }
    
    /* 0.4修饰出口函数,修饰入口函数,声明LICENSE */
    module_init(my_button_init);
    module_exit(my_button_exit);
    MODULE_LICENSE("GPL");
    

    测试代码

    测试代码编写思路如下:

    1打开/dev/mybutton文件

    2读取获得的值

    3打印获得的值

     

    测试代码如下:

    /* 本文件是button_test.c,是根据button-中断法驱动的
      * <测试代码>章节编写,主要任务是用来测试
      * button 驱动
      */
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    
    /* button 驱动,中断法实验
      */
    int main(int argc, char **argv)
    {
    	int fd;
    	unsigned char key_val;
    	int cnt;
    	
    	fd = open("/dev/mybutton", O_RDWR);
    	if (fd < 0)
    	{
    		printf("can't open!\n");
    	}
    
    	while (1)
    	{
    		read(fd, &key_val, 1);
    		printf("key_val = 0x%x\n", key_val);
    	}
    	
    	return 0;
    }
    

    Makefile

    KERN_DIR = /home/linux/tools/linux-2.6.31_TX2440A
    all:
           make -C $(KERN_DIR) M=`pwd` modules
    clean:
           make -C $(KERN_DIR) M=`pwd` modules clean
           rm -rf modules.order
    obj-m += button_drv_int.o
    

    目录结构

    代码编写完成的目录结构如下所示。直接执行make即可生成.ko文件。

    .

    ├── button_drv_int.c(驱动代码)

    ├── button_drv_int_skel.c(驱动框架代码)

    ├── button_test.c(驱动测试代码)

    └── Makefile(用来编译驱动代码)

     

    测试步骤

    0 在linux下的makefile +180行处配置好arch为arm,cross_compile为arm-linux-(或者arm-angstrom-linux-gnueabi-)

    1 在menuconfig中配置好内核源码的目标系统为s3c2440

    2 在pc上将驱动程序编译生成.ko,命令:make

    3 在pc上将测试程序编译生成elf可执行文件,生成的button就是我们所要使用的命令。

    编译:arm-angstrom-linux-gnueabi-gcc button_test.c -o button_test

    4 挂载nfs,这样就可以在开发板上看到pc端的.ko文件和测试文件

    mount -t nfs -o nolock,vers=2 192.168.0.105:/home/linux/nfs_root  /mnt/nfs

    5 insmod button_drv.ko,加载按键的驱动模块

    6执行命令./button,依次按下四个按键,观察串口的打印信息。

     

    执行结果

    执行完./button后,依次按下四个按键,串口的日志如下,按下一个按键一次,会出现很多次打印。

    [root@TX2440A 4-button-int]# insmod button_drv_int.ko

    [root@TX2440A 4-button-int]# lsmod

    button_drv_int 3356 0 - Live 0xbf000000

    [root@TX2440A 4-button-int]# ./button_test

    key_val = 0x4

    key_val = 0x4

    key_val = 0x3

    key_val = 0x2

    key_val = 0x2

    key_val = 0x2

    key_val = 0x2

    key_val = 0x2

    key_val = 0x2

    key_val = 0x1

    key_val = 0x1

    key_val = 0x1

    上述log表明,当前的驱动有按键抖动的现象,还需要继续优化。

     

    结果总结

    在本篇文章中,中年润跟读者分享了按键字符设备驱动的编写思路和方法,其中贯穿始终的有几个函数和关键数据结构,它们分别是:

    struct file_operations

    struct class

    struct class_device

     

    register_chrdev

    class_create

    device_create

    unregister_chrdev

    device_destroy

    class_destroy

    ioremap

    iounmap

    DECLARE_WAIT_QUEUE_HEAD

    request_irq

    free_irq

    请读者尽力去了解这些函数的作用,入参,返回值。

     

    问题汇总

    暂无

     

    实战目标

    1请读者根据《需求描述》章节,独立编写需求分析和需求分解。

    2请读者根据需求分析和需求分解,独立编写编写思路和详细步骤。

    3请读者根据编写思路,独立写出编写框架。

    4请读者根据详细步骤,独立编写驱动代码和测试代码。

    5请读者根据《Makefile》章节,独立编写Makefile。

    6请读者根据《测试步骤》章节,独立进行测试。

    7请读者抛开上述练习,自顶向下从零开始再编写一遍驱动代码,测试代码,makefile

    8如果无法独立写出7,请重复练习1-6,直到能独立写出7。

     

    参考资料

    《linux设备驱动开发祥解》

    《TX2440开发手册及代码》

    《韦东山嵌入式教程》

    《鱼树驱动笔记》

    《s3c2440a》芯片手册英文版和中文版

     

    致谢

    感谢在嵌入式领域深耕多年的前辈,感谢中年润的家人,感谢读者。没有前辈们的开拓,我辈也不能站在巨人的肩膀上看世界;没有家人的鼎力支持,我们也不能集中精力完成自己的工作;没有读者的关注和支持,我们也没有充足的动力来编写和完善文章。看完中年润的文章,希望读者学到的不仅仅是如何编写代码,更进一步能够学到一种思路和一种方法。

     

    为了省去驱动开发者搜集各种资料来写驱动,中年润后续有计划按照本模板编写linux下的常见驱动,敬请读者关注。

     

    联系方式

    微信群:自顶向下学嵌入式(可先加微信号:runzhiqingqing, 通过后会邀请入群。)

    微信订阅号:自顶向下学嵌入式  公众号:EmbeddedAIOT

    CSDN博客:中年润  网址:https://blog.csdn.net/chichi123137

    邮箱:834759803@qq.com

    QQ群:766756075

    更多原创文章请关注微信公众号。另外,中年润还代理销售韦东山老师的视频教程,欢迎读者点击下面二维码购买。在中年润这里购买了韦东山老师的视频教程,除了能得到韦东山官方的技术支持外,还能获得中年润细致入微的技术和非技术的支持和帮助。欢迎选购哦。

    公众号二维码如下图

    入群小助手二维码如下图

    中年润代理销售的韦东山视频购买地址如下图

    如果略有所获,欢迎赞赏,您的支持对中年润无比重要

    展开全文
  • 中断的方法和计数器的方法差不多,只是当计算器溢出时便产生一次中断,用户可以在中断程序中置标志,程序不断的查询该标志来决定是否发送或接收下一位,当然程序中需对中断进行初始化,同时编写中断程序。...
  • 中断法配置定时器 #include<reg52.h> sbit LED P0^4; void main() { EA = 1;//打开总中断 TMOD&=0xFC; TMOD|=0x01; TH0 = (65535-2000)/256;//定时2ms TL0 = (65535-2000)/%256; E...

    51单片机学习笔记———6.中断法配置定时器

    #include<reg52.h>
    sbit LED P0^4;
    void main()
    {
        EA = 1;//打开总中断
        TMOD&=0xFC;
        TMOD|=0x01;
        TH0 = (65535-2000)/256;//定时2ms
        TL0 = (65535-2000)/%256;
        ET0 = 1;
        TR0 = 1;
        while(1)
        {
            ...
        }
        void interruptTime() interrupt 1
        {
            static unsigned int n = 0;
            TH0 = (65535-2000)/256;
            TL0 = (65535-2000)%256;
            n++;
            if(n>=1000)
            {
                n = 0;
                LED =!LED;
            }
        }
    }
    
    展开全文
  • 中断法独立按键实验及while();语句分析  实验名称及内容:中断法独立按键实验  * 基于小王子stm8s开发板  ********************************************************/ include “iostm8s208mb.h”//...

    中断法独立按键实验及while();语句分析 
    实验名称及内容:中断法独立按键实验 
    * 基于小王子stm8s开发板 
    ********************************************************/

    include “iostm8s208mb.h”//主控芯片的头文件

    /**************常用数据类型定义***************/

    define u8 uint8_t

    define u16 uint16_t

    define u32 uint32_t

    typedef unsigned char uint8_t; 
    typedef unsigned int uint16_t; 
    typedef unsigned long uint32_t; 
    /**************端口/引脚定义区域**************/

    define LED PI_ODR_ODR0//连接至LED引脚

    /****************函数声明区域*****************
    void delay(u16 Count);//延时函数声明 
    /****************主函数区域*******************
    void main(void) 

    asm(“sim”);//MAIN程序的优先级配置为3级(关总中断) 
    //EXTI_CR1|=0x08;//配置PB为仅下降沿触发 
    EXTI_CR1|=0x04;//配置PB为仅上升沿触发 
    PB_DDR_DDR0=0;//配置PB0端口为输入模式 
    PB_CR1_C10=1;//配置PB0端口为弱上拉输入模式 
    PB_CR2_C20=1;//使能PB0端口外部中断

    PI_DDR_DDR0=1;//配置PI0端口为输出模式 
    PI_CR1_C10=1;//配置PI0端口为推挽输出模式 
    PI_CR2_C20=0;//配置PI0端口低斜率输出 
    LED=1;//上电后让PI0引脚输出高电平

    asm(“rim”);//MAIN程序的优先级由3级降低至0级(开总中断) 
    //从asm(“rim”);语句开始,若检测到外部中断产生,讲进入中断函数 
    while(1);//死循环 

    //****************中断函数区域****************

    pragma vector=0x05

    __interrupt void EXTI_PORTA_IRQHandler(void) 

    }

    pragma vector=0x06

    __interrupt void EXTI_PORTB_IRQHandler(void) 

    asm(“sim”); 
    PB_CR2_C20=0;//禁止PB0端口外部中断 
    while(PB_IDR_IDR0==0); //按键按下,则PB_IDR_IDR0为1,1不等于0,表达式为假,退出循环。这是一个按键检测语句,检测PB_IDR_IDR0即PB0所连接的按键是否被按下。 
    delay(100); 
    LED=!LED; 
    delay(100); 
    PB_CR2_C20=1;//使能PB0端口外部中断 
    asm(“rim”); 
    }

    pragma vector=0x07

    __interrupt void EXTI_PORTC_IRQHandler(void) 

    }

    pragma vector=0x08

    __interrupt void EXTI_PORTD_IRQHandler(void) 

    }

    pragma vector=0x09

    __interrupt void EXTI_PORTE_IRQHandler(void) 


    //**************************************************** 
    /******************************************************
    //延时函数delay(),有形参Count用于控制延时函数执行次数,无返回值 
    /******************************************************
    void delay(u16 Count) 

    u8 i,j; 
    while (Count–)//Count形参控制延时次数 

    for(i=0;i<50;i++) 
    for(j=0;j<20;j++); 

    }

    下面聊一下按键检测: 
    由于自己的基础比较差,所以在理解while(PB_IDR_IDR0==0);的原理时废了很大的精力。一直理解不了这个带分号的while语句的意义。 
    while(PB_IDR_IDR0==0);这是一条语句,当判断条件为真,为死循环,一直等待,当判断条件为假,退出循环。 
    同理,while(!1); 
    while(!PB_IDR_IDR0);当PB_IDR_IDR0为1时(被按下),判断条件为假,退出循环。当PB_IDR_IDR0位0时(未被按下),判断条件为真,死循环,一直等待。 
    while(!KEY1);一样的道理。

    展开全文
  • 定时器中断法扫描数码管及秒表的实现 程序源码 /*-----------------------包含头文件区域-------------------------*/ #include<reg52.h> //单片机头文件 /*-----------------------数据类型定义区域-----...
  • 2、实验目的:学习通过编程时钟0工作方式1中断法控制数码管0-59变化。 3、实验连接图。 4、实验代码。 #include<reg52.h> unsigned char seg7code[]={0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, ...
  • 中断法实现按键点灯

    2016-12-23 23:26:00
     汇编代码 .global _start /* 声明一个全局的标号 */ .global key_isr _start: /* 设置栈,以调用c函数... /* 开总中断 */ ... bic r0, r0, #0x00000080 /* 清楚第7位,IRQ中断禁止位,写0使能IRQ */ m
  • 单片机-外部中断与计数定时器外部中断计数定时器 外部中断 中断的概念:CPU在执行主程序的时候,中断源发送请求。CPU响应中断源,去执行中断源的程序。中断源往往含有优先级,优先级高的中断源可以向优先级低的中断...
  • 中断法键盘扫描c程序

    2014-08-18 15:25:19
    程序效果:按下按键,蜂鸣器响,数码管有相应的键值 显示,按下E键继电器关,按下C键继电器开。
  • 硬件环境:STC89C52 软件环境:IDE Keil uVision V4.10  编译器 C51 V9.0 代码如下: [cpp] view plaincopy /**************************************...方法3:中断法  硬件:11.0592MHz
  • 简单按键驱动,采用中断法 驱动程序如下,third_drv.c #include &lt;linux/module.h&gt; #include &lt;linux/kernel.h&gt; #include &lt;linux/fs.h&gt; #includ...
  • 硬件环境:STC89C52 软件环境:IDE Keil uVision V4.10  编译器 C51 V9.0 代码如下: /******************************...方法3:中断法 硬件:11.0592MHz晶振,STC89C52,RXD P1.0 TXD P1.1 波特率:9600 描述:
  • 利用串口调试助手给单片机发数据,单片机会将改数据原样送回电脑
  • 1、定时器查询实现LED闪烁 ;2013/4/23 ;延时1s闪烁 org 0000h ajmp start org 30h start:mov r0,#20 mov p0,#0ffh ;关灯 mov TMOD,#00000001B mov TH0,#3CH
  • S3C2440按键驱动之中断法

    千次阅读 2013-03-18 17:58:18
    上一篇博客实现了一个S3C2440的按键驱动 但是采用查询实现,基本占尽了CPU资源,现在写一个改进型驱动,采用中断方式。 在上一篇基础上,我们要将按键对应的几个引脚设为中断引脚,多个引脚共享一个中断函数,...
  • #include<iocc2530.h> #include<string.h> #define uint unsigned int #define uchar unsigned char #define LED1 P1_0 #define LED2 P1_5 /********************** *函数名称:Init_Led ...
  • S5PV210有4个向量中断控制器(VIC),每个向量中断控制器包含32个中断源。 当某个中断源产生中断时,CPU会自动的将VICxVECTADDRy(x=0,1,2,3,y=0-31)寄存器的值赋给VICxADDRESS(x=0,1,2,3),因此我们可以把...
  • s5pv210中断法控制LED灯

    千次阅读 2014-11-09 00:28:15
    前面介绍过轮询的方法控制LED灯,这里将介绍如何使用按键产生中断的方法控制LED灯。 linux内核的中断需要使用request_irq函数来申请,并用free_irq来释放它,在此就不介绍它的原理,下面将直接讲如何使用它们来...
  • //使URX0中断标志赋值0,因为已经进入中断函数,故赋值0 c = U0DBUF; switch( c ) { case ‘1’: LED1 = 1; LED2 =LED3 =LED4 =0; break; case ‘2’: LED2 = 1; LED1 =LED3 =LED4 =0; break; case ‘3’: LED3 = 1;...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,331
精华内容 532
关键字:

中断法