我们在LINUX外设驱动的开发与调试中,GPIO的正确使用是一门必修课,本文主要描述MTK平台GPIO的使用和调试方法。
打开dws文件的工具 DrvGen.exe 有新和旧两种,不同平台使用的工具可能不一样,新平台大多使用新的 DrvGen.exe,两种工具只是打开的方式和界面不一样,其实GPIO的配置项两个都大同小异,本文以MT6739平台为例,使用新的 DrvGen.exe 工具。
路径在:alps\vendor\mediatek\proprietary\scripts\dct下,打开后选择左上角Pro然后open我们项目上所使用的 codegen.dws 文件,配置完成后保存即可,使用起来非常方便。
如下图:
Preloader初始化:
文件:alps\vendor\mediatek\proprietary\bootable\bootloader\preloader\platform\mt6739\src\drivers\gpio_init.c
mt_gpio_set_default()->mt_gpio_set_default_chip()
LK初始化:
文件:alps\vendor\mediatek\proprietary\bootable\bootloader\lk\platform\mt6739\gpio_init.c
mt_gpio_set_default()->mt_gpio_set_default_chip()
具体的初始化流程此处不作分析,有兴趣的朋友可以到上面文件中分析。
上面讲了DWS文件的配置,在 Preloader 和 LK 中我们会对GPIO进行初始化,在Preloader 和 LK 驱动中我们会根据需要对部分GPIO进行操作,如果在 Kernel 中我们不再对某些GPIO进行初始化和操作,将继续沿用 Preloader 和 LK 的属性。
pinctrl 子系统
在内核中我们使用 pinctrl 子系统对GPIO进行初始化。
dts部分配置:
&pio {
xxx_default: xxx_default {
};
xxx_init: xxxinit {
pins_cmd0_dat {
pinmux = ; /*普通gpio模式*/
slew-rate = <0>; /*0输入 1输出*/
bias-disable; /*浮空 bias-pull-up = <00>上拉 bias-pull-down= <00>下拉*/
};
pins_cmd1_dat {
pinmux = ;
slew-rate = <0>;
bias-disable;
};
};
};
&xxx {
pinctrl-names = "xxx_default", "xxx_init";
pinctrl-0 = <&xxx_default>;
pinctrl-1 = <&xxx_init>;
status = "okay";
};
在dts配置中,如果需要使用GPIO的复用功能,只需在 pinmux =
如:
pinmux =
驱动中 Pinctrl API 的
应用:
struct pinctrl *pctl = devm_pinctrl_get(&pdev->dev);
获取一个 pinctrl 句柄,参数dev是包含这个 pin 的device结构体,即xxx这个设备的device。
struct pinctrl_state *xxx_init = pinctrl_lookup_state(pctl, "xxx_init");
获取这个pin对应pin_state。
pinctrl_select_state(pctl, xxx_init);
设置引脚为某个stata。
GpioLib 库函数使用
使用 pinctrl 子系统我们也能对GPIO进行控制,如输入输出等,但是使用 gpiolib 库函数来控制GPIO的输入输出等更为方便,我们只需获取对应的 GPIO_NUM 即可。
获取 GPIO_NUM:
#define GPIO_XXX_PIN (GPIO84| 0x80000000)
xxx-gpio = <&pio 22 0>;
node = of_find_matching_node(NULL, of_match); //node = client->dev.of_node
gpio_num = of_get_named_gpio(node, "xxx-gpio", 0);
获取 IRQ_NUM:
gpio_to_irq->irq_num = gpio_to_irq(gpio_num);
interrupt-parent = <&pio>;
interrupts = <22 IRQ_TYPE_LEVEL_LOW>;
irq_num = irq_of_parse_and_map(node, 0);
获取到了 GPIO_NUM 和 IRQ_NUM 之后,我们就可以使用 gpiolib 库函数对GPIO进行操作了。
主要函数有:
static inline int gpio_request(unsigned int gpio, const char *label)
static inline void gpio_free(unsigned gpio)
static inline int gpio_direction_input(unsigned int gpio)
static inline int gpio_direction_output(unsigned int gpio, int v)
static inline int gpio_set_debounce(unsigned gpio, unsigned debounce) //防抖
static inline int gpio_get_value(unsigned int gpio)
static inline void gpio_set_value(unsigned int gpio, int v)
static inline int gpio_to_irq(unsigned int gpio)
static inline int irq_to_gpio(unsigned int irq的)
至于上面函数的功能也非常好理解,功能基本就是函数名的字面意思,这里就不做解释了。
在MTK平台,我们可以通过cat节点(mt_gpio)来查看对应的状态。不同的平台,该管脚对应的位置也是不同的。
需要先执行:adb root
查找节点位置:find sys/ -name "mt_gpio"
一般位于:/sys/devices/platform/soc/1000b000.pinctrl/mt_gpio
adb输入命令:cat ./sys/devices/platform/soc/1000b000.pinctrl/mt_gpio
PIN: [MODE] [DIR] [DOUT] [DIN] [PULL_EN] [PULL_SEL] [IES] [SMT] [DRIVE] ( [R1] [R0] )
0: 011110100 01
1: 000010100 01
2: 000111100 01
3: 000010100 01
4: 011100100 00
5: 000010100 01
6: 000100110
...
对应的属性基本与DWS配置项一致,这里只对 [DRIVE] ( [R1] [R0] ) 两项进行说明
[DRIVE]:驱动能力,一般可取值0~7
([R1] [R0]):当前GPIO pin的(上下拉)并联电阻的使能状态
1 0表示enable R1,disable R0
0 1表示disable R1,enable R0
1 1表示enable R1, enable R0
不打印出来,表示当前的GPIO pin不支持PUPD状况,即只有一个上拉电阻、一个下拉电阻。
通过对比上面信息我们可以看出GPIO的设置是否生效,如对应属性和驱动中设置不符,则需要检查驱动是否有疏漏,还要检查对应的GPIO是否有其他模块也使用了,我们也可以使用adb将GPIO配置成我们需要的状态,来验证此GPIO是否可控,也可以用此方法来临时满足我们外设的需求,方便调试其他功能。
使用adb设置GPIO属性:
设置模式:echo mode 14 0 > mt_gpio
设置输入/输出:echo dir 14 1 > mt_gpio
输出高/低:echo out 14 1 > mt_gpio
设置pullen: echo pullen 14 1 > mt_gpio
NOTE:设置pullen时,
For pin with 2 pull resistors, $value can be 0 (R1=0, R0=0), 1 (R1=0, R0=1), 2 (R1=1, R0=0), 3 (R1=1, R0=1)
For pin with 1 pull resistor, $value can be 0 or 1
以上如有不当之处欢迎私信或底下评论给小编指导更正,非常感谢查阅!