ESP32外設入門GPIO

前言

最近開發用到了ESP32,樂鑫已經對接口進行比較深度的封裝,但是還是看了下外設的參考手冊,摘取了一部分結合自己的理解給記錄下來。
如有異議,歡迎指正

概述

ESP32芯片具有34個物理GPIO。每個pad都可用作一個通用IO,或連接內部的外設信號。IO_MUX、RTC_IO_MUX和GPIO交換矩陣用於將信號從外設傳輸至GPIO pad,這些模塊共同組成了芯片的IO控制。
備註:這34個物理GPIO pad的序列號爲:0-19, 21-23, 25-27, 32-39。其中GPIO34-39僅作爲輸入管腳,其他的可作爲輸入輸出管腳

GPIO交換矩陣結構圖

  • 數字IO口通過IO_MUX複用器來選擇通用IO口還是複用外設功能,然後進過GPIO matrix交換矩陣來關聯到具體的外設中
    圖中也可以配置不進過交換矩陣,這樣可以提高引腳的高頻特性【如高速率 UART SPI SDIO】

在這裏插入圖片描述
在這裏插入圖片描述

GPIO配置的流程

  • 配置外設信號寄存器GPIO_FUNC_IN_SEL_CFG,選擇需要配置的PIN腳
  • 設置GPIO_ENABLE_DATA使能寄存器
  • 配置IO_MUX寄存器,配置IO功能(上下拉、輸入輸出)

備註

  • 同一個引腳可以同時綁定多個內部信號
  • 可以通過寄存器GPIO_FUNC_IN_INV_SEL對信號進行取反,在一些應用中很實用
  • 當某個外設不綁定GPIO時,例如SPI中的MOSI不使用,可以配置GPIO_FUNC_IN_SEL爲0x38或0x30,保證外設的正常運行

RTC IO_MUX低功耗IO功能

當管腳被配置成RTC GPIO時,能在系統處於Deep-Sleep睡眠模式下保持電平值,也可以實現在低功耗模式下的喚醒
PS:測試發現,ESP32在低功耗上面表現並不是很優異

Light-Sleep模式管腳功能

當 ESP32 處於 Light-sleep 模式時管腳可以有不同的功能。如果某一 GPIO pad 的 IO_MUX 寄存器中 SLP_SEL位置爲 1,芯片處於 Light-sleep 模式下將由另一組不同的寄存器控制 pad。

  • 如果 SLP_SEL 置爲 0,則芯片在正常工作和 Light-sleep 模式下,管腳的功能一樣。
    在這裏插入圖片描述

Pad Hold特性

每個 IO pad(包括 RTC pad)都有單獨的 hold 功能,由 RTC 寄存器控制。pad 的 hold 功能被置上後,pad 在
置上 hold 那一刻的狀態被強制保持,無論內部信號如何變化,修改 IO_MUX 配置或者 GPIO 配置,都不會改變
pad 的狀態。應用如果希望在看門狗超時觸發內核復位和系統復位時或者 Deep-sleep 時 pad 的狀態不被改變,
就需要提前把 hold 置上。

IO_MUX Pad列表

  • 設計硬件前,可以查看IO的複用功能來進行合理的配置
    在這裏插入圖片描述

RTC_MUX管腳清單

在這裏插入圖片描述

代碼例程

打開idf目錄下的gpio_example_main,路徑esp-idf\examples\peripherals\gpio\main

代碼講解

開發可移植例程代碼,具體可看註釋

  • GPIO18 GPIO19配置爲輸出,通過接口gpio_set_level配置輸出電平
  • GPIO4 GPIO5配置成輸入中斷使能,外部中斷入口gpio_isr_handler
#define GPIO_OUTPUT_IO_0    18
#define GPIO_OUTPUT_IO_1    19
#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))
#define GPIO_INPUT_IO_0     4
#define GPIO_INPUT_IO_1     5
#define GPIO_INPUT_PIN_SEL  ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))
#define ESP_INTR_FLAG_DEFAULT 0

static xQueueHandle gpio_evt_queue = NULL;

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);//中斷後發送io消息
}

static void gpio_task_example(void* arg)
{
    uint32_t io_num;
    for(;;) 
    {
        if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) //收到消息打印對應引腳的電平
        {
            printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
        }
    }
}

void app_main(void)
{
    gpio_config_t io_conf;
    //disable interrupt
    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    //set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;//輸出模式
    //bit mask of the pins that you want to set,e.g.GPIO18/19
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;//配置輸出
    //disable pull-down mode
    io_conf.pull_down_en = 0;//禁用下拉
    //disable pull-up mode
    io_conf.pull_up_en = 0;//禁用上拉
    //configure GPIO with the given settings
    gpio_config(&io_conf);//進行配置

    //interrupt of rising edge
    io_conf.intr_type = GPIO_PIN_INTR_POSEDGE;//上升沿中斷
    //bit mask of the pins, use GPIO4/5 here
    io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;//配置輸入
    //set as input mode    
    io_conf.mode = GPIO_MODE_INPUT;//輸入模式
    //enable pull-up mode
    io_conf.pull_up_en = 1;//使能上拉
    gpio_config(&io_conf);

    //change gpio intrrupt type for one pin
    gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);

    //create a queue to handle gpio event from isr
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));//創建隊列
    //start gpio task
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);//創建任務

    //install gpio isr service
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);//安裝中斷服務
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);//GPIO4中斷回調註冊
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);//GPIO5中斷回調註冊

    //remove isr handler for gpio number.
    gpio_isr_handler_remove(GPIO_INPUT_IO_0);//移除測試
    //hook isr handler for specific gpio pin again
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);//重新添加測試

    int cnt = 0;
    //1秒閃爍
    while(1) 
    {
        printf("cnt: %d\n", cnt++);
        vTaskDelay(1000 / portTICK_RATE_MS);
        gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2);
        gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);
    }
}

API文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章