前言
閑來無事,再開一坑,說是說從零學起,實際上就是分析官方例子,順便幫助大家總結一波,在此分析一下,不要拿我的博文作為自己的學習esp-idf的”教參“,
官方例程詳細注釋
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
/**
* 摘要:
* 此代碼展示了如何配置 gpio 以及如何使用 gpio 中斷,
*
* GPIO 狀態:
* GPIO18: 推挽輸出
* GPIO19: 推挽輸出
* GPIO4: 上拉輸入, 上升沿和下降沿中斷
* GPIO5: 上拉輸入, 下降沿中斷
*
* 實驗操作:
* GPIO18 連接到 GPIO4
* GPIO19 連接到 GPIO5
* 利用GPIO18/19產生信號,觸發GPIO4/5上的中斷
*
*/
#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); // 中斷專屬發送資訊給佇列的函式
}
// 實際起作用的gpio中斷處理函式,一直等待中斷發送資訊然后到這里處理資訊
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 = {
.intr_type = GPIO_INTR_DISABLE; //不啟用gpio中斷
.mode = GPIO_MODE_OUTPUT;//推挽輸出模式
.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;//設定goio,可以同時設定多個
.pull_down_en = 0;// 不下拉
.pull_up_en = 0;// 不上拉
}
gpio_config(&io_conf);
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_POSEDGE; //啟用下降沿中斷
.mode = GPIO_MODE_INPUT;//輸入模式
.pin_bit_mask = GPIO_INPUT_PIN_SEL;//設定goio,可以同時設定多個
.pull_down_en = 0;// 不下拉
.pull_up_en = 1;// 上拉
}
gpio_config(&io_conf);
//改變gpio中斷模式為任意邊沿中斷(上升沿和下降沿中斷)
gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);
//創建用于傳遞中斷資訊的訊息佇列
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
//開始gpio中斷處理執行緒
xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);
//安裝gpio中斷驅動(引數為優先級)
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
//給指定的gpio系結中斷服務函式
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);
// 列印記憶體使用情況
printf("Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size());
int cnt = 0;
while(1) {
printf("cnt: %d\n", cnt++);
vTaskDelay(1000 / portTICK_RATE_MS);
gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2); // 設定gpio電平
gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);
}
}
吐槽
本例程總的來說還不錯,只不過官方為了盡可能的展示自己的api在系結好gpio的中斷服務函式后,多此一舉的remove掉了,然后再次系結,

這里我刪掉了這個迷惑行為,還有就是為了大家看起來簡潔,我直接使用結構體宣告的時候賦值,其余的部分均參照官方例子
決議官方例子
實際上,這個最簡單的代碼,在沒有接觸過嵌入式系統的人來說會有點迷惑,我們如果玩裸機編程玩慣了,下意識的情況會選擇去直接寫中斷服務函式,實際上,中斷服務函式的作用是再外界的信號來的時候,打斷你手頭做的事情,去執行它的服務函式,如果該函式的時間很長,則會導致整個程式的其它東西被耽擱,而我們通過全域變數將變數保存后,丟個主回圈去處理,則會導致處理的不夠及時,整個系統的實時性會變得很差,于是乎,上了嵌入式系統后,我們就結合二者的情況,專門創建一個執行緒去做,當我們的中斷來臨的時候,把中斷資訊打包好,然后交給中斷處理的執行緒去處理這個資訊,由于執行緒之間的并發性,所以不會去耽擱別的執行緒,這個是不同于裸機的中斷編程思維,
總結

相關官方資料鏈接
gpio例程:https://github.com/espressif/esp-idf/tree/master/examples/peripherals/gpio/generic_gpio
gpio相關api:https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-reference/peripherals/gpio.html
freertos相關鏈接:https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-reference/system/freertos.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/293048.html
標籤:其他
上一篇:1.物聯網的基礎知識
