之前都是在驱动程序中设置gpio引脚信息,设置寄存器状态功能,现在引入pinctrl和gpio子系统功能,可以在设备树中设置引脚,而不用去驱动程序中设置,方便程序的模块化设计。
文章目录
一.pinctrl子系统
1.pinctrl介绍
进入/arch/arm/boot/dts/可以找到imx6ull.dts。找到一个叫做iomuxc的节点。此节点是I.MX6ULL的外设对应的节点,打开imx6ull-alientek-emmc.dts找到iomuxc地址,会看到很多引脚。选择一个来解释说明
&iomuxc{
pinctrl-names = "default";
pinctrl-0 = <&pinstrl_hog_1>;
imx6ul-evk {
pindtrl_led: ledgrp{
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0
>;
}
}
}
重点是pins配置信息,GPIO1_IO03这个引脚有好几个功能可以选择,这里选择作为GPIO口使用,前面一个就是宏定义,一般定义在imx6ul-pinfunc.h中,可以看到每一个定义后面都有五个数字,含义如下:
mux_reg | conf_reg | input_reg | mux_mode | input_val |
---|---|---|---|---|
MUX寄存器偏移地址 | CONF偏移地址 | input偏移地址 | MUX寄存器值 | input 寄存器值 |
后面的0x10B0就是CONF寄存器的值,也就是PAD。用来设置此引脚的电气特性,上拉、驱动能力、频率等等。
2.节点模板
参考上面的led模板
首先在evk节点下面创建节点,pinstrl_led,必须是pinctrl_。然后添加属性,属性名字必须是“fsl,pins”,里面是具体引脚的配置信息。
二、gpio子系统
如果一个引脚通过上一节配置为GPIO口,那就用到下面的gpio系统。用于初始化gpio函数,以及其他API函数。
1.节点模板
\{
gpioled{
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-gpioled";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_led>;
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
status = "okay";
};
}
前三行属性不用介绍,描述pinctrl属性名字为“default”。pinstrl-0节点,引用上面在iomuxc下创建的pinstrl_led。表示gpioled节点使用的PIN信息保存在pinctrl_led。再下面添加GPIO属性信息,表示引用哪个IO口,并且设置成低电平。
2.gpio相关的OF函数
//统计设备树属性里定义了几个GPIO信息
int of_gpio_named_count(struct device_node *np, const char *proname)
//统计“gpios”这个属性的GPIO数量
int of_gpio_count(struct device_node *np)
//获取GPIO编号,Linux内核关于gpio的API都要使用编号,使用很频繁。
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
- np:设备节点。
- propname:要统计的GPIO属性
- index:GPIO索引,一个属性可能含有多个GPIO,此参数指定获取哪个GPIO编号
- return:正值,统计的数量/编号;负值失败。
3.gpio子系统API函数
//申请一个GPIO管教,使用GPIO之前一定先申请
int gpio_request(unsigned gpio, const char *label)
//不使用GPIO需要释放
void gpio_free(unsigned gpio)
//设置某个GPIO为输入
int gpio_direction_input(unsigned gpio)
//设置某个GPIO为输出
int gpio_direction_output(unsigned gpio, int value)
//获取某个GPIO的值
int gpio_get_value(unsigned gpio)
//设置某个GPIO的值
void gpio_set_value(unsigned gpio, int value)
三.程序设计
1.设备树修改
按照第一节的知识,在iomuxc节点下和根节点下分别创建节点。
2.检查PIN
一个引脚有多种功能,可能厂家的设备树把此引脚用于其他配置,但一个引脚只能实现一个功能,因此需要全局搜索,把此引脚的其他设置都屏蔽。
首先找此引脚有没有被其他pinctrl使用。MX6UL_PAD_GPIO1_IO03__GPIO1_IO03搜索,如果有直接屏蔽。
然后检查GPIO1_IO03有没有被其他外设使用,搜“gpio1 3”,找到后屏蔽。
3.驱动程序
使用第六章的程序。
struct gpioled_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 设备节点 */
int led_gpio; /* led所使用的GPIO编号 */
};
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioled; /* 设置私有数据 */
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
struct gpioled_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt);
ledstat = databuf[0]; /* 获取状态值 */
}
static int __init led_init(void)
{
int ret = 0;
/* 设置LED所使用的GPIO */
/* 1、获取设备节点:gpioled */
gpioled.nd = of_find_node_by_path("/gpioled");
/* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
printk("led-gpio num = %d\r\n", gpioled.led_gpio);
/* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */
ret = gpio_direction_output(gpioled.led_gpio, 1);
/* 注册字符设备驱动 */
return 0;
}
4.测试
测试APP和第六章一样,参考前面。