由于直接对寄存器操作-----达到IO复用 操作电气属性的目的 太过于底层
linux提供了一些配置GPIO驱动的函数
————————————————————1——————————————————————
pinctrl 子系统:
①、获取设备树中 pin 信息。
②、根据获取到的 pin 信息来设置 pin 的复用功能
alphaled {
#address-cells = <1>;
#size-cells =<1>;
status = "okay";
compatible = "alientek,alphaled";
//地址 地址长度
reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */
0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */
0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */
0X0209C000 0X04 /* GPIO1_DR_BASE */
0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */
};
/*寄存器物理地址定义*/
#define CCM_CCGR1_BASE (0X020C406C) /*CCGR1时钟地址 控制GPIO*/
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068) /*IO口打复用地址*/
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4) /*电气属性*/
#define GPIO1_DR_BASE (0X0209C000) /*高低电平设置*/
#define GPIO1_GDIR_BASE (0X0209C004) /*设置输入输出模式 4*8=32位*/
————————————————变更
此节点需要的属性:“fsl,pins”属性
pinctrl 驱动程序是通过读取“fsl,pins”属性值来获取 PIN 的配置信息
根 据 reg 属 性 可 知 IOMUXC 外 设 寄 存 器 起 始 地 址 为 0x020e0000
/*属于pinctrl子系统
对于一个 PIN 的配置主要包括两方面,
一个是设置这个 PIN 的复用功能
另一个就是设置这个 PIN 的电气特性*/
pincrtl_gpioled: gpioledgrp{
fsl,pins = <
/*led灯的外设电气属性 加 寄存器地址*/
/*MX6UL_PAD_GPIO1_IO03__GPIO1_IO03
*宏定义0x0068 0x02F4 0x0000 0x5 0x0在imx6ul-pinfun
*0x0068 复用寄存器偏移地址
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的偏移地址 20E_0000h base + 68h offset = 20E_0068h
*0x02F4 电气属性寄存器偏移地址
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03的偏移地址 20E_0000h base + 2F4h offset = 20E_02F4h
*0x0000寄存器偏移地址,有些外设有 input_reg 寄存器
*0x5 第一个寄 存 器 值 复用
0101 ALT5 — Select mux mode: ALT5 mux port: GPIO1_IO03 of instance: gpio1
*0x0 input_reg 寄存器值 */
//0x10b0 为电气属性寄存器的值
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10b0
>;
};
将pinctrl 节点添加到设备节点中
pinctrl-0 = <&pincrtl_gpioled>;
//gpio设备子节点在/目录下添加
gpioled{
compatible = "alientek,gpioled";
//pinctrl子系统 控制外设寄存器地址和电气属性
pinctrl-names = "default";
pinctrl-0 = <&pincrtl_gpioled>;
//gpio子系统
led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;//灯低电平有效
status = "okay";
};
——————————————————————————————————
1、设置GPIO1—IO03的IO复用和电气属性
2、配置了GPIO1-IO03 添加 GPIO 属性信息,表明 LED所使用的 GPIO 是哪个引脚
gpio 子系统提供的常用的 API 函数
添加完成后,检查引脚 是否被其他外设使用①、检查 pinctrl 设置②、如检查 GPIO 设置
_________________________________使用 make dtbs
——————————————————————————————————————————
程序编写
描述一个设备信息
/*gpioled设备结构体*/
/*该设备需要的*/
struct gpioled_dev
{
dev_t devid; /*设备号 32位*/
int major ;/*主设备号 高12位*/
int minor ;/*次设备号 低20位*/
/*cdev表示字符设备 结构体初始化就是初始化结构体变量*/
struct cdev cdev ;//包含了操作函数集 和 设备号 包含头文件 #include
struct class *class; /*类*/
struct device *device; /*设备*/
struct device_node *nd; //节点
int led_gpio ;//led 所使用的 GPIO 编号
};
struct gpioled_dev gpioled;
——————————————————————————
/*入口 -注册字符设备*/中注册设备号 /cdev 具体的字符设备操作集合 向内核添加字符设备 自动创建设备节点 不变
根据设备树文件
/*1-找到节点 dts文件中的节点信息 gpioled*/
/*全路径是 backlight---使用全路径查找*/
gpioled.nd = of_find_node_by_path("/gpioled");
if(gpioled.nd == NULL)//是否成功
{
ret = -EINVAL ;
goto failed_findnd;
}
/*2-获取编号 最重要的是gpios属性 —————————这里使用的是gpio子系统提供的API函数*/
/*获取led对应的gpio led-gpios 获取 GPIO 编号*/
//gpio 的节点 获取GPIO信息的属性名 指定要获取哪个 GPIO的编号,如果只有一个 GPIO 信息的话此参数为 0
//正值,获取到的 GPIO 编号;负值,失败。
gpioled.led_gpio = of_get_named_gpio(gpioled.nd,"led-gpios", 0);
if( gpioled.led_gpio < 0 )
{
printk("cant't find led-gpios \r\n");
ret = -EINVAL;
goto failed_named;
}
printk("led gpio num =%d \r\n" ,ret );
/*3 在使用一个 GPIO 之前要先申请GPIO管脚-----------在退出中要有释放*/
//gpio_request 函数用于申请一个 GPIO 管脚
//0,申请成功;其他值,申请失败
//GPIO 的标号 给 gpio 设置个名字
//如果不申请就无法检测这个IO有没有被使用
ret = gpio_request(gpioled.led_gpio, "ledgpio");
if(ret)
{
printk("falied to request the led gpio \r\n");
ret = -EINVAL;
goto failed_request;
}
/*4————————申请成功后需要使用IO */
/*设置为输出*/
//gpio:要设置为输出的 GPIO 标号value:GPIO 默认输出值。
//:0,设置成功;负值,设置失败
//灯低电平点亮 默认不亮 高电平
ret = gpio_direction_output(gpioled.led_gpio, 1);
if(ret<0)
{
printk("falied to set the led gpio output \r\n");
ret = -EINVAL;
goto failed_output;
}
/*5——————设置输出后就可以控制灯输出高低电平了*/
//输出低电平 点亮LED灯
// gpio:要设置的 GPIO 标号 value:要设置的值。
gpio_set_value(gpioled.led_gpio, 0);
————————————————————
同样 在操作函数中也调用GPIO的API函数(这是在注册字符设备的时候完成的)