目录
1、GPIO配置流程
2、GPIO子系统API
2.1 of_find_node_by_path
2.2 of_get_named_gpio
2.3 gpio_request 与 gpiod_get 与 gpiod_get_index
2.4 gpio_direction_input 与 gpiod_direction_input
2.5 gpio_direction_output 与 gpiod_direction_output
2.6 gpio_get_value 与 gpiod_get_value
2.7 gpio_set_value 与 gpiod_set_value
2.8 gpiod_get_from_of_node
2.9 gpio_free 与 gpiod_put
3、GPIO子系统驱动程序
3.1 没有platform总线前怎么得到gpio节点
3.1.1 修改设备树
3.1.2 驱动程序:老版接口
3.1.3 驱动程序:新版接口
3.2 platform总线下得到
3.2.1 修改设备树:配置pinctrl
3.2.2 修改设备树:配置自己的节点
3.2.3 驱动程序
1、GPIO配置流程
如果要操作GPIO引脚的话,需要先将所用引脚配置为GPIO功能,这需要通过Pinctrl子系统来实现。
在Pinctrl子系统将引脚配置为GPIO功能后,就可以使用GPIO子系统来设置GPIO的方向等。
在设备树中,“GPIO组”就是一个GPIO Controller,这通常都由芯片厂家设置好。我们要做的是找到它名字,比如“gpio1”,然后指定要用它里面的哪个引脚,比如<&gpio1 0>。
我们需要关心里面的这2个属性:
#gpio-controller; 表示这个节点是一个GPIO Controller,它下面有很多引脚。
#gpio-cells = <2>; 表示这个控制器下每一个引脚要用2个32位的数(cell)来描述。
普遍的用法是,用第1个cell来表示哪一个引脚,用第2个cell来表示有效电平:
GPIO_ACTIVE_HIGH : 高电平有效
GPIO_ACTIVE_LOW : 低电平有效
可参考的帮助文档
Linux-4.9.88/Documentation/devicetree/bindings/gpio/gpio.txt
Linux-4.9.88/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.txt
2、GPIO子系统API
2.1 of_find_node_by_path
struct device_node *of_find_node_by_path(const char *path)
/**/
功能:通过路径或者节点结构体
参数:
@path:路径
返回值:成功返回节点的首地址,失败返回NULL
2.2 of_get_named_gpio
int of_get_named_gpio(struct device_node *np,const char *propname, int index)
/*
功能:根据节点结构体解析gpio编号
参数:
@np:节点指针
@propname:键
@index:索引号
返回值:成功返回gpio的编号,失败返回错误码
*/
2.3 gpio_request 与 gpiod_get 与 gpiod_get_index
int gpio_request(unsigned gpio, const char *label)
/*
功能:申请要使用的gpio
参数:
@gpio:gpio的编号
@label:标签名
返回值:成功返回0,失败返回错误码
*/
//如果是一个GPIO就用gpiod_get,如果有多个就用gpiod_get_index
struct gpio_desc *__must_check gpiod_get(struct device *dev,
const char *con_id, enum gpiod_flags flags)
struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags)
/*
功能:申请要使用的gpio
参数:
@dev: device结构体,如果有platform,则 &pdev->dev
@con_id: 键
@idx: 索引号
@flags: 0
返回值:成功返回gpio_desc 结构体,失败返回错误码
*/
2.4 gpio_direction_input 与 gpiod_direction_input
int gpio_direction_input(unsigned gpio)
/*
功能:设置gpio的为输入
参数:
@gpio:gpio的编号
返回值:成功返回0,失败返回错误码
*/
int gpiod_direction_input(struct gpio_desc *desc)
/*
功能:设置gpio为输入
参数:
@desc:gpio_desc结构体
返回值:成功返回0,失败返回错误码
*/
2.5 gpio_direction_output 与 gpiod_direction_output
int gpio_direction_output(unsigned gpio, int value)
/*
功能:设置gpio的为输出
参数:
@gpio:gpio的编号
@value:默认电平的状态 1高电平 0低电平
返回值:成功返回0,失败返回错误码
*/
int gpiod_direction_output(struct gpio_desc *desc, int value)
/*
功能:设置gpio为输出
参数:
@desc:gpio_desc结构体
返回值:成功返回0,失败返回错误码
*/
2.6 gpio_get_value 与 gpiod_get_value
int gpio_get_value(unsigned gpio)
/*
功能:读取管脚的值
参数:
@gpio:gpio的编号
返回值:1高电平 0低电平
*/
int gpiod_get_value(const struct gpio_desc *desc)
/*
功能:读取管脚的值
参数:
@desc: gpio_desc结构体
返回值:1高电平 0低电平
*/
2.7 gpio_set_value 与 gpiod_set_value
void gpio_set_value(unsigned gpio, int value)
/*
功能:设置输出电平的值
参数:
@gpio:gpio的编号
@value:1高电平 0低电平
返回值:无
*/
void gpiod_set_value(struct gpio_desc *desc, int value)
/*
功能:设置输出电平的值
参数:
@desc: gpio_desc结构体
@value: 1高电平 0低电平
返回值:无
*/
2.8 gpiod_get_from_of_node
struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
const char *propname, int index, enum gpiod_flags dflags, const char *label)
/*
功能:在设备树节点信息结构体中获取并申请要使用的gpio编号,然后输出
此函数一个函数就完成了申请加上输出的功能
参数:
@node:设备树节点信息结构体指针
@propname:键名
@index:索引
@dflags:gpio状态值
GPIOD_OUT_LOW
GPIOD_OUT_HIGH
@label:标签,填写NULL
返回值:成功返回gpio描述结构体指针,失败返回错误码指针
*/
2.9 gpio_free 与 gpiod_put
void gpio_free(unsigned gpio)
/*
功能:释放gpio
参数:
@gpio:gpio的编号
返回值:无
*/
void gpiod_put(struct gpio_desc *desc)
/*
功能:释放gpio
参数:
@desc:gpio_desc结构体
返回值:无
*/
3、GPIO子系统驱动程序
3.1 没有platform总线前怎么得到gpio节点
3.1.1 修改设备树
设备树文件在
arch/arm/boot/dts/100ask_imx6ull-14x14.dts
在根节点下写自己的节点
myleds{
led1=<&gpioe 10 0>;
led2=<&gpiof 10 0>;
led3=<&gpioe 8 0>;
};
3.1.2 驱动程序:老版接口
struct device_node *node;
int gpiono;
static int __init mycdev_init(void)
{
//1.获取节点
node = of_find_node_by_path("/myleds");
//2.解析得到gpio
gpiono = of_get_named_gpio(node, "led1", 0);
//3.申请使用的gpio
gpio_request(gpio,NULL));
//4.通过gpio申请设备
gpio_direction_output(gpiono, 0);
return 0;
}
static void __exit mycdev_exit(void)
{
gpio_set_value(gpiono,0);
gpio_free(gpiono);
}
3.1.3 驱动程序:新版接口
static int __init mycdev_init(void)
{
//1.获取节点
node = of_find_node_by_path("/myleds");
//2、申请GPIO
//此函数一个函数就完成了申请加上输出的功能
desc = gpiod_get_from_of_node(node, "led2", 0, GPIO_OUT_HIGH, 0);
return 0;
}
static void __exit mycdev_exit(void)
{
gpiod_set_value(desc,0);
gpiod_put(desc);
}
3.2 platform总线下得到
3.2.1 修改设备树:配置pinctrl
使用imx图形化界面来配置引脚文章来源:https://www.toymoban.com/news/detail-525200.html
//在pinctrl下设置引脚
&iomuxc_snvs
myled_for_gpio_subsys: myled_for_gpio_subsys{
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x000110A0
>;
};
3.2.2 修改设备树:配置自己的节点
在根节点下写自己的节点文章来源地址https://www.toymoban.com/news/detail-525200.html
myled {
compatible = "100ask, leddrv" ;
pinctrl-names = "default";
pinctrl-0 = <&myLed_for_gpio_subsys>;
led-gpios = <&gpio5 3 GPIO_ACTIVE_HIGH>;
};
3.2.3 驱动程序
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
gpiod_set_value(led_gpio, status);
}
static struct file_operations led_drv = {
.write = led_drv_write,
};
static int chip_demo_gpio_probe(struct platform_device *pdev)
{
led_gpio = gpiod_get(&pdev->dev, "led", 0);
register_chrdev(0, "100ask_led", &led_drv);
return 0;
}
static int chip_demo_gpio_remove(struct platform_device *pdev)
{
unregister_chrdev(major, "100ask_led");
gpiod_put(led_gpio);
return 0;
}
static const struct of_device_id ask100_leds[] = {
{ .compatible = "100ask,leddrv" },
{ },
};
/* 1. 定义platform_driver */
static struct platform_driver chip_demo_gpio_driver = {
.probe = chip_demo_gpio_probe,
.remove = chip_demo_gpio_remove,
.driver = {
.name = "100ask_led",
.of_match_table = ask100_leds,
},
};
static int __init led_init(void)
{
platform_driver_register(&chip_demo_gpio_driver);
}
static void __exit led_exit(void)
{
platform_driver_unregister(&chip_demo_gpio_driver);
}
到了这里,关于Linux驱动开发:gpio子系统的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!