在本文中,我们对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< 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开发板adc驱动代码分析及测试代码分析
下一篇:基于RS-485总线的智能家居网络系统
推荐阅读最新更新时间:2024-10-31 10:08
设计资源 培训 开发板 精华推荐
- GI_Dice_DevBoard_V1.2
- 使用 ON Semiconductor 的 NUD4021 的参考设计
- AXM0F343-256-868-1-GEVK:AXM0F343-256-1 评估套件 - 868MHz
- MC33071ADR2G高频脉宽调制的典型应用
- 1.8V DC 至 DC 单路输出手机电源
- AM1G-0512SH30Z 12V 1 瓦 DC/DC 转换器的典型应用
- 使用 RP40-11015SFR DC/DC 转换器并根据 EN55022 A 类(双输出)进行 EMC 滤波的典型应用
- LT1248IS 300W、382V 功率因数控制器的典型应用电路
- STM32_f1mini全功能版本
- LT1226 的典型应用 - 低噪声超高速运算放大器