這種其實應(yīng)該用一個直觀通俗的比喻的先講,再把里面的關(guān)系對應(yīng)到實際的代碼,多看圖,有詳有簡,慢慢改吧

1. STM32 MCU GPIO操控機制

1.1 底層寄存器定義架構(gòu)

頭文件結(jié)構(gòu) (stm32f4xx.h為例)

/* APB1總線基地址定義 */
#define APB1PERIPH_BASE       0x40000000UL
#define APB2PERIPH_BASE       0x40010000UL

/* GPIO外設(shè)偏移地址定義 */
#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800UL)  
#define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00UL)
#define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000UL)

/* GPIO寄存器結(jié)構(gòu)體定義 */
typedef struct {
    __IO uint32_t MODER;    /* 模式寄存器           偏移: 0x00 */
    __IO uint32_t OTYPER;   /* 輸出類型寄存器       偏移: 0x04 */
    __IO uint32_t OSPEEDR;  /* 輸出速度寄存器       偏移: 0x08 */
    __IO uint32_t PUPDR;    /* 上拉下拉寄存器       偏移: 0x0C */
    __IO uint32_t IDR;      /* 輸入數(shù)據(jù)寄存器       偏移: 0x10 */
    __IO uint32_t ODR;      /* 輸出數(shù)據(jù)寄存器       偏移: 0x14 */
    __IO uint32_t BSRR;     /* 位設(shè)置/復(fù)位寄存器    偏移: 0x18 */
    __IO uint32_t LCKR;     /* 配置鎖定寄存器       偏移: 0x1C */
    __IO uint32_t AFR[2];   /* 復(fù)用功能寄存器       偏移: 0x20-0x24 */
} GPIO_TypeDef;

/* GPIO外設(shè)指針定義 */
#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)

1.2 HAL庫封裝機制

GPIO初始化結(jié)構(gòu)體

typedef struct {
    uint32_t Pin;       /* 指定要配置的GPIO引腳 */
    uint32_t Mode;      /* 指定選中引腳的工作模式 */
    uint32_t Pull;      /* 指定選中引腳的上拉或下拉激活 */
    uint32_t Speed;     /* 指定選中引腳的速度 */
    uint32_t Alternate; /* 外設(shè)復(fù)用選擇 */
} GPIO_InitTypeDef;

/* 標(biāo)準(zhǔn)庫初始化函數(shù) */
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);

實際應(yīng)用示例/* 包含相關(guān)頭文件 */#include "stm32f4xx_hal.h"

void LED_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    /* 使能GPIO時鐘 */
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    /* 配置GPIO結(jié)構(gòu)體 */
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    
    /* 初始化GPIO */
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    /* 底層實現(xiàn)等價于: GPIOA->MODER |= (0x01 << (5*2)); */
}
不管是MCU,MPU,只要跑裸機操控GPIO都這樣。
采用宏定義的方式在頭文件定義相關(guān)GPIO,源文件使用宏定義,這樣可以將GPIO與實際的GPIO分隔開,使用不同的GPIO或者外設(shè),替換即可。野火的stm32f10x教程就是這樣寫的,方便不少。那么linux設(shè)備和驅(qū)動模型就是這樣一個映射關(guān)系。有一個設(shè)備樹文件描述這些GPIO等等信息,編譯生成dtb文件,在uboot分區(qū)下,在系統(tǒng)啟動會被轉(zhuǎn)化
為device。
在linux操作系統(tǒng)且mpu有mmu這個硬件(進程地址進行檢查),不能直接操作GPIO寄存器,相應(yīng)的xx外設(shè).core提供了操作函數(shù),若直接操作gpio,需要使用ioremap。需要記住的就是xxxcore.h
提供的接口函數(shù)。同時設(shè)備樹文件會在uboot被內(nèi)核轉(zhuǎn)為struct platform_device結(jié)構(gòu)體,被注冊,包含硬件地址,描述信息等等。
寫的驅(qū)動結(jié)構(gòu)體在module_init被注冊。platform_driver 和 platform_device 被匹配(此時應(yīng)有一圖)

image

 




Linux

probe函數(shù)主要是,字符類設(shè)備驅(qū)動:
  • 確定主設(shè)備號,也可以讓內(nèi)核分配

  • 定義自己的file_operations結(jié)構(gòu)體,并定義相應(yīng)的open,read,write,close,ioctrl函數(shù)

    • 需要注意用copy_to_user和copy_from_user做buf檢測,操作io寄存器需要ioremap或者傳給app操作framebuff需要mmap
  • register_chrdev
  • class_create, device_create,注冊設(shè)備節(jié)點
  • 對于其他如lcd,需要app來操作framebuff,需要mmap,對于需要操作GPIO寄存器,需要ioremap

 

2. Linux設(shè)備樹GPIO描述機制

2.1 設(shè)備樹GPIO節(jié)點完整示例

 
dts
// arch/arm/boot/dts/imx6ull-myboard.dts
/ {
    compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
    
    /* GPIO控制器定義 */
    gpio1: gpio@0209c000 {
        compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
        reg = <0x0209c000 0x4000>;
        interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
                     <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&clks IMX6UL_CLK_GPIO1>;
        gpio-controller;
        #gpio-cells = <2>;
        interrupt-controller;
        #interrupt-cells = <2>;
        gpio-ranges = <&iomuxc 0 23 10>, <&iomuxc 10 17 6>, 
                      <&iomuxc 16 33 16>;
    };
    
    /* LED設(shè)備節(jié)點 */
    leds {
        compatible = "gpio-leds";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_led>;
        
        led0 {
            label = "sys-led";
            gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;  /* GPIO1_IO03, 低電平有效 */
            default-state = "on";
            linux,default-trigger = "heartbeat";
        };
        
        led1 {
            label = "usr-led"; 
            gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>; /* GPIO4_IO19, 高電平有效 */
            default-state = "off";
            retain-state-suspended;
        };
    };
    
    /* 按鍵設(shè)備節(jié)點 */
    gpio-keys {
        compatible = "gpio-keys";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_gpio_keys>;
        
        user-key {
            label = "User Key";
            gpios = <&gpio1 18 GPIO_ACTIVE_LOW>;
            linux,code = <KEY_ENTER>;
            gpio-key,wakeup;
            debounce-interval = <50>;
        };
    };
    
    /* 自定義GPIO設(shè)備 */
    my_gpio_device {
        compatible = "mycompany,gpio-device";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_my_gpio>;
        
        reset-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
        power-gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;
        status-gpios = <&gpio3 15 GPIO_ACTIVE_HIGH>;
        
        gpio-controller;
        #gpio-cells = <2>;
        status = "okay";
    };
};

/* IOMUX配置 */
&iomuxc {
    pinctrl_led: ledgrp {
        fsl,pins = <
            MX6UL_PAD_GPIO1_IO03__GPIO1_IO03    0x17059  /* LED0 */
            MX6UL_PAD_CSI_VSYNC__GPIO4_IO19     0x17059  /* LED1 */
        >;
    };
    
    pinctrl_gpio_keys: gpio_keysgrp {
        fsl,pins = <
            MX6UL_PAD_UART1_CTS_B__GPIO1_IO18   0x80000000
        >;
    };
    
    pinctrl_my_gpio: my_gpiogrp {
        fsl,pins = <
            MX6UL_PAD_ENET1_TX_EN__GPIO2_IO01   0x17059  /* RESET */
            MX6UL_PAD_ENET1_TX_CLK__GPIO2_IO02  0x17059  /* POWER */
            MX6UL_PAD_LCD_RESET__GPIO3_IO04     0x17059  /* STATUS */
        >;
    };
};

2.2 設(shè)備樹GPIO屬性詳解

屬性名稱含義示例值說明
compatible 兼容性字符串 "gpio-leds" 匹配驅(qū)動程序
gpios GPIO規(guī)格說明 <&gpio1 3 GPIO_ACTIVE_LOW> 控制器+引腳號+極性
gpio-controller GPIO控制器標(biāo)識 - 表明該節(jié)點是GPIO控制器
#gpio-cells GPIO單元格數(shù)量 <2> 指定GPIO引用所需參數(shù)個數(shù)
pinctrl-names 引腳控制狀態(tài)名 "default", "sleep" 定義引腳狀態(tài)
pinctrl-0 默認(rèn)引腳配置 <&pinctrl_led> 引用IOMUX配置
default-state 默認(rèn)狀態(tài) "on"/"off"/"keep" LED初始狀態(tài)
linux,code 按鍵鍵值 <KEY_ENTER> 對應(yīng)input子系統(tǒng)鍵值

3. Linux內(nèi)核GPIO子系統(tǒng)架構(gòu)

3.1 內(nèi)核GPIO API函數(shù)

基礎(chǔ)GPIO操作函數(shù)

 
gpiod_get / gpiod_put 申請 / 釋放 GPIO 句柄 (生命周期管理)
gpiod_direction_input 設(shè)置為輸入模式
gpiod_direction_output 設(shè)置為輸出模式并設(shè)置初始值
gpiod_set_value 設(shè)置輸出電平
gpiod_get_value 獲取當(dāng)前電平
gpiod_set_debounce 設(shè)置硬件防抖 (輸入)
gpiod_set_open_drain 設(shè)置為開漏輸出
gpiod_to_irq 將 GPIO 轉(zhuǎn)換為 IRQ 號碼 (用于中斷)