一、pinctrl 子系统
1、简介
注册和编写不用管Linux内核调用机制,按规则编写注册就好了,大不了底层配置demo没有的需要自己配置PIN信息,其他按步就班编写就好了
作用: pinctrl 子系统主要用于管理芯片的引脚配置对应的工作模式。imx6ull 芯片拥有众多的片上外设,大多数外设需要通过芯片的引脚与外部设备(器件)相连实现相对应的控制。
- pinctrl 子系统是由芯片厂商来实现的, 简单来说用于帮助我们管理芯片引脚并自动完成引脚的初始化,而我们要做的只是在设备树中按照规定的格式写出想要的配置参数即可。
- 获取设备树中 pin 信息。
- 根据获取到的 pin 信息来设置 pin 的复用功能。
- 根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。
目的:学会在设备树下修改或者添加自己的pinctrl
2、 iomuxc
iomuxc设备节点pinctrl 子系统的平台驱动做匹配
- 子节点追加数据,不同的外设使用的 PIN 不同、其配置也不 同,意思是某个外设所使用的所有 PIN 都组织在一个子节点里面。
- 比如串口就是串口名字然后配置所有的PIN引脚信息。
- 各个厂商会提供不同的文件节点,比如正点引用在imx6ull-alientek-emmc.dts野火./arch/arm/boot/dts/imx6ull-mmc-npi.dts中通过&iomuxc追加。
3、怎么看设备树里面pinctrl子系统每个外设的PIN 配置
第一个参数为复用,具体配置是配置芯片引脚的功能,第二个参数一般为电气属性配置其上下拉等一些电气属性,一般按照官方demo配置就没有问题的
4、添加设备树中 pinctrl 节点模板
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
>;
- 节点前缀一定要为“pinctrl_”
pinctrl_xxx: xxx
- 添加“fsl,pins”属性
fsl,pins = <
>;
- 在“fsl,pins”属性中添加 PIN 配置信息
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059
MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058
>;
二、 gpio 子系统
作用:在pinctrl设置好引脚的复用与电气属性以后,如果是使用GPIO的功能使用GPIO子系统配置引脚的输入输出,高低电平
1、设备树的体现
在需要外设使用GPIO子系统下的设备节点添加对应的gpio属性
- 比如在SD卡中检测是否插入或者拔掉的办法
- cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
&usdhc1 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc1>;
pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
keep-power-in-suspend;
enable-sdio-wakeup;
vmmc-supply = <®_sd1_vmmc>;
status = "okay";
};
2、对于驱动开发人员,设置好设备树以后就可以使用 gpio 子系统提供的 API 函数来操作指定的 GPIO
- gpio_request 函数
- gpio_free 函数
- gpio_direction_input 函数
- gpio_direction_output 函数
- gpio_get_value 函数
- gpio_set_value 函数
OF函数
- of_gpio_named_count
- of_gpio_count
- of_get_named_gpio
3、编写GPIO子系统一句话
在注册为GPIO模式下需要的节点下面添加 gpio = <&gpio1 0 GPIO_ACTIVE_LOW>;
三、编写测试程序实操
1、修改设备树,添加pinctrl&gpio子系统,并且检测系统中引脚是否复用
- 编写设备节点,在节点添加注册pinctrl子系统pinctrl_0 = <&pinctrl_gpioled>与注册GPIO子系统led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;注册完以后驱动程序就可以使用gpio注册函数编写代码了
liqi_gpioled{
compatible = "liqi-led";
pinctrl_name ="default";
pinctrl_0 = <&pinctrl_gpioled>;
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
#address-cells = <1>;
#size-cells = <1>;
status = "okay";
};
- 在pinctrl子系统下面配置GPIO属性
pinctrl_gpioled: gpioledgrp-1 {
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0
>;
};
- 利用pinctrl和gpio系统引脚配置检测设备树有没有重复使用引脚的。在linux下一个引脚仅支持复用为一种功能的代码
在dts源文件下搜索GPIO1_IO03或者gpio1 3
2、编写驱动程序
先改设备树使用GPIO子系统gpio驱动函数,再注册设备驱动
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define GPIOLED_CNT 1
#define GPIOLED_NAME "gpioled"
#define LEDOFF 0
#define LEDON 1
struct gpioled_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int led_gpio;
};
struct gpioled_dev gpioled;
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioled;
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
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);
if(retvalue < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0];
if(ledstat == LEDON) {
gpio_set_value(dev->led_gpio, 0);
} else if(ledstat == LEDOFF) {
gpio_set_value(dev->led_gpio, 1);
}
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
static int __init led_init(void)
{
int ret = 0;
gpioled.nd = of_find_node_by_path("/gpioled");
if(gpioled.nd == NULL) {
printk("gpioled node not find!\r\n");
return -EINVAL;
} else {
printk("gpioled node find!\r\n");
}
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if(gpioled.led_gpio < 0) {
printk("can't get led-gpio");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", gpioled.led_gpio);
ret = gpio_direction_output(gpioled.led_gpio, 1);
if(ret < 0) {
printk("can't set gpio!\r\n");
}
if (gpioled.major) {
gpioled.devid = MKDEV(gpioled.major, 0);
register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
} else {
alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}
printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if (IS_ERR(gpioled.class)) {
return PTR_ERR(gpioled.class);
}
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if (IS_ERR(gpioled.device)) {
return PTR_ERR(gpioled.device);
}
return 0;
}
static void __exit led_exit(void)
{
cdev_del(&gpioled.cdev);
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
device_destroy(gpioled.class, gpioled.devid);
class_destroy(gpioled.class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");
3、编译测试
- 修改makefile文件
obj-m := xxx.o
make -j32 编译成功以后就会生成一个名为“gpioled.ko”的驱动模块文件。
- 编译测试 APP
arm-linux-gnueabihf-gcc xxx.c -o xxx
生成可执行文件
- gpioled.ko 和 ledApp 这两个文件拷贝到 rootfs/lib/modules/4.1.15 目 录中,重启开发板,进入到目录 lib/modules/4.1.15 中
cp xx xxx /home/alientek/linux/nfs/rootfs/lib/modules/4.1.15 -f
- 加载驱动
depmod modprobe xx.ko 查看驱动输出信息,cat /proc/devices 查看当前注册设备节点
5. 测试应用程序 ./liqiledAPP dev/dtsled 1 ./liqiledAPP dev/dtsled 0
- 实验现象led亮灭
总结
联系
交个博友,技术分享交流联系作者Q群:586025772
|