嵌入式开发之hi3519---GPIO 按键驱动
摸索了一个星期,终于把海思HI3515开发板的按键中断程序搞出来了,hi3515的核心芯片与网上例子较多的s3c之类的有一些区别,以至于浪费了好些时间去琢磨。管脚配置方式不一样,中断的使用情况也不一样。而比较麻烦的是网上关于海思的资料太少了。对于水平不太高的人,老自己摸索还是会走不少弯路。现在就把本人写的能在开饭板测试运行通过的程序贴出来,但愿这个不会违反到保密协议的内容,代码可都是我自己写的哈。希望能给后来者带来些帮助,也希望大家多提意见,一起进步·,^_^
第一步,编写按键驱动程序,button.c代码如下:
/*所有模块都需要的头文件*/
#include<linux/module.h>
/*声明printk()这个内核态的函数*/
#include<linux/kernel.h>
/*文件系统有关的,结构体file_operations也在fs头文件定义*/
#include<linux/fs.h>
/*init和exit相关宏*/
#include<linux/init.h>
#include<linux/delay.h>
#include<linux/poll.h>
/*linux中断定义*/
#include<linux/irq.h>
/**/
#include<asm/irq.h>
/*包含与中断相关的大部分宏及结构体的定义,request_irq()等*/
#include<linux/interrupt.h>
/*linux中的用户态内存交互函数,copy_from_user(),copy_to_user()等*/
#include<asm/uaccess.h>
//#include<mach/regs-gpio.h>
//#include<mach/hardware.h>
#include<linux/platform_device.h>
#include<linux/cdev.h>
/*misc混合设备注册与注销*/
#include<linux/miscdevice.h>
#include <asm/io.h>
#include <asm/system.h>
#define BUTTON_READ 0x01
#define DEVICE_NAME "BUTTON_irq"
#define REG_WRITE(addr,value) ((*(volatile unsigned int *)(addr)) = (value))
#define REG_READ(Addr) (*(volatile unsigned int *)(Addr))
static unsigned int gpio3_virtual_addr = 0;
static unsigned int reg_virtual_addr = 0;
/*数组中是否有数据标志,0表示无数据可读,1表示有数字可读*/
static volatile char key;
/*定义和初始化一个等待队列头*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
/*定义一个整形变量,判断按键是否按下*/
static volatile int ev_press = 0;
/*
*定义结构体类型,由它把按钮中断的信息综合起来
*/
struct button_irq_desc {
int irq;/*中断号*/
int pin;/*中断标志寄存器,有中断产生时为1,无中断时为0*/
int number;/*编号*/
char *name;/*名称*/
};
static struct button_irq_desc button_irqs[]={
{8,2,1,"KEY1"},
};
static void hi3515_button_pin_cfg(void)
{
/*配置作为普通输入*/
REG_WRITE(reg_virtual_addr + 0x08,0x1);/*reg2管脚复用配置gpio3_0,按键1*/
REG_WRITE(reg_virtual_addr + 0x0c,0x1);/*reg3管脚复用配置gpio3_1,按键1*/
REG_WRITE(reg_virtual_addr + 0x10,0x1);/*reg4管脚复用配置gpio3_2,按键1*/
REG_WRITE(reg_virtual_addr + 0x14,0x1);/*reg5管脚复用配置gpio3_3,按键2*/
/*管脚中断配置*/
REG_WRITE(gpio3_virtual_addr + 0x0400,0x3);/*dir设置管脚为0-1:输出,2-3:输入*/
REG_WRITE(gpio3_virtual_addr + 0x0404,0xc);/*is边沿触发中断*/
REG_WRITE(gpio3_virtual_addr + 0x040c,0x0);/*iev低电平触发*/
REG_WRITE(gpio3_virtual_addr + 0x041c,0xff);/*ic清除中断*/
REG_WRITE(gpio3_virtual_addr + 0x0410,0x04);/*ie启用中断*/
}
/*
*gpio地址映射
*/
static int virtual_addr_map(void)
{
reg_virtual_addr = (unsigned int)ioremap_nocache(0x200f0000,0x4000);
if(!reg_virtual_addr)
{
printk("0x200f0000 ioremap addr failed !\n");
return -1;
}
gpio3_virtual_addr = (unsigned int)ioremap_nocache(0x20180000,0x4000);
if(!gpio3_virtual_addr)
{
printk("0x20180000 ioremap addr failed !\n");
return -1;
}
}
/*取消地址映射*/
static void virtual_addr_unmap(void)
{
iounmap((void*)gpio3_virtual_addr);
iounmap((void*)reg_virtual_addr);
}
/*
*read调用的具体函数,由它读取键盘输入的结果,
*实质上就是读取key_values数组的值
*完成键盘输入设备的核心功能,根据标志位ev_press判断是否可读
*如果可读,则读取数据到用户buffer中,如果不可读,
*则进程进入等待队列等待,直到数组可读为止
*等待队列机制,所中断管理中常用的机制。
*/
static int button_irq_read(struct file *filp,
char __user *buff,
size_t count,loff_t *offp)
{
unsigned long err;
#if 1
if(!ev_press) /*ev_press=0,则表示没有数据可读*/
{
if(filp->f_flags & O_NONBLOCK)
return -EAGAIN;
else /*无数据可读时,进程休眠,放进button_waitq等待队列*/
wait_event_interruptible(button_waitq,ev_press);
/*
*wait_event_interruptible()函数将进程置为可中断的挂起状态
*反复检查ev_press=1是否成立,如果不成立,则继续休眠。
*条件满足后,即把本程序置为运行态,
*/
}
/*ev_press=1之后,进程退出等待队列。从此处开始运行*/
ev_press = 0;/*置0标志位,表明本次中断已经处理*/
err = copy_to_user(buff,&key,sizeof(key));
/*把按键值传会用户空间*/
#endif
return 0;
}
static irqreturn_t irq_interrupt(int irq,void *dev_id)
{
#if 1
/*对传入的中断资源进行处理,获得中断控制寄存器的值(即是否有数据)
取反后赋值给down,为0时说明有数据,
注意按下依次按钮有两次中断,
对数组可读标志位进行设置,ev_press=1表示数组已经可以读了*/
//struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
int down;
down = 0x0c & REG_READ(gpio3_virtual_addr + 0x0414);
REG_WRITE(gpio3_virtual_addr + 0x041c,0xff);/*ic清除按键中断*/
mdelay(5);/**/
if(down != 0x0c )
{
key = (char)down;
ev_press = 1; /*置1标志位,唤醒等待队列进程,在read函数中使用*/
/*
*唤醒休眠的进程,用户空间程序使用调用read函数时,
*如果没有产生中断,进程就会进入休眠状态,一直等待,直到产生中断
*中断产生后,通过wake_up_interruptible()函数唤醒休眠进程
*/
wake_up_interruptible(&button_waitq);
}
#endif
REG_WRITE(gpio3_virtual_addr + 0x0410,0x0c);/*ie启用按键中断*/
return IRQ_HANDLED; //IRQ_HANDLED=1
}
/*
*poll调用的具体函数,poll实质上是select的调用函数
*如果有按键数据,则select会立刻返回
*如果没有按键数据,则等待
*实质上这是键盘等待输入的机制
*poll_wait()会监测进程队列button_waitq里的进程
*例如button_irq_read所在的进程的标志ev_press置为1了
*那么就不再等待,这实质上就所select函数的运行机制
*/
static unsigned int button_irq_poll(struct file *file, struct poll_table_struct *wait)
{
#if 1
/*
*poll调用的具体函数,poll实质上是select的调用函数
*如果有按键数据,则select会立刻返回
*如果没有按键数据,则等待
*实质上这是键盘等待输入的机制。
*select调用是用户程序里面使用的。
*/
unsigned int mask = 0;
poll_wait(file,&button_waitq,wait);
/*poll_wait会检测button_waitq里的进程*/
if(ev_press)
mask |=POLLIN | POLLRDNORM;
return mask;
#endif
}
static int button_irq_open(struct inode *inode,struct file *file)
{
#if 1
int err;/*中断注册返回值*/
virtual_addr_map(); /*地址映射*/
hi3515_button_pin_cfg();/*管脚配置,要先进行地址映射*/
/*注册中断*/
err = request_irq(8,irq_interrupt,IRQF_SHARED,\
"KEY",(void *)&button_irqs);
if(err)/*如果注册中断失败,则释放已经成功注册的中断*/
{
return -EBUSY;
}
ev_press = 1;
#endif
return 0;
}
static int button_irq_close(struct inode *inode,struct file *file)
{
free_irq(8,(void *)&button_irqs);
virtual_addr_unmap();/*取消地址映射*/
return 0;
}
int button_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned int __user *argp = (unsigned int __user *)arg;
int value;
value = *(unsigned int *)arg;
switch (cmd)
{
case 1:
if(value == 0) /*led1亮*/
REG_WRITE(gpio3_virtual_addr +0x4,0);
else if(value == 1) /*led1灭*/
REG_WRITE(gpio3_virtual_addr +0x4,1);
break;
case 2:
if(value == 0) /*led2亮*/
REG_WRITE(gpio3_virtual_addr +0x8,0);
else if(value == 1) /*led2灭*/
REG_WRITE(gpio3_virtual_addr +0x8,2);
break;
default:
return -1;
}
return 0;
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = button_irq_open,
.release = button_irq_close,
.ioctl = button_ioctl,
.read = button_irq_read,
.poll = button_irq_poll,/*用户程序使用select调用的时候才会用到poll*/
};
/*
*misc混合设备注册和注销
*/
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,/*次设备号*/
.name = DEVICE_NAME,/*设备名*/
.fops = &dev_fops,/*设备文件操作结构体*/
};
static int __init button_init(void)
{
int ret;
ret = misc_register(&misc);
if(0 != ret)
{
printk("register device failed! !\n");
return -1;
}
printk("register device success !\n");
return 0;
}
static void __exit button_exit(void)
{
misc_deregister(&misc);
printk("unregister device success !\n");
}
module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Dong");
第二步。,编写测试程序,test_button.c代码如下
/*
*按键中断测试程序
*按键被按下时,产生中断
*打印按下信息,切换led显示状态
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<linux/delay.h>
int main(int argc , char* argv[])
{
int fd = -1;
unsigned int led1;
unsigned int led2; ;
char key;
fd = open("/dev/BUTTON_irq", 0);
if (fd<0)
{
printf("Open BUTTON_irq dev error!\n");
return -1;
}
for(;;)
{
int ret;
ret = read(fd,&key,sizeof(key));
if(ret< 0)
{
perror("read button:");
return -1;
}
if(key == 4)/*按键1被按下*/
{
printf("K1 is press!\n");
led1 = (~led1)&0x1;
ioctl(fd, 0x01, &led1); /*切换led1的状态*/
}
if(key == 8)/*按键2被按下*/
{
printf("K2 is press!\n");
led2 = (~led2)&0x1;
ioctl(fd, 0x02, &led2); /*切换led2的状态*/
}
//printf("\n");
}
close(fd);
return 0;
}
第三步,makefile文件,代码如下:
LINUXROOT = /opt/Hi3515_SDK_V1.0.5.1/source/os/linux-2.6.24
#这是放内核的路径
CC = arm-hismall-linux-gcc
obj-m := button.o
default:
$(CC) -g -Wall -o test_button test_button.c
@make -C $(LINUXROOT) M=$(PWD) modules
rm -rf *.o *.mod.c *.symvers
clean:
@make -C $(LINUXROOT) M=$(PWD) clean
http://blog.csdn.net/xiufu004/article/details/7198338 按键驱动
http://blog.csdn.net/mao0514
嵌入式开发之hi3519---GPIO 按键驱动的更多相关文章
- AM335x(TQ335x)学习笔记——GPIO按键驱动移植
还是按照S5PV210的学习顺序来,我们首先解决按键问题.TQ335x有六个用户按键,分别是上.下.左.右.Enter和ESC.开始我想到的是跟学习S5PV210时一样,编写输入子系统驱动解决按键问题 ...
- 嵌入式开发之hi3519---GPIO 驱动
在一个嵌入式系统中使用最多的莫过于 通用输入输出 GPIO口.看到论坛中经常有朋友问海思为什么没有提供GPIO驱动.其实不然. 在海思SDK xxx/osdrv/tools/board_tools/ ...
- 嵌入式开发之zynq---Zynq PS侧I2C驱动架构
http://blog.chinaunix.net/uid-24148050-id-120532.html http://bbs.csdn.net/topics/390538368?page=1 ht ...
- 嵌入式开发之zynq驱动—— zynq ps pl ddr 内存地址空间映射
http://www.wiki.xilinx.com/Zynq-7000+AP+SoC+-+32+Bit+DDR+Access+with+ECC+Tech+Tip http://patchwork.o ...
- 嵌入式开发之zynq---Zynq PS侧sd驱动
http://blog.chinaunix.net/uid-29404121-id-4217026.html http://blog.chinaunix.net/uid-29709984-id-430 ...
- 嵌入式开发之zynq---Zynq PS侧DMA驱动
http://xilinx.eetrend.com/blog/10760 http://xilinx.eetrend.com/blog/10787
- 嵌入式开发之NorFlash 和NandFlash
http://blog.csdn.net/tigerjibo/article/details/9322035 [摘要]:作为一个嵌入式工程师,要对NorFlash 和NandFlash要有最起码的认知 ...
- 嵌入式开发之zynq——赛灵思的一款两a9加一fpga芯片的开发板
没办法,回家入职新公司,做通信的,用到这款zynq加ad9163射频架构的开发版,要我做驱动,这可是初次接触zynq,带fpga的集成芯片,心里还是有点惊喜和忧愁,忧愁怎么最快啃下这个硬骨头,好吧上网 ...
- 嵌入式开发之zynqMp ---Zynq UltraScale+ MPSoC 图像编码板zcu102
1.1 xilinx zynqMp 架构 1.1.1 16nm 级别工艺 Zynq UltraScale+ MPSoC架构 Xilinx新一代Zynq针对控制.图像和网络应用推出了差异化的产品系,这 ...
随机推荐
- Codeforces.528D.Fuzzy Search(FFT)
题目链接 \(Descripiton\) 给出文本串S和模式串T和k,S,T为DNA序列(只含\(A,T,G,C\)).对于S中的每个位置\(i\),只要\(s[i-k]\sim s[i+k]\)中有 ...
- 洛谷.3121.审查(AC自动机 链表)
题目链接 //删掉一个单词需要前移一段位置,用链表维护就好了 复杂度O(sum(len)) #include <cstdio> #include <cstring> #defi ...
- C++ 动态存储空间的分配和释放 new与malloc的区别
使用new申请空间: 特点: 1.没有名字,只能通过指针间接访问它们. 2.从堆中申请空间 分类: 变量: 语法:指针变量 = new 类型名; Delete 指针变量 举例:int *p; p=ne ...
- netty如何知道连接已经关闭,socket心跳,双工?异步?
https://stackoverflow.com/questions/10240694/java-socket-api-how-to-tell-if-a-connection-has-been-cl ...
- Android笔记(四):RecyclerView
RecyclerView是ListView的增强版.有了它之后,你就可以抛弃ListView了. recycle,重复利用.在ListView里,我们得自己写重复利用View的代码,而Recycler ...
- 封装PHP验证码方法
/** * 生成验证码 * @param integer $width 图片宽度 * @param integer $height 图片高度 * @param string $type 验证码类型 * ...
- 前端AngularJS后端ASP.NET Web API上传文件
本篇体验使用AngularJS向后端ASP.NET API控制器上传文件. 首先服务端: public class FilesController : ApiController { //usi ...
- Juno Puppet Opertaors Meetup小结
今年五月刚结束的Juno OpenStack Summit是半年一度的Openstack盛会,抛去那些迷花渐欲乱人眼的商业活动,我们来看一看本届summit puppet-openstack社区有哪些 ...
- hive行转列
一.问题 hive如何将 a ,, b , c 转化成为: a a a b b c 二.原始数据 cat row_column.txt a ,, b , c 三.解决方案 3.1 遍历每一列 3.1. ...
- NIO-新的io操作方式
1.BIO带来的挑战 BIO即阻塞IO,不管是磁盘IO,还是网络IO,数据在写入OutputStream或者从InputStream读取时都有可能发生阻塞,一旦有阻塞,当前线程将会被挂起,即线程进入非 ...