Linux驱动开发:gpio_set_value()的简明总结
1.gpio_set_value(unsigned gpio, int value)用来设置gpio寄存器的值
2.gpio_direction_output(unsigned gpio, int value)用来设置gpio为输出功能,同时设置gpio输出的值。
一般来说,设置一个GPIO口为输出,先执行一次gpio_direction_output,然后接下来只需执行gpio_set_value就行了。
3.gpio_direction_input(unsigned gpio)用来设置gpio为输入功能
4.gpio_get_value(unsigned gpio)用来获取gpio口的输入的值;
5.在使用gpio口之前,先用gpio_request(unsigned gpio, const char* label)申请gpio口的使用,若申请成功,则说明该gpio口未被使用。
6.在使用完gpio口之后,用gpio_free(unsigned gpio)释放gpio口。
7.如何获取gpio口呢,可以查看内核中对应版型的相关文件,也可以自己进行计算,比如GPIOA1的gpio为1,GPIOB2为34。
8.gpio口的通用函数接口定义在gpiolib.c文件中,声明则在gpio.h中。
.
.
.
linux内核驱动中通用GPIO函数使用
1. gpio_request(unsigned gpio, const char *label):向内核申请指定gpio,所申请的IO口会被内核记录
参数:gpio:申请IO口编号 ,label:申请者的名字,随便。
返回:int值,成功:0;失败:负数
注:在使用gpio口之前,应先用gpio_request()申请gpio口。
若申请成功,则说明该gpio口未被使用。
若申请失败,则说明该gpio口不存在或未被释放。
2. gpio_set_value(unsigned gpio, int value):设置gpio口的值
参数:gpio:要设置的IO口编号 ,value:要设置的值(0或1)
返回:无
3. gpio_get_value(unsigned gpio):获取gpio口的值
参数:gpio:要获取的IO口编号
返回:int值,IO口状态
4. gpio_direction_input(unsigned gpio):设置gpio为输入功能
参数:gpio:要设置的IO口编号
返回:int值
5. gpio_direction_output(unsigned gpio, int value):设置gpio为输出功能,同时设置gpio输出的值
参数:gpio:要设置的IO口编号 ,value:要设置的值(0或1)
返回:int值
注:一般来说,设置一个GPIO口为输出,先执行一次gpio_direction_output,然后接下来只需执行gpio_set_value就行了。
6. gpio_free(unsigned gpio):释放gpio口,释放的IO口会在内核记录消除
注:在使用完gpio口之后,应及时释放gpio口。释放的io,可以再次被申请。
注意:gpio口的通用函数接口定义在 gpiolib.c 文件中,声明则在 gpio.h 中。
int gpio_request(unsigned gpio, const char *label)
gpio则为你要申请的哪一个管脚,label则是为其取一个名字
1、一般gpio_request封装了mem_request(),起保护作用,最后要调用mem_free之类的。主要是告诉内核这地址被占用了。当其它地方调用同一地址的gpio_request就会报告错误,该地址已被申请。在/proc/mem应该会有地址占用表描述。
这种用法的保护作用前提是大家都遵守先申请再访问,有一个地方没遵守这个规则,这功能就失效了。好比进程互斥,必需大家在访问临界资源的时候都得先获取锁一样,其中一个没遵守约定,代码就废了。
2、__gpio_set_value和gpio_set_value的区别
一般带__这种操作的宏和函数是未保护的,对这中__操作的使用最好不用,除非你知道其中的原理。
你说的这种显然就是地址检测保护了。主要是防止错误地址引用。__gpio_set_vallue是没有地址范围检测的,如果引用非法地址,有可能内核down掉
如何用Linux内核里的gpio_request(),gpio_set_value()等函数编写LED驱动
步骤一:打开Linux内核源代码里的Documentation文件夹下的gpio.txt文档
文档里介绍了要用到的头文件和gpio函数介绍,用到的头文件是
#include <linux/gpio.h>
步骤二:打开Linux sourceindight工程搜索gpio.h,打开Linux目录下的那个里面是gpio函数的介绍,还有要包含的头文件,头文件里是gpio引脚号的定义
#include<asm/gpio.h>
找到arch/arm/include/asm/gpio.h,里面需要包含的头文件是
#include<mach/gpio.h>这个头文件和具体的平台有关,这里我们以S5PC100平台为例
linux/arch/arm/mach-s5pc100/include/mach/gpio.h
按键驱动程序设计
/*******************************
*
*混杂设备驱动:miscdevice
*majior=10;
*
* *****************************/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/init.h>
//#include <linux/moduleparam.h>
//#include <linux/slab.h>//kcalloc,kzalloc等内存分配函数
//———ioctl————
#include <linux/ioctl.h>
//———misc_register—-
#include <linux/miscdevice.h>
//———-cdev————–
#include <linux/cdev.h>
//———-delay————-
#include <linux/delay.h>
//———-GPIO—————
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#define MISC_DYNAMIC_MINOR 0
#define DEVICE_NAME "leds"
//静态映射的管脚虚拟地址
static int led_gpios[] = {
S5PV210_MP04(4),
S5PV210_MP04(5),
S5PV210_MP04(6),
S5PV210_MP04(7),
};//4个LED
#define LED_NUM ARRAY_SIZE(led_gpios)
static long fl210_leds_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg) //第二个参数是命令号,第三个参数是附加参数
{
switch(cmd) {
case 0: //命令码:如果写规范格式如:#define LED2_OFF _IOR(‘L’,0,unsigned char)
case 1: //命令码:如果写规范格式如:#define LED2_ON _IOR(‘L’,1,unsigned char) 也可以把命令码放在一个头文件里,应用和驱动都包含它
if (arg > LED_NUM) {
return -EINVAL;
}
gpio_set_value(led_gpios[arg], !cmd);//根据cmd设置LED的暗灭
printk(DEVICE_NAME": %ld %d\n", arg, cmd);
break;
default:
return -EINVAL;
}
return 0;
}
static struct file_operations fl210_led_dev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = fl210_leds_ioctl,
};
//—————-miscdevice——————
static struct miscdevice fl210_led_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &fl210_led_dev_fops,
};
//——————————————–
static int __init fl210_led_dev_init(void) {
int ret;
int i;
//申请gpio,只有在gpio_request后才可以调用gpio_set_value,gpio_get_value等函数
for (i = 0; i < LED_NUM; i++) {
ret = gpio_request(led_gpios[i], "LED");//申请GPIO口
if (ret) {
printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
led_gpios[i], ret);
return ret;
}
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);//设置GPIO口为输出
//也可以用函数gpio_direction_output(unsigned gpio, int value);
gpio_set_value(led_gpios[i], 1);//初始化GPIO口的值
}
ret = misc_register(&fl210_led_dev);//注册混杂设备
printk(DEVICE_NAME"\tinitialized\n");
printk("led num is: %d\n",LED_NUM);
return ret;
}
static void __exit fl210_led_dev_exit(void) {
int i;
for (i = 0; i < LED_NUM; i++) {
gpio_free(led_gpios[i]);//释放GPIO口
}
misc_deregister(&fl210_led_dev);//注销设备
}
module_init(fl210_led_dev_init);
module_exit(fl210_led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("");
按键应用程序设计
#include <stdio.h>
//#include "sys/types.h"
#include <sys/ioctl.h>
#include <stdlib.h>
#include <unistd.h>//read,write等等
//#include "termios.h"
//#include "sys/stat.h"
#include <fcntl.h>
#define LED2_ON 0x1 //命令码:如果写规范格式如:#define LED2_ON _IOR(‘L’,0,unsigned char)
#define LED2_OFF 0x0 //命令码:如果写规范格式如:#define LED2_OFF _IOR(‘L’,1,unsigned char)
main(int argc,char *argv[])
{
int fd;
if ((fd=open("/dev/leds",O_RDWR /*| O_NDELAY | O_NOCTTY*/)) < 0)
{
printf("Open Device failed.\r\n");
exit(1);
}
else
{
printf("Open Device successed.\r\n");
}
if (argc<3)
{
/* code */
printf("Usage: %s <on|off num>\n",argv[0]);
exit(1);
}
if(!strcmp(argv[1],"on"))//命令号为1
{
printf("led1 will on!!\n");
if(ioctl(fd,LED2_ON,atoi(argv[2]))<0)//命令附加参数是atoi(argv[2])
{
printf("ioctl err!!\n");
}
}
if(!strcmp(argv[1],"off"))
{
printf("led1 will off!!\n");
if(ioctl(fd,LED2_OFF,atoi(argv[2]))<0)
{
printf("ioctl err!!\n");
}
}
close(fd);
}
/*应用层里ioctl函数的原型是:
*#man ioctl
*int ioctl(int d, int request, …)
*内核驱动源码里ioctl函数的原型是:
*long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
*可以知道虽然应用层是可变参数…,但是实际上应用层和驱动层的ioctl函数是对应的,
*所以我们知道,虽然应用层是用…,表示ioctl参数,但是我们应该明白
*应用层的ioctl函数的参数最多只能有3个
*/
Linux中gpio接口的使用方法示例
这篇文章主要给大家介绍了关于Linux中gpio接口的使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
前言
Linux内核中gpio是最简单,最常用的资源(和 interrupt ,dma,timer一样)驱动程序,应用程序都能够通过相应的接口使用gpio,gpio使用0~MAX_INT之间的整数标识,不能使用负数,gpio与硬件体系密切相关的,不过linux有一个框架处理gpio,能够使用统一的接口来操作gpio.在讲gpio核心(gpiolib.c)之前先来看看gpio是怎么使用的
使用gpio
使用gpio接口需要包含#include <linux/gpio.h>
,在驱动中使用延时函数mdelay,需要包含#include <linux/delay.h>
文件,Documentation/gpio.txt文件有作详细说明。
判断一个IO是否合法:
1 |
|
设置GPIO的方向,如果是输出同时设置电平:
1 2 3 |
|
获取输入引脚的电平:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
获取一个GPIO并声明标签:
1 2 3 4 5 6 7 |
|
将GPIO映射为IRQ中断:
1 2 3 4 5 |
|
设置GPIO的IRQ中断类型:
1 2 3 4 5 6 7 |
|
GPIO使用总结
一、GPIO重要概念
要想操作GPIO引脚,需要先把所用引脚配置成GPIO功能,这个通过pinctrl子系统来实现。然后可以根据设置的引脚的方向来读取引脚的值和设置输出值。GPIO子系统存在之前,我们驱动需要在代码中配置寄存器来使用GPIO引脚。再BSP工程师实现好GPIO子系统后,我们就可以在设备树中指定GPIO引脚,在驱动中使用GPIO子系统的标准函数来获取GPIO、设置GPIO方向、读取/设置GPIO的值。这样的驱动代码是于单板无关的。
二、GPIO内核相关文档
Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt Documentation\gpio\gpio.txt Documentation\devicetree\bindings\gpio\gpio.txt
三、GPIO设备树配置
1. BSP工程师实现的gpio驱动,驱动工程师直接在设备树中配置使用。
//client节点 device {
led-gpios = <组, 哪个几个,flag>;
//"组"是必须要有的元素,为gpio控制器的描述,这里除了组之外还有几个域是由组中的#gpio-cells的值决定的。
};
//service端,设备树中对一个gpio控制器的表示:
gpio1 {
......
gpio-controller;
#gpio-cells = <2>;
//表示client使用gpio1这一组中的某个引脚时,除了组之外还需要使用2个整数来表示。
};
2. 举个例子:
foo_device {
compatible = "acme,foo";
......
led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH> /*red*/ //一般这里可能为&gpioX
<&gpio 16 GPIO_ACTIVE_HIGH> /*green*/
<&gpio 17 GPIO_ACTIVE_HIGH>; /*blue*/
power-gpios = <&gpio, 1 GPIO_ACTIVE_LOW>; //注意这里使用了avtive_low属性了
};
驱动代码中:
gpiod_get_index(dev, "led", 0, GOIOD_OUT_HIGH); //取出设备树中名为led的gpio中的第0个引脚,也就是red。
gpiod_get_index(dev, "led", 1, GOIOD_OUT_HIGH); //取出第1个
若在设备树中只定义了一个引脚,就可以使用:
gpiod_get(dev, "power", GPIO_OUTPUT_HIGH); //把这个设备下名为power的那个引脚给取出来。
三、在驱动中使用GPIO
1.GPIO子系统有两套接口
(1) 一是基于描述符(descriptor-based)的,相关api函数都是以"gpiod_"为前缀,它使用gpio_desc结构来表示一个引脚。
(2) 另一种是老(legency)的,相关api函数都是以"gpio_"为前缀,它使用一个整数来表示一个引脚。
要操作一个引脚,首先要get引脚,然后设置方向,然后读取、写值。
2.列举操作GPIO常使用的函数
//1.获取GPIO
gpiod_get //legency为gpio_request
gpiod_get_index
gpiod_get_array //legency为gpio_request_array
devm_gpiod_get
devm_gpiod_get_index
devm_gpiod_get_array
//2.设置方向 gpiod_direction_input //legency为gpio_direction_input
gpiod_direction_output //legency为gpio_direction_input
//3.读值、写值
gpiod_get_value //legency为gpio_get_value
gpiod_set_value //legency为gpio_set_value
//4.释放GPIO
gpio_free //gpio_free
gpiod_put //gpio_free_array
gpiod_put_array
devm_gpiod_put
devm_gpiod_put_array
前缀为"devm_"的含义是设备资源管理,这是一种自动释放资源的机制。它的思想是“资源是属于设备的,设备不存在时资源就可以自动释放”。在Linux驱动开发过程中,先申请了GPIO,再申请内存,如果内存申请失败,那么在返回之前就需要先释放GPIO资源。如果使用的是devm相关函数,在内存申请失败时可以直接返回,设备的销毁函数会自动地释放已经申请了的GPIO资源。建议使用devm相关函数操作GPIO。
3.如何通过GPIO号来使用GPIO
比如要通过gpio号来操作原理图上的GPIO5_14这个gpio引脚,那么得先知道这个gpio引脚的number是多少。那么得先找到gpio5组的基gpio number是多少。
/sys/class/gpio/# ls
export gpio30 gpiochip0 gpiochip32 gpiochip64 gpiochip96 gpiochip128 gpiochip504 unexport //gpiochip96 这一组gpio的基gpio号是96
/sys/class/gpio/gpiochip128# ls
base device label ngpio power subsystem uevent
/sys/class/gpio/gpiochip128# cat label //可以看出设备树节点名为gpio@20ac000,20ac000就是这一组gpio寄存器的基地址,可通过它查看每一组gpio的基gpio号 20ac000.gpio
/sys/class/gpio/gpiochip128# cat base //表示这一组gpio的基gpio号为128 128
/sys/class/gpio/gpiochip128# cat ngpio //表示这一组gpio的个数 32
在dtsi文件中检索20ac000就可以看到如下设备树配置,所以知道gpio5这一组gpio的基gpio号是128
gpio5: gpio@20ac000 {
compatible = "fsl,im6ul-gpio", "fsl,imx35-gpio";
reg = <0x020ac000 0x4000>;
interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller; #gpio-cells = <0x2>;
interrupt-controller;
#interrupt-cells = <0x2>;
};
那么GPIO5_14,其gpio号就是128+14=142,然后可以将这个gpio给导出来,设置其方向,配置其值,进行验证。
/sys/class/gpio# echo 142 > export
/sys/class/gpio# ls export unexport gpio142 ... //此时可以看到多了一个gpio142目录
/sys/class/gpio/gpio142# ls active_low direction power uevent device edge subsystem value //此时可以cat active_low 看是否具有active_low属性
/sys/class/gpio/gpio142# echo in > direction //设置为输入引脚 /sys/class/gpio/gpio142# cat value //读取其值 1
/sys/class/gpio# echo 142 > unexport //取消映射
如果某个引脚已经被使用了,再次export就会报错:“resource busy”。算出引脚号后就可以在驱动中使用legency函数来通过gpio号来操作gpio引脚了。
注意: Qcom平台,如果内核级驱动程序通过of_get_named_gpio()函数或类似函数获取,请求并使用该GPIO,则无法将该GPIO导出以进行sysfs控制。
五、active_low属性
注意,设置的逻辑电平并不一定等于物理电平,因为有active_low属性,若在获取GPIO的时候指定了active_low属性,那么设置为1就是低电平,设置为0才是高电平。但是也有一些函数直接忽略active_low属性,整理如下:
unction(example) active-low属性 物理电平
gpiod_set_raw_value(desc, 0) don't care 0
gpiod_set_raw_value(desc, 1) don't care 1
gpiod_set_value(desc, 0) 是 1
gpiod_set_value(desc, 0) 否 0
gpiod_set_value(desc, 1) 是 0
gpiod_set_value(desc, 1) 否 1