GPIO及LED/KEY的功能实现
流程介绍
GPIO功能注册和控制
MT7628支持引脚功能的复用,通过配置GPIO1_MODE和GPIO2_MODE寄存器可指定引脚的功能,通常在dts文件中对引脚功能进行注册。
MT7628引脚的GPIO注册位于/target/linux/ramips/dts/mt7628an.dtsi文件中,GPIO总共被分为3组,gpio0(GPIO#0——GPIO#31)、gpio1(GPIO#32——GPIO#63)和gpio2(GPIO#64——GPIO#95), @指明了gpio寄存器的起始地址为0x600,节点中compatible属性值指定了设备使用驱动为 mtk,mt7621-gpio。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | gpio@600 { #address-cells = <1>; #size-cells = <0>; compatible = "mtk,mt7628-gpio", "mtk,mt7621-gpio"; reg = <0x600 0x100>; interrupt-parent = <&intc>; interrupts = <6>; gpio0: bank@0 { reg = <0>; compatible = "mtk,mt7621-gpio-bank"; gpio-controller; #gpio-cells = <2>; }; gpio1: bank@1 { reg = <1>; compatible = "mtk,mt7621-gpio-bank"; gpio-controller; #gpio-cells = <2>; }; gpio2: bank@2 { reg = <2>; compatible = "mtk,mt7621-gpio-bank"; gpio-controller; #gpio-cells = <2>; }; }; |
使用GPIO引脚除了注册GPIO外,还需要释放GPIO引脚复用功能,将功能名称添加至 ralink group 属性中:
1 2 3 4 5 6 7 | &pinctrl { state_default: pinctrl0 { gpio { ralink,group = "i2c", "gpio", "jtag", "i2s", "spi cs1", "uart2", "pwm0", "pwm1", "sdmode", "spis"; ralink,function = "gpio"; }; }; |
与dts文件对应的引脚复用定义在source-17.01.4/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7628/linux-4.4.92/arch/mips/ralink/mt7620.c中。
如以下代码中i2c复用的gpio引脚定义为gpio#1和gpio#2(从gpio#1开始占用2个gpio), spi refclk复用的gpio引脚定义为gpio#37、gpio#38、gpio#39。同理,其他复用引脚的定义一样。
1 2 3 4 5 | static struct rt2880_pmx_func i2c_grp[] = { FUNC("i2c", 0, 1, 2) }; static struct rt2880_pmx_func spi_grp[] = { FUNC("spi", 0, 3, 4) }; static struct rt2880_pmx_func uartlite_grp[] = { FUNC("uartlite", 0, 15, 2) }; static struct rt2880_pmx_func rgmii1_grp[] = { FUNC("rgmii1", 0, 24, 12) }; static struct rt2880_pmx_func refclk_grp[] = { FUNC("spi refclk", 0, 37, 3) }; |
如以下代码中与WRTNODE2P.dts中ralink group属性的GPIO复用功能相对应。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | static struct rt2880_pmx_group mt7628an_pinmux_data[] = { …… GRP_G("i2c", i2c_grp_mt7628, MT7628_GPIO_MODE_MASK, 1, MT7628_GPIO_MODE_I2C), GRP("refclk", refclk_grp_mt7628, 1, MT7628_GPIO_MODE_REFCLK), GRP("perst", perst_grp_mt7628, 1, MT7628_GPIO_MODE_PERST), GRP("wdt", wdt_grp_mt7628, 1, MT7628_GPIO_MODE_WDT), GRP("spi", spi_grp_mt7628, 1, MT7628_GPIO_MODE_SPI), GRP_G("sdmode", sd_mode_grp_mt7628, MT7628_GPIO_MODE_MASK, 1, MT7628_GPIO_MODE_SDMODE), GRP_G("uart0", uart0_grp_mt7628, MT7628_GPIO_MODE_MASK, 1, MT7628_GPIO_MODE_UART0), …… { 0 } }; |
用户可以通过sysfs接口方式访问GPIO(目录位于/sys/class/gpio),gpio目录中包括 export文件、 unexport文件、gpiochipx目录、gpiox目录。
/sys/class/export文件用于通过GPIO引脚编号导出控制的gpio文件,/sys/class/unexport文件用于通过GPIO引脚编号去除控制的gpio文件。export导出成功后会在目录中生成/sys/class/gpio/gpiox目录,如果未出现相应的目录,说明该引脚不可导出。
/sys/class/gpiochipx/目录用于管理和控制一组GPIO的控制器, 目录保存该组GPIO寄存器信息,如寄存器控制引脚的起始编号base、寄存器名称label、引脚总数ngpio。
/sys/class/gpiox/目录用于具体的GPIO引脚控制,目录下有 direction、 value、edge等属性文件。其中direction文件定义为GPIO引脚方向(out为输出、in为输入)。value文件定义为GPIO引脚的电平(0为低电平,1为高电平), 当GPIO引脚方向配置为输出时,value文件值可写(任何非零的值都将输出高电平),当GPIO引脚方向配置为输入时,value文件值可读(cat value)。edge定义为中断的触发方式。
GPIO-LED功能注册和控制
在将GPIO注册为LED设备之前需要在配置界面(make menuconfig命令进入配置界面)选择添加Led驱动(默认编译时已经选上)
Led驱动: > Kernel modules > LED modules > <*> kmod-leds-gpio
WRTNODE2P.dts文件中将GPIO注册为LED设备,compatible属性值与LED驱动文件leds-gpio.c中的compatible属性值相对应。label值最终对应为文件系统/sys/class/leds/目录下LED设备的名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | gpio-leds { compatible = "gpio-leds"; indicator { label = "wrtnode:blue:indicator"; gpios = <&gpio1 9 1>; }; gpio35 { label = "wrtnode:red:sys_status"; gpios = <&gpio1 12 1>; }; gpio3 { label = "wrtnode:yellow:3g_wwan1"; gpios = <&gpio0 3 1>; }; }; |
烧录固件后进入 /sys/class/leds 可查看注册成功的led设备,可通过对brightness文件写入1和0的方式控制LED亮灭。
GPIO-KEY功能注册和控制
将GPIO注册为KEY设备与LED的方法类似,需要在配置界面(make menuconfig命令进入配置界面)选择添加Button的驱动(默认编译时已经选上)
Button 驱动: > Kernel modules > Other modules > kmod-gpio-button-hotplug
同理WRTNODE2P.dts文件中将GPIO#5注册为名为"reset"的KEY,通过调试状态查看KEY值,hi 表示高电平,lo 表示低电平。
1 2 3 4 5 6 7 8 9 10 11 12 | gpio-keys-polled { compatible = "gpio-keys-polled"; #address-cells = <1>; #size-cells = <0>; poll-interval = <20>; reset { label = "reset"; gpios = <&gpio0 5 1>; linux,code = <KEY_RESTART>; }; }; |
按键驱动文件 package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c中对按键的键值和名称进行了映射,比如"reset"按键,键值为 KEY_RESTART,当按下或释放该按键时驱动会向用户层发送事件执行/etc/rc.button/目录下所对应的脚本 reset.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | static struct bh_map button_map[] = { BH_MAP(BTN_0, "BTN_0"), BH_MAP(BTN_1, "BTN_1"), BH_MAP(BTN_2, "BTN_2"), BH_MAP(BTN_3, "BTN_3"), BH_MAP(BTN_4, "BTN_4"), BH_MAP(BTN_5, "BTN_5"), BH_MAP(BTN_6, "BTN_6"), BH_MAP(BTN_7, "BTN_7"), BH_MAP(BTN_8, "BTN_8"), BH_MAP(BTN_9, "BTN_9"), BH_MAP(KEY_BRIGHTNESS_ZERO, "brightness_zero"), BH_MAP(KEY_CONFIG, "config"), BH_MAP(KEY_COPY, "copy"), BH_MAP(KEY_EJECTCD, "eject"), BH_MAP(KEY_HELP, "help"), BH_MAP(KEY_LIGHTS_TOGGLE, "lights_toggle"), BH_MAP(KEY_PHONE, "phone"), BH_MAP(KEY_POWER, "power"), BH_MAP(KEY_RESTART, "reset"), BH_MAP(KEY_RFKILL, "rfkill"), BH_MAP(KEY_VIDEO, "video"), BH_MAP(KEY_WIMAX, "wwan"), BH_MAP(KEY_WLAN, "wlan"), BH_MAP(KEY_WPS_BUTTON, "wps"), }; |
例子代码
1.dts文件需要引入头文件
#include
1 2 3 4 5 6 7 8 9 10 | gpio-keys { compatible = "gpio-keys-polled"; poll-interval = <20>; reset { label = "reset"; gpios = <&gpio1 6 GPIO_ACTIVE_LOW>; linux,code = <KEY_RESTART>; }; }; |
3. 修改/etc/rc.button/reset脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | . /lib/functions.sh OVERLAY="$( grep ' /overlay ' /proc/mounts )" case "$ACTION" in pressed) [ -z "$OVERLAY" ] && return 0 return 5 ;; timeout) . /etc/diag.sh set_state failsafe ;; released) if [ "$SEEN" -ge 3 -a "$SEEN" -lt 8 ] then echo "REBOOT" > /dev/console sync reboot elif [ "$SEEN" -ge 10 -a -n "$OVERLAY" ] then echo "FACTORY RESET" > /dev/console jffs2reset -y && reboot & fi ;; esac |
参考链接
https://www.imooc.com/article/43412
https://blog.csdn.net/zhjmyx/article/details/103593528