S3C6410开发板按键驱动代码分析及测试代码分析

发布者:吉州古玩斋最新更新时间:2021-03-23 来源: eefocus关键字:S3C6410  开发板  按键驱动 手机看文章 扫描二维码
随时随地手机看文章

在本文中,我们对S3C6410开发板按键驱动代码的实现过程进行分析,然后通过一个实例对按键功能进行测试。在本文的资源中包含了设备驱动的源码和测试的源码。


一、设备驱动源码分析

按键的设备驱动主要实现了模块的初始化、模块的卸载、设备打开、设备关闭、设备读取、设备阻塞功能。


1、模块初始化

模块的初始化的源码如下所示。


static int __init dev_init(void)

{

int ret;

 

ret = misc_register(&misc);

 

printk (DEVICE_NAME"tinitializedn");

 

return ret;

}


这段代码只实现了一个功能,就是使用misc_register()函数向内核注册按键的混杂设备。


2、模块卸载

模块卸载的代码比较简单,如下所示。


static void __exit dev_exit(void)

{

misc_deregister(&misc);

}


模块卸载代码实现的功能是将按键的混杂设备从内核中取消注册。


3、设备打开

设备打开代码主要实现了按键对应的GPIO接口的初始化。


static int s3c64xx_buttons_open(struct inode *inode, struct file *file)

{

    int i;

    int err = 0;

    

    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {

if (button_irqs[i].irq < 0) {

continue;

}

        err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, 

                          button_irqs[i].name, (void *)&button_irqs[i]);

        if (err)

            break;

    }

 

    if (err) {

        i--;

        for (; i >= 0; i--) {

    if (button_irqs[i].irq < 0) {

continue;

    }

    disable_irq(button_irqs[i].irq);

            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);

        }

        return -EBUSY;

    }

 

    ev_press = 1;

    

    return 0;

}


在这段代码中,使用request_irq()函数用来初始化GPIO接口,主要是开启了GPIO接口的输入中断功能,将中断号与中断处理函数、中断描述结构体绑定在一起。其中,button_irqs[i].irq是中断号,buttons_interrupt是中断处理函数,(void *)&button_irqs[i]是指向中断描述结构体的指针,函数还将中断的模式设置为双边沿触发。当按下或松开按键时,会产生下降沿或上升沿,就会触发中断,中断会把相应的中断描述结构体保持下来,并转到buttons_interrupt函数去处理中断。


buttons_interrupt()函数的源码如下所示。


static irqreturn_t buttons_interrupt(int irq, void *dev_id)

{

struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;

int down;

int number;

unsigned tmp;

 

udelay(0);

number = button_irqs->number;

switch(number) {

case 0: case 1: case 2: case 3: case 4: case 5:

tmp = readl(S3C64XX_GPNDAT);

down = !(tmp & (1< break;

case 6: case 7:

tmp = readl(S3C64XX_GPLDAT);

down = !(tmp & (1 << (number + 5)));

break;

default:

down = 0;

}

 

if (down != (key_values[number] & 1)) {

key_values[number] = '0' + down;

 

        ev_press = 1;

        wake_up_interruptible(&button_waitq);

    }

 

    return IRQ_RETVAL(IRQ_HANDLED);

}


中断处理函数的重要输入是中断描述结构体dev_id,结构体内部包含了,中断号、按键编号、以及按键名。程序根据按键编号对GPIO数据寄存器的值进行读取,从而判断外部输入电平的状态,并将按键状态保存到key_values[ ]数组中,‘1’代表按键按下,‘0’代表按键没有按下。当按键状态有变化时,程序调用wake_up_interruptible(&button_waitq);将阻塞的内核排队队列唤醒。


4、设备关闭

设备关闭代码如下所示


static int s3c64xx_buttons_close(struct inode *inode, struct file *file)

{

    int i;

    

    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {

if (button_irqs[i].irq < 0) {

    continue;

}

free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);

    }

 

    return 0;

}


功能比较简单,主要是将按键的中断功能禁止。


5、设备读取

设备的读取代码主要是读取8个按键的状态,代码如下。


static int s3c64xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)

{

    unsigned long err;

 

    if (!ev_press) {

if (filp->f_flags & O_NONBLOCK)

    return -EAGAIN;

else

    wait_event_interruptible(button_waitq, ev_press);

    }

    

    ev_press = 0;

 

    err = copy_to_user((void *)buff, (const void *)(&key_values), min(sizeof(key_values), count));

 

    return err ? -EFAULT : min(sizeof(key_values), count);

}

代码实现的功能是将设备状态数组key_values[ ]回传到用户程序的buf中。


6、设备阻塞

设备阻塞函数是用来相应用户空间的select()系统调用函数的,代码如下


static unsigned int s3c64xx_buttons_poll( struct file *file, struct poll_table_struct *wait)

{

    unsigned int mask = 0;

    poll_wait(file, &button_waitq, wait);

    if (ev_press)

        mask |= POLLIN | POLLRDNORM;

    return mask;

}


阻塞函数功能比较简单,是在没有按键按下或松开时将程序阻塞,当有按键按下或松开时退出阻塞状态。


二、测试代码分析

编写了一个简单的测试代码对按键的驱动进行测试,代码如下。


int main()

{

int fd_led,fd_key;

fd_set read_set;

int max_fd;

struct timeval timeout;

int ret;

 

char buf[8];

 

fd_led = open("/dev/leds",0);

fd_key = open("/dev/buttons",O_RDONLY);

 

if(fd_led<0||fd_key<0)

{

fprintf(stderr,"can't open leds or buttons!n");

return 1;

}

 

while(1)

{

//清零描述符集合

FD_ZERO(&read_set);

FD_SET(fd_key,&read_set);

max_fd = fd_key;

//设置超时时间

timeout.tv_sec=3;

timeout.tv_usec=0;

ret = select(max_fd+1, &read_set, 0, 0, &timeout);

 

if(ret>0)

{

read(fd_key,buf,8);

if(buf[0]=='1')

{

ioctl(fd_led,1,0);

}

else

{

ioctl(fd_led,0,0);

}

 

if(buf[1]=='1')

{

ioctl(fd_led,1,1);

}

else

{

ioctl(fd_led,0,1);

}

 

if(buf[2]=='1')

{

ioctl(fd_led,1,2);

}

else

{

ioctl(fd_led,0,2);

}

 

if(buf[3]=='1')

{

ioctl(fd_led,1,3);

}

else

{

ioctl(fd_led,0,3);

}

}

}

 

close(fd_key);

close(fd_led);

 

return 0;

}


在这个代码中,通过select()函数将程序阻塞,等待按键的状态变化。当按键状态有变化时,读取按键状态,并根据按键状态对LED灯进行控制。当按键按下时,将相应的LED灯点亮;松开按键时,将相应的LED灯熄灭。


关键字:S3C6410  开发板  按键驱动 引用地址:S3C6410开发板按键驱动代码分析及测试代码分析

上一篇:S3C6410开发板adc驱动代码分析及测试代码分析
下一篇:基于RS-485总线的智能家居网络系统

推荐阅读最新更新时间:2024-10-31 10:08

OK6410A 开发板 (六) 2 OK6410A linux-5.11 移植
$ arm-linux-gnueabi-gcc --version arm-linux-gnueabi-gcc (Linaro GCC 7.4-2019.02) 7.4.1 20181213 Copyright (C) 2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. arch/arm/configs/s3
[单片机]
linux-2.6.32在mini2440开发板上移植(17)之按键驱动程序移植
编者:按键驱动程序涉及到linux中断程序的编写。 1、按键原理图。 2、驱动程序的编写移植。 在/linux-2.6.32.2/drivers/char/目录下创建一个新的驱动程序文件mini2440_buttons.c,内容及详细注释如下: #include linux/module.h #include linux/kernel.h #include linux/fs.h #include linux/init.h #include linux/delay.h #include linux/poll.h #include linux/irq.h #include asm/irq.h #incl
[单片机]
linux-2.6.32在mini2440<font color='red'>开发板</font>上移植(17)之<font color='red'>按键</font><font color='red'>驱动</font>程序移植
2440开发板启动代码学习
2440init.s是启动代码的主文件,包括3个头文件,option.inc,memcfg.inc,2440addr.inc。 option.inc定义了3个堆栈起始地址,总线宽度,时钟相关参数的定义等。其中这个总线宽度将作为一个IF判断量,决定了各Bank的位宽设置,参见memcfg.inc文件及datasheet。 memcfg.inc是存储器Bank的配置文件,定义了存储器相关寄存器位的值。各bank的位宽的设置定义也是在这个文件中,注意bank0的位宽没有相关的定义,因为它的位宽取决于OM 引脚,即启动方式。 2440addr.inc定义了相关寄存器地址,包括存储器控制寄存器,时钟电源管理寄存
[单片机]
基于STM32的32位单片机开发板设计与实现
  RISC(reduced instruction set computer,精简指令集计算机)是一种执行较少类型计算机指令的微处理器,起源于80 年代的MIPS主机(即RISC 机),RISC机中采用的微处理器统称RISC处理器。这样一来,它能够以更快的速度执行操作(每秒执行更多百万条指令,即MIPS)。因为计算机执行每个指令类型都需要额外的晶体管和电路元件,计算机指令集越大就会使微处理器更复杂,执行操作也会更慢。STM32F100VBT6采用ARM Cortex-M3 32位RISC内核,工作频率24MHz,集成了高速嵌入式存储器(闪存高达128kB、SRAM高达8kB)以及各种增强外设和连接到两条APB总线的I/O。所有器
[单片机]
基于STM32的32位单片机<font color='red'>开发板</font>设计与实现
基于51单片机开发板8*8LED矩阵的贪吃蛇程序
引语:过年的时候闲着无聊,恰好也刚学了点51开发板的东西,所以就想写一个贪吃蛇的小程序。在网上总结了一些关于贪吃蛇的算法,于是就有了以下的程序。 先来说以一下算法的原理:将8*8LED看作是直角坐标系,将左上角定为坐标原点。定义两个数组 snake_x ,snake_y 用来存储蛇的x,y轴坐标。因此,我们的核心算法就是小蛇惯性保持自己的运动姿态,产生食物,以及小蛇吃到食物后长度增加。 再来看一下我的开发板,我的开发板是德飞莱51开发板,这里不再讲解其8*8LED的工作原理,直接上代码 void Hc595SendByte() { unsigned char a; SRCLK=0; RCLK=0; for(a=
[单片机]
TQ2440开发板挂载U盘出现乱码
解决方法:配置内核 make menuconfig File Systems --- DOS/FAT/NT Filesystems --- (utf8) Default iocharset for FAT 改为 (cp936) Default iocharset for FAT 重新编译内核,烧写到开发板,启动开发板,插入u盘,挂载u盘,查看u盘内容,没有乱码了:
[单片机]
TQ2440<font color='red'>开发板</font>挂载U盘出现乱码
2410开发板上实现DHCP自动获取IP地址
前提:系统已经实现DNS(即使用ping 通)。 1. 在内核中添加以下选项: Networking --- Networking support Networking options --- Packet socket //添加.配置CONFIG_PACKET IP: DHCP support //添加 Network packet filtering (replaces ipchains) --- //添加,后面子选项可不选,配置CONFIG_NETFILTER 说明:若没选 Packet socket, Network packet f
[单片机]
OK6410A 开发板 (八) 116 linux-5.11 OK6410A 用户空间虚拟内存布局
不同架构的 用户空间虚拟内存布局 不同, 以 arm 为例 用户空间实际从0x10 000开始,从低到高有代码段,数据段,堆,栈 用户空间范围为1000 -beff ffff vdso stack sigpage heap-mmap-ld libc heap-brk 数据段 代码段 vdso 为了提供系统调用 ,x86提供了3类指令int/sysenter/syscall 其中 sysenter和syscall相对于int来说,快一点 而不管怎么快,都要从用户态切换到内核态 新机制出现了,vsyscall vsyscall主要是 1.对特定的系统调用使用函数调用代替,即对一些系统调用进行加速 2.vsy
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved