GPIO读操作与按键轮询实现

  • GPIO读操作
    • 硬件
      • 查找对应IO口
      • 寄存器配置
    • 软件
      • 需要的函数
      • 注册设备
      • 代码及分析
    • 实验效果
  • 按键轮询实现
    • 原理分析
    • 硬件
    • 软件
      • 用到的函数
      • 先前准备工作
      • 代码及分析
    • 实验效果
    • 总结

GPIO读操作

前面我们使用GPIO来控制IO口,点亮了LED灯,当然,IO口是可以有多种配置的,输入输出是最基本的两种,今天我们就来尝试一下GPIO的输入操作,我们使用4412开发板上的3、4号拨码开关来实现

硬件

查找对应IO口


可以看出3、4号分别为AP_SLEEP、XEINT6
经过查阅原理图、手册我们可以找到以下的对应关系

  • AP_SLEEP->GPC0_3->EXYNOS4_GPC0(3)
  • XEINT6->GPX0_6->EXYNOS4_GPX0(6)

按键上就有下拉电阻,所以向内为低电平,向外为高电平

寄存器配置

1、设置为输入状态
2、读DAT寄存器
3、既不上拉也不下拉
以下为配置、数据、上下拉寄存器


软件

需要的函数

  • gpio_request申请GPIO
  • s3c_gpio_cfgpin初始化GPIO S3C_GPIO_INPUT
  • gpio_get_value读值
  • s3c_gpio_setpull设置上下拉S3C_GPIO_PULL_NONE
  • gpio_free释放GPIO

注册设备

注册设备这个是很重要的一个点,但是它也是很简单的,我们需要修改iTop4412的平台文件、字符驱动的Kconfig

vim arch/arm/mach-exynos/mach-itop4412.c

添加已下两处

#ifdef CONFIG_GPIO_READ_CTL
struct platform_device s3c_device_gpio_read_ctl = {.name   = "gpio_read_ctl",.id             = -1,
};
#endif
#ifdef CONFIG_GPIO_READ_CTL&s3c_device_gpio_read_ctl,
#endif
vim drivers/char/Kconfig

添加

config GPIO_READ_CTLbool "Enable GPIO_READ config"default yhelpEnable GPIO_READ config

然后make menuconfig
接着make zImage
最后将生成的zImage烧录到开发板

代码及分析

驱动代码

#include <linux/init.h>
#include <linux/module.h>
/*driver register*/
#include <linux/platform_device.h>/*注册杂项设备头文件*/
#include <linux/miscdevice.h>
/*注册设备节点的文件结构体*/
#include <linux/fs.h>/*Linux中申请GPIO的头文件*/
#include <linux/gpio.h>
/*三星平台的GPIO配置函数头文件*/
/*GPIO配置参数宏定义头文件*/
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>
/*三星平台4412平台,GPIO宏定义头文件*/
#include <mach/gpio-exynos4.h>#define DRIVER_NAME "gpio_read_ctl"
#define DEVICE_NAME "gpio_read_ctl_dev"//设备名
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("GYY");static int gpio_read_open(struct inode * pinode , struct file * pfile )
{printk(KERN_EMERG "gpio_read OPEN !!\n");return 0;
}static int gpio_read_release(struct inode * pinode, struct file * pfile)
{printk(KERN_EMERG "gpio_read RELEASE !!\n");return 0;
}
/*应用中通过ioctl来获取管脚电平*/
static long gpio_read_ioctl(struct file * pfile, unsigned int cmd, unsigned long arg)
{int ret;printk("cmd is %d ,arg is %d\n",cmd,arg);//参数cmd可以是0或1if(cmd > 1){printk(KERN_EMERG "cmd is 0 or 1 \n");return 0;}if(arg > 1){printk(KERN_EMERG "arg is only 1 \n");return 0;}/*cmd为0返回AP_SLEEP->GPC0_3->EXYNOS4_GPC0(3),SWITCH3*/if(cmd==0){ret = gpio_get_value(EXYNOS4_GPC0(3));}/*cmd为0返回XEINT6->GPX0_6->EXYNOS4_GPX0(6),SWITCH4*/else if(cmd==1){ret = gpio_get_value(EXYNOS4_GPX0(6));}return ret;
}static struct file_operations gpio_read_ops = {.owner = THIS_MODULE,.open = gpio_read_open,.release = gpio_read_release,.unlocked_ioctl = gpio_read_ioctl,};static struct miscdevice gpio_read_dev = {.minor = MISC_DYNAMIC_MINOR,//自动分配设备号.name = DEVICE_NAME,//设备名.fops = &gpio_read_ops,
};static int gpio_read_probe (struct platform_device *pdv){int ret;printk(KERN_EMERG "\tinitialized\n");/*申请GPIO*/ret = gpio_request(EXYNOS4_GPC0(3),"SWITCH 3");if(ret < 0){printk(KERN_EMERG "gpio_request EXYNOS4_GPC0(3) failed\n");return ret;}else{/*设置为输入*/s3c_gpio_cfgpin(EXYNOS4_GPC0(3),S3C_GPIO_INPUT);/*不上拉不下拉*/s3c_gpio_setpull(EXYNOS4_GPC0(3),S3C_GPIO_PULL_NONE);}/*申请GPIO*/ret = gpio_request(EXYNOS4_GPX0(6),"SWITCH 4");if(ret < 0){printk(KERN_EMERG "gpio_request EXYNOS4_GPX0(6) failed\n");return ret;}else{/*设置为输入*/s3c_gpio_cfgpin(EXYNOS4_GPX0(6),S3C_GPIO_INPUT);/*不上拉不下拉*/s3c_gpio_setpull(EXYNOS4_GPX0(6),S3C_GPIO_PULL_NONE);}  /*生成设备节点*/misc_register(&gpio_read_dev);return 0;
}static int gpio_read_remove (struct platform_device *pdv){printk(KERN_EMERG "\tremove\n");misc_deregister(&gpio_read_dev);return 0;
}static void gpio_read_shutdown (struct platform_device *pdv){}static int gpio_read_suspend (struct platform_device *pdv,pm_message_t state){return 0;
}static int gpio_read_resume (struct platform_device *pdv){return 0;
}struct platform_driver gpio_read_driver = {.probe = gpio_read_probe,.remove = gpio_read_remove,.shutdown = gpio_read_shutdown,.suspend = gpio_read_suspend,.resume = gpio_read_resume,.driver = {.name = DRIVER_NAME,.owner = THIS_MODULE,}
};static int gpio_read_init(void)
{int DriverState;printk(KERN_EMERG "GPIO_READ enter!\n");DriverState=platform_driver_register(&gpio_read_driver);printk(KERN_EMERG "\t%d\n",DriverState);return 0;
}static void gpio_read_exit(void)
{printk(KERN_EMERG "GPIO_READ exit!\n");platform_driver_unregister(&gpio_read_driver);
}module_init(gpio_read_init);
module_exit(gpio_read_exit);

驱动代码分析

这个驱动程序和杂项驱动点灯的代码没有太大的变化,在probe函数中我们完成了对GPIO的申请与初始化,我们首先调用 gpio_request() 来申请GPIO,接下来通过调用 s3c_gpio_cfgpin() 来初始化IO为输入模式,最后调用 **s3c_gpio_setpull()**设置IO口既不上拉也不下拉
在ioctl函数中我们完成了对IO口的读操作,当应用程序调用ioctl时将会读出两个开关的电平

应用程序代码

#include <stdio.h>#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>int main(int argc,char **argv)
{int fd,cmd=0;char *read_node = "/dev/gpio_read_ctl_dev";char *cmd0 = "0";char *cmd1 = "1";printf("argv[1] is %s\n",argv[1]);if(strcmp(argv[1],cmd0)==0){cmd=0;}else if(strcmp(argv[1],cmd1)==0){cmd=1;}if((fd = open(read_node,O_RDWR|O_NDELAY))<0){printf("APP open %s failed\n",read_node);}else{printf("APP open %s success\n",read_node);printf("%d io value is %d\n",cmd,ioctl(fd,cmd,0));}close(fd);
}

应用程序代码分析
这个应用程序所做的事情很简单就是打开设备节点文件,然后根据命令读出对应开关的电平并打印

实验效果

安装模块

查看设备


可以看到生成了gpio_read_ctl_dev设备节点
执行应用程序

可以看到我们改变拨码开关的状态读出的电平发生了改变,符合我们的要求

按键轮询实现

原理分析


原理图如上图所示
不按下为高电平,按下为低电平
通过GPIO的输入电平来检测按键的变化

硬件

通过查找原理图和datasheet找到IO口的如下对应关系

  • Home->UART_RING->GPX1_1->EXYNOS4_GPX1(1)
  • back->SIM_DET->GPX1_2->EXYNOS4_GPX1(2)
  • sleep->GYRO_INT->GPX3_3->EXYNOS4_GPX3(3)
  • Vol±>KP_ROW1->GPX2_1->EXYNOS4_GPX2(1)
  • Vol–>KP_ROW0->GPX2_0->EXYNOS4_GPX2(0)

软件

用到的函数

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *)

file_operations 结构体中的read函数,对应用户空间的read函数

unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)

将数据从内核空间拷贝到用户空间
参数1:用户空间目标地址
参数2:内核空间地址
参数3:要拷贝数据的数量

先前准备工作

当前内核这些IO口已被驱动占用,我们需要取消编译那个驱动
make menuconfig->device drivers->input device support->Keyboards ->去掉GPIO_button
接着在平台文件、Kconfig中注册设备添加pollkey,这个就和上面注册gpio_read_ctl是差不多的就不再赘述了
最后重新编译和烧录内核

代码及分析

驱动代码

#include <linux/init.h>
#include <linux/module.h>#include <linux/kernel.h>
#include <linux/fs.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
//#include <mach/gpio-bank.h>
#include <mach/regs-gpio.h>
#include <asm/io.h>
#include <linux/regulator/consumer.h>
//#include "gps.h"
#include <linux/delay.h>
/*copy_to_user头文件*/
#include <asm/uaccess.h>#define DPRINTK(x...) printk("POLLKEY_CTL DEBUG:" x)/*驱动名*/
#define DRIVER_NAME "pollkey_ctl"/*按键IO数组*/
static int key_gpios[] = {EXYNOS4_GPX1(1),//HomeEXYNOS4_GPX1(2),//BackEXYNOS4_GPX3(3),//SleepEXYNOS4_GPX2(1),//Vol+EXYNOS4_GPX2(0)//Vol-
};int pollkey_open(struct inode *inode,struct file *filp)
{DPRINTK("Device Opened Success!\n");return nonseekable_open(inode,filp);
}int pollkey_release(struct inode *inode,struct file *filp)
{DPRINTK("Device Closed Success!\n");return 0;
}
/*关键驱动:按键扫描(read)函数*/
static ssize_t pollkey_read (struct file *pfile, char __user *buff, size_t size, loff_t * ppos)
{unsigned char key_value[5];//键值数组存放读取到的电平int i;if(size != sizeof(key_value)){return -1;}/*循环读五个IO口*/for(i=0;i<5;i++){key_value[i]=gpio_get_value(key_gpios[i]);}//将数据传递给用户空间copy_to_user(buff,key_value,sizeof(key_value));return 0;
}int pollkey_pm(bool enable)
{int ret = 0;printk("debug: pollkey PM return %d\r\n" , ret);return ret;
};static struct file_operations pollkey_ops = {.owner  = THIS_MODULE,.open    = pollkey_open,.release= pollkey_release,.read = pollkey_read,
};static struct miscdevice pollkey_dev = {.minor   = MISC_DYNAMIC_MINOR,.fops = &pollkey_ops,.name   = "pollkey_ctl_dev",
};static int pollkey_probe(struct platform_device *pdev)
{int ret, i;char *banner = "pollkey Initialize\n";printk(banner);for(i=0;i<5;i++){/*申请GPIO*/ret = gpio_request(key_gpios[i],"key_gpio");/*设置为输入*/s3c_gpio_cfgpin(key_gpios[i],S3C_GPIO_INPUT);/*不上拉不下拉*/s3c_gpio_setpull(key_gpios[i],S3C_GPIO_PULL_NONE);}ret = misc_register(&pollkey_dev);return 0;}static int pollkey_remove (struct platform_device *pdev)
{misc_deregister(&pollkey_dev); return 0;
}static int pollkey_suspend (struct platform_device *pdev, pm_message_t state)
{DPRINTK("pollkey suspend:power off!\n");return 0;
}static int pollkey_resume (struct platform_device *pdev)
{DPRINTK("pollkey resume:power on!\n");return 0;
}static struct platform_driver pollkey_driver = {.probe = pollkey_probe,.remove = pollkey_remove,.suspend = pollkey_suspend,.resume = pollkey_resume,.driver = {.name = DRIVER_NAME,.owner = THIS_MODULE,},
};static void __exit pollkey_exit(void)
{platform_driver_unregister(&pollkey_driver);
}static int __init pollkey_init(void)
{return platform_driver_register(&pollkey_driver);
}module_init(pollkey_init);
module_exit(pollkey_exit);MODULE_LICENSE("Dual BSD/GPL");

驱动代码分析
我们使用了一个数组来存放五个IO口,在probe函数中我们完成了对IO口的初始化,在pollkey_read函数中是最为关键的工作,我们用一个数组来存放五个IO口的状态,读取完成后并将该数据拷贝到用户空间

应用程序代码

#include <stdio.h>#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>int main()
{int fd;char *read_key = "/dev/pollkey_ctl";unsigned char buffer[5];if((fd = open(read_key,O_RDWR|O_NDELAY))<0){printf("APP open %s failed\n",read_key);return -1;}printf("APP open %s success\n",read_key);while(1){/*从文件中读取数据到buffer数组*/read(fd,buffer,sizeof(buffer));if(!buffer[0]||!buffer[1]||!buffer[2]||!buffer[3]||!buffer[4]){if(!buffer[0])printf("KEY:HOME\n");else if(!buffer[1])printf("KEY:BACK\n");else if(!buffer[2])printf("KEY:SLEEP\n");else if(!buffer[3])printf("KEY:VOL+\n");else if(!buffer[4])printf("KEY:VOL-\n");}}close(fd);
}

应用程序代码分析
在打开设备节点文件后,调用read函数读取数据到buffer数组,然后我们检测buffer数组中是否有0(按键按下为0,即有没有按键被按下),如果有按键按下,我们则进一步判断是哪个按键被按下并打印信息

实验效果

安装模块


生成了设备节点
执行应用程序

所以当按键按下时打印出了对应的按键名

总结

使用查询的方式处理按键效率很低,占用CPU过高
应使用中断、异步通信、休眠等方式

4412开发板学习之Linux驱动开发(八):GPIO读操作与按键轮询实现相关推荐

  1. 4412开发板学习之Linux驱动开发(五):4412MMU及GPIO操作(点灯)

    4412MMU及GPIO操作(点灯) 物理地址与虚拟地址 与传统MCU的对比 4412中的物理地址 MMU内存管理单元 存储器分类 4412中的存储器映射 物理地址和虚拟地址 其他的地址概念 GPIO ...

  2. linux轮询脚本,linux驱动的等待队列(阻塞操作)和轮询(poll),缓冲区笔记

    觉得还是贴代码最直接,以后要用的时候也方便参考. 先是相应驱动的详细代码: /* linux/drivers/char/sep4020_char/sep4020_fifo.c * * Copyrigh ...

  3. i.MX283开发板第一个Linux驱动-LED驱动

    字符设备驱动开发 字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的.比如我们最常见的点灯.按键.IIC.SPI,LCD ...

  4. 嵌入式linux驱动开发实战教程,嵌入式Linux驱动开发实战视频教程

    嵌入式Linux驱动开发实战教程(内核驱动.看门狗技术.触摸屏.视频采集系统) 适合人群:高级 课时数量:109课时 用到技术:嵌入式 Linux 涉及项目:驱动开发.看门狗技术.触摸屏.视频采集 咨 ...

  5. ITOP4412开发板学习前的准备--开发环境搭建

    前些天买到了迅为公司的ITOP4412精英版的开发板,到货大概也有三四天了,硬件零基础-需要好好研究一下.以后就把一些学习中遇到的问题以及学习心得等记录下来,提醒自己注意和规避一些问题,也希望可以帮助 ...

  6. AM335 嵌入式 linux,am335x开发板建立嵌入式 Linux NFS 开发环境

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 am335x开发板嵌入式开发板系统 NFS客户端的配置 首先运行在嵌入式开发板系统的 Linux 内核支持 NFS 客 户端, 运行 #make menu ...

  7. 【正点原子MP157连载】第二十三章 Linux设备树-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  8. 【正点原子Linux连载】第四十三章 Linux设备树 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  9. Linux驱动开发工程师需要掌握哪些技能?

    一.前言 Linux驱动开发是一项高度技术性的工作,需要深厚的编程技能和对计算机硬件的深入理解.随着物联网.人工智能等领域的快速发展,Linux驱动开发工程师的需求日益增加.在这篇文章中,我将为您介绍 ...

最新文章

  1. Microsoft Expression Blend 2 密钥,key
  2. Charles 4.2.1 HTTPS抓包
  3. 提示找不到include/common.h 提示No package 'minigui' found
  4. RequireJS 主入口加载模块经常会加载失败的问题
  5. tp5 模型中配置数据库连接信息
  6. nightwatch testing 注意事项
  7. 用鼠标在窗口中画方形的程序------基于OpenCV+VS
  8. php 数组的深度,有没有办法找出PHP数组的“深度”?
  9. ppt倒计时3分钟_老板发来200页PPT文件,让我翻译成英文,3分钟教你搞定
  10. 【eoeAndroid社区索引】android开发混淆
  11. 如何将两个mp3文件合成一个?
  12. 为什么-关于因果关系的新科学 | 导言
  13. 将HTML 转为pdf
  14. SAP中采购收货控制中的配置问题分析
  15. 计算机工程制图箭头怎么画,AutoCAD制图时怎么画剖视的箭头 AutoCAD箭头画法教程...
  16. 【概率论】设随机变量X~N(0, 1), 则P(X>1)的值为
  17. 苹果自带输入法怎么换行_Iphone手机原生输入法的5个技巧,学会了,才知道这么牛...
  18. delphi7的程序在英文系统下显示中文乱码
  19. 2.Matlab画好图后,如何插入到word里面去
  20. PyCharm2020介绍

热门文章

  1. ICLR 2023(投稿)|自然语言处理相关论文分类整理
  2. 企业微信接入群聊机器人详细步骤
  3. java转义字符包括元字符_语言的转义字符及正则表达式的转义字符的表示
  4. vue实现多页面应用开发,包含项目之间跳转
  5. nginx发布vue多页面程序
  6. Poincaré圆盘模型:一个神奇的双曲世界
  7. 计算机打印要先安装驱动吗,打印机驱动怎么安装,教您打印机驱动怎么安装
  8. 集合Set边遍历边删除
  9. 致敬大师 | 你敲出的代码,幸福了我的整个童年
  10. shell脚本系列:5、shell参数