pinctrl子系统和gpio子系统

简介: pinctrl子系统和gpio子系统

pinctrl子系统和gpio子系统简化了我们驱动工程师的开发,将芯片的底层操作和驱动分割开来了,底层由bsp工程师完成,他来实现pinctrl子系统和gpio子系统,而我们只需要使用该系统就可完成相关的设备的初始化和配置。pinctrl完成的是引脚的配置,比如引脚的复用,引脚属性(上下拉,电阻大小等等)。gpio完成的是gpio的输入输出功能,给驱动提供接口来控制引脚的输出电平或读取引脚的值。这两者的配置我们只需要在设备树中添加就OK了。

1. pinctrl子系统使用步骤

1.1 节点的基本结构

该节点用于定义

pinctrl的节点应该添加到&iomuxc节点下, GPIO5相关的添加到&iomuxc_snvs下

// 基本结构如下:
pinctrl_test: testgrp { 
  fsl,pins = < 
    3 /* 设备所使用的 PIN 配置信息 */
  >;
};
// eg: 
pinctrl_rgb_led:rgb_led {
  fsl,pins = <
    MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1
    MX6UL_PAD_CSI_HSYNC__GPIO4_IO20  0x000010B1
    MX6UL_PAD_CSI_VSYNC__GPIO4_IO19  0x000010B1
  >;
};
  • pinctrl_test: 为pinctrl节点的别名,注意:前缀pinctrl_是必须有的
  • testgrp: 为pinctrl节点的名字
  • fsl, pins = <> , 规则如此没什么说的。里面的PIN配置信息结构为复用功能宏定义 设置pad的值。这些宏定义定义在文件源码根目录/arch/arm/boot/dts/imx6ul-pinfunc.h中,pad的值一般设置为10b1就好了。

1.2 需要在设备节点下调用前面定义的节点

需要添加的属性有:


  • pinctrl-nams = “default” , 该属性用于指定该设备节点的状态
  • pinctrl-n = <&pinctrl_test>, 该属性指定该设备的第n个状态的引脚配置魏pinctrl_test对应的配置
// eg: 
my_rgb_led {
    #address-cells = <1>;
    #size-cells = <1>;
    pinctrl-names = "default";
    compatible = "fire,my_rgb_led";
    pinctrl-0 = <&pinctrl_rgb_led>;
    // 后面的与GPIO子系统有关
    rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
    rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
    rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
    status = "okay";
  };

2. GPIO子系统使用步骤

2.1 需要在对应的设备树节点添加

属性:gpios = <>,属性值有三个,第一个指定使用的引脚属于那一组(格式为&gpio1, &gpio2,…),第二个为引脚号,第三个为一个宏定义,指定什么电平有效(GPIO_ACTIVE_HIGH, GPIO_ACTIVE_LOW) 。

// eg: 
my_rgb_led {
    ...
    rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
    rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
    rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
    status = "okay";
  };

2.2 GPIO子系统提供的API

2.2.1 gpio_request

用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用 gpio_request进行申请

int gpio_request(unsigned gpio, const char *label)
  • gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信息,此函数会返回这个 GPIO 的标号。
  • label:给 gpio 设置个名字。

2.2.2 gpio_free

对申请的gpio进行释放

void gpio_free(unsigned gpio)

2.2.3 gpio_direction_input

设置某个GPIO为输入

int gpio_direction_input(unsigned gpio)

2.2.4 gpio_direction_output

此函数用于设置某个 GPIO 为输出,并且设置默认输出值

int gpio_direction_output(unsigned gpio, int value)

2.2.5 gpio_get_value

此函数用于获取某个 GPIO 的值(0 或 1)

#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)

2.2.6 gpio_set_value

此函数用于设置某个 GPIO 的值

#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)

3. 与gpio相关的of函数

3.1 of_gpio_named_count

用于获取设备树某个属性里面定义了几个 GPIO 信息

int of_gpio_named_count(struct device_node *np, const char *propname)

3.2 of_gpio_count

与上一个函数功能相同,不同的是该函数指定了统计属性为"gpios"

int of_gpio_count(struct device_node *np)

3.3 of_get_named_gpio

此函数获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号

int of_get_named_gpio(struct device_node *np, const char *propname, int index)

4. 实例(野火i.MX6ULL PRO开发板)

4.1 设备树添加的内容

// 根节点下添加
  my_rgb_led {
    #address-cells = <1>;
    #size-cells = <1>;
    pinctrl-names = "default";
    compatible = "fire,my_rgb_led";
    pinctrl-0 = <&pinctrl_rgb_led>;
    rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
    rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
    rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
    status = "okay";
  };
// &iomuxc节点下添加
  pinctrl_rgb_led:rgb_led {
    fsl,pins = <
      MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1
      MX6UL_PAD_CSI_HSYNC__GPIO4_IO20  0x000010B1
      MX6UL_PAD_CSI_VSYNC__GPIO4_IO19  0x000010B1
    >;
  };

4.2 驱动文件

/* 编写要点
 1. 完成平台驱动(platform_driver_register())的注册,probe, remove函数的实现
 2. 完成驱动(driver_register())的注册,增加设备节点,实现file_operation结构体的相关函数
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <asm/io.h>
static int major = 0;  // 主设备号
static struct class *led_class;
static int rgb_led_red;
static int rgb_led_green;
static int rgb_led_blue;
static int current_led;
static int led_drv_open(struct inode *node, struct file *file)
{
  int minor = iminor(node);
  switch(minor) {
    case 0:
      current_led = rgb_led_red;
      break;
    case 1:
      current_led = rgb_led_green;
      break;
    case 2:
      current_led = rgb_led_blue;
      break;
    default:
      printk("========error: not minor=========\n");
  }
  printk("current_led: %d\n", current_led);
  printk("===============%s===============\n", __FUNCTION__);
  return 0;
}
static int led_drv_close(struct inode *node, struct file *file)
{
  printk("%s\n", __FUNCTION__);
  return 0;
}
static ssize_t led_drv_read(struct file *file, char __user * buf, size_t size, loff_t *offset)
{
  printk("%s\n", __FUNCTION__);
  return 0;
}
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
  int minor;
  char status;
  struct inode *node = file_inode(file);
  minor = iminor(node);
  copy_from_user(&status, buf, 1);
  if (status) {
    // 开灯
    gpio_direction_output(current_led, 0);
  } else {
    // 关灯
    gpio_direction_output(current_led, 1);
  }
  printk("%s\n", __FUNCTION__);
  return 0;
}
static struct file_operations led_drv = {
  .owner = THIS_MODULE,  //gcc用法
  .open = led_drv_open,
  .read = led_drv_read,
  .write = led_drv_write,
  .release = led_drv_close,
};
static int rgb_led_probe(struct platform_device *pdev)
{
  int err;
  // 获取资源
  struct device_node *rgb_device_node = NULL;
  // led节点的父节点
  rgb_device_node = of_find_node_by_path("/my_rgb_led");
  if (rgb_device_node == NULL) {
    printk("%s,%s,%d error: not find /my_reg_led\n", __FILE__, __FUNCTION__, __LINE__);
    return -1;
  }
  // 获取引脚编号
  rgb_led_red = of_get_named_gpio(rgb_device_node, "rgb_led_red", 0);
  rgb_led_green = of_get_named_gpio(rgb_device_node, "rgb_led_green", 0);
  rgb_led_blue = of_get_named_gpio(rgb_device_node, "rgb_led_blue", 0);
  printk("========red_red: %d=========\n", rgb_led_red);
  printk("========red_green: %d=========\n", rgb_led_green);
  printk("========red_blue: %d=========\n", rgb_led_blue);
  gpio_direction_output(rgb_led_red, 1);
  gpio_direction_output(rgb_led_green, 1);
  gpio_direction_output(rgb_led_blue, 1);
  // 完成字符设备的注册
  major = register_chrdev(0, "rgb_led", &led_drv);
  // 注册设备类
  led_class = class_create(THIS_MODULE, "hxdled_class");
  err = PTR_ERR(led_class);
  if (IS_ERR(led_class)) {
    unregister_chrdev(major, "rgb_led");
    return -1;
  }
  // 注册设备节点,/dev/rgb_led%
  device_create(led_class, NULL, MKDEV(major, 0), NULL, "led_red");
  device_create(led_class, NULL, MKDEV(major, 1), NULL, "led_green");
  device_create(led_class, NULL, MKDEV(major, 2), NULL, "led_blue");
  printk("=======================%s========================\n", __FUNCTION__);
  return 0;
}
static int rgb_led_remove(struct platform_device *dev)
{
  // 释放设备节点
  device_destroy(led_class, MKDEV(major, 0));
  device_destroy(led_class, MKDEV(major, 1));
  device_destroy(led_class, MKDEV(major, 2));
  // 释放设备类
  class_destroy(led_class);
  // 释放注册字符设备
  unregister_chrdev(major, "rgb_led");
  printk("%s\n", __FUNCTION__);
  return 0;
}
static const struct of_device_id rgb_led[] = {
  {.compatible = "fire,my_rgb_led"},
};
static struct platform_driver rgb_led_platform_driver = {
  .probe = rgb_led_probe,
  .remove = rgb_led_remove,
  .driver = {
    .name = "rgb-leds-device-tree",
    .owner = THIS_MODULE,
    //.remove = rgb_led_remove,
    .of_match_table = rgb_led,   // match匹配的时候会用到这个列表里面的compatible和设备树里面的compatible对比
  }
};
// 平台驱动入口函数
static int __init  platform_rgb_led_init(void)
{
  int ret = 0;
  // 完成平台驱动的注册
  ret = platform_driver_register(&rgb_led_platform_driver);
  printk("%s\n", __FUNCTION__);
  return 0;
}
// 平台驱动出口函数
static void __exit platform_rgb_led_exit(void)
{
  // 完成平台驱动的移除
  platform_driver_unregister(&rgb_led_platform_driver);
  printk("%s\n", __FUNCTION__);
  return;
}
module_init(platform_rgb_led_init);
module_exit(platform_rgb_led_exit);
MODULE_LICENSE("GPL");

4.3 应用程序

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<string.h>
int main(int argc, char **argv)
{
  int fd;
  char status;
  if (argc != 3) {
    printf("Usage %s <dev-path> <on/off>\n", argv[0]);
    return -1;
  }
  fd = open(argv[1], O_RDWR);
  if (fd < 0) {
    perror("open error");
    return -1;
  }
  if (strcmp(argv[2], "on") == 0) {
    // 开灯
    status = 1;
    write(fd, &status, 1);
  } else if (strcmp(argv[2], "off") == 0) {
    // 关灯
    status = 0;
    write(fd, &status, 1);
  } else {
    printf("Usage %s <dev-path> <on/off>\n", argv[0]);
    return -1;
  }
  close(fd);
  return 0;
}

4.4 Makefile

ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export  ARCH  CROSS_COMPILE
KERN_DIR = /home/hxd/workdir/ebf_linux_kernel_6ull_depth1/build_image/build
all:
  make -C $(KERN_DIR) M=`pwd` modules 
  $(CROSS_COMPILE)gcc -o led_test led_test.c 
clean:
  make -C $(KERN_DIR) M=`pwd` modules clean
  rm -rf modules.order
  rm -f hello_drv_test
obj-m += led_driver.o


目录
相关文章
|
2月前
|
Linux API 芯片
GPIO子系统驱动程序 【ChatGPT】
GPIO子系统驱动程序 【ChatGPT】
|
5月前
|
Linux API 芯片
Linux GPIO 和 Pinctrl 子系统的使用(十四)
Linux GPIO 和 Pinctrl 子系统的使用(十四)
62 1
|
API SoC
pinctrl和gpio子系统
pinctrl和gpio子系统
119 0
|
6月前
|
消息中间件 存储 Linux
嵌入式Linux系统中SPI 子系统基本实现
嵌入式Linux系统中SPI 子系统基本实现
140 0
|
6月前
|
Linux API 芯片
嵌入式Linux中pinctrl 子系统和 gpio 子系统分析
嵌入式Linux中pinctrl 子系统和 gpio 子系统分析
152 0
|
异构计算 SoC 内存技术
深入理解AMBA总线(九)AHB2SRAM设计
深入理解AMBA总线(九)AHB2SRAM设计
760 0
|
Linux 芯片
GPIO和Pinctrl子系统的使用
GPIO和Pinctrl子系统的使用
95 0
|
Ubuntu Linux Shell
UART子系统(三) UART属于TTY体系之一
UART子系统(三) UART属于TTY体系之一
236 1
UART子系统(三) UART属于TTY体系之一
|
移动开发 Unix Linux
UART子系统(四) TTY驱动程序框架
UART子系统(四) TTY驱动程序框架
227 1
UART子系统(四) TTY驱动程序框架
|
存储 Ubuntu Linux
UART子系统(五) 串口应用编程之回环(下)
UART子系统(五) 串口应用编程之回环
536 1
UART子系统(五) 串口应用编程之回环(下)