ESP32-GPIO带详细注释

原创不易,转载需注明来源:https://blog.youkuaiyun.com/qq_32955031

下面程序呢是乐鑫提供的GPIO使用例程,其中包括GPIO的输入输出设置,中断设置,当然避免不了ESP32基于的Free RTOS系统的一些内容,笔者呢,对其进行了一些注解,将一些函数的API解析文件整理了出来(中文的版本的),为了自己方便学习,在运行例程的基础上也对这个例程做了一些改变,对应自己整理处理出的文档实验,方便自己的理解。
下面就是解析后的源码了
 
/**
 * 本例程在Esp 官方例程上加以改动以及注释本人对本例程的理解
 * 例程功能见下方Brief(简介)
 * 本人联系方式QQ:1737410733
 * 本人优快云博客:https://blog.youkuaiyun.com/qq_32955031
 * 
*/

#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"

/**
 * Brief:
 * This test code shows how to configure gpio and how to use gpio interrupt.-此测试代码显示如何配置gpio以及如何使用gpio中断。
 *
 * GPIO status:
 * GPIO18: output
 * GPIO19: output
 * GPIO4:  input, pulled up, interrupt from rising edge and falling edge-输入,上拉,从上升沿和下降沿中断
 * GPIO5:  input, pulled up, interrupt from rising edge.-输入,上拉,从上升沿中断。
 *
 * Test:
 * Connect GPIO18 with GPIO4
 * Connect GPIO19 with GPIO5
 * Generate pulses on GPIO18/19, that triggers interrupt on GPIO4/5-在GPIO18 / 19上产生脉冲,触发GPIO4 / 5上的中断
 *
 */



#define GPIO_OUTPUT_IO_0    18                                                       //定义GPIO18为输出管脚0
#define GPIO_OUTPUT_IO_1    19                                                       //定义GPIO19为输出管脚1
#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))   //混合
#define GPIO_INPUT_IO_0     4                                                        //定义GPIO4为输入管脚0
#define GPIO_INPUT_IO_1     5                                                        //定义GPIO5为输入管脚1
#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;
    //向消息队列中投递消息,消息内容为GPIO的管脚号
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
//GPIO中断消息处理任务
static void gpio_task_example(void* arg)
{
    uint32_t io_num;
    for(;;) {
        //API函数:xQueueReceive,见本人整理文档之FreeRTOS篇【整理以最新版本最优】
        //这是一个调用xQueueGenericReceive()函数的宏。
        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()
{
    //申明GPIO的结构体
    gpio_config_t io_conf;
    //disable interrupt
    //禁用GPIO中断
    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    //set as output mode
    //GPIO设置为输出模式
    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配置,API函数:gpio_config 见本人整理文档之GPIO篇【整理版本以最新最优】
    gpio_config(&io_conf);

    //interrupt of rising edge
    //GPIO中断类型:上升沿
    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   
    //GPIO输入模式 
    io_conf.mode = GPIO_MODE_INPUT;
    //enable pull-up mode
    //势能上拉模式
    io_conf.pull_up_en = 1;
    //configure GPIO with the given settings
    //GPIO配置,API函数:gpio_config, 见本人整理文档之GPIO篇【整理版本以最新最优】   
    gpio_config(&io_conf);

    //change gpio intrrupt type for one pin
    //改变其中一个管教的中断管脚;API函数:gpio_set_intr_type,见本人整理文档之GPIO篇【整理版本以最新最优】  
    gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);

    //create a queue to handle gpio event from isr
    //创建一个队列来处理来自isr的gpio事件
    //创建新的队列实例,API函数:xQueueCreate,见本人整理文档之FreeRTOS篇
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    //start gpio task
    //创建任务,API函数;xTaskCreate 见本人整理文档之FreeRTOS篇【整理以最新版本最优】
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

    //install gpio isr service
    //安装驱动程序的GPIO ISR处理程序服务,该服务允许每个引脚的GPIO中断处理程序。
    //该API函数:gpio_install_isr_service,见本人整理文档之GPIO篇【整理版本以最新最优】
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    //hook isr handler for specific gpio pin
    //为相应的GPIO引脚添加ISR处理程序。
    //该API函数:gpio_isr_handler_add,见本人整理文档之GPIO篇【整理版本以最新最优】
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);

    //remove isr handler for gpio number.
    //删除相应GPIO引脚的ISR处理程序。
    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;
    while(1) {
        printf("cnt: %d\n", cnt++);
        //延时
        vTaskDelay(1000 / portTICK_RATE_MS);
        //设置RTC IO输出电平。
        gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2);
        gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);
    }
}

这个例程,理解起来并不复杂,总的功能就是把 GPIO 18以及GPIO 19设置为输出管脚,输出电平只有0或者1,即搞定电平,根据ESP32 的供电电压,高电平近3.3V ,低电平约为0V。GPIO 4 与GPIO 5为电平输入管脚,即只能体现输入电压为高电平还是低电平,这个应用可以用于 按键输入控制,根据实际电路情况去对应按键的输入输出情况。
需要注意的是基本GPIO(输入&输出)有上拉、下拉的配置,暂时我没有注意到默认是什么模式。如果有认真看过文档知道也可以评论说一下一起进步啊;
除了设置一些基本的GPIO模式外就是Free RTOS的的事情了,这部分我没有好好研读官方文件,之前了解过的也忘得差不多了。就暂时不误人子弟了。看注释就多多少少能理解了。
效果演示的话,参考下面图片。
在这里插入图片描述
因为这里延迟了1秒,即方波的频率为0.5HZ,高电平为3.3。

vTaskDelay(1000 / portTICK_RATE_MS);

这边显示的是串口信息,上电打印了部分系统信息外,还输出了GPIO的设置。
在这里插入图片描述
将GPIO4 连接到 GPIO19后的实验现象如下
在这里插入图片描述

<think>我们正在处理用户的问题:如何更换ESP32-CAM在使用Arduino IDE编程时的LED控制引脚,以避免GPIO4被占用。 根据引用[2],我们知道ESP32-CAM开发板上有一个正面的大颗粒LED(照明灯),通常由GPIO4控制。但有时我们可能需要使用GPIO4做其他事情(比如连接其他传感器或执行器),那么我们就需要将LED的控制引脚更换到其他GPIO上。 步骤: 1. **确定新的GPIO引脚**:首先,我们需要选择一个未被占用的GPIO引脚。ESP32-CAM的引脚图显示,除了GPIO4,我们还可以使用其他GPIO,例如GPIO2、GPIO12、GPIO13、GPIO14等(注意:有些引脚在启动时有特殊功能,应避免使用,比如GPIO0、GPIO2等可能影响启动模式)。这里,我们选择GPIO13作为新的LED控制引脚(因为GPIO13通常没有特殊启动功能,且不是关键引脚)。 2. **修改代码**:在Arduino代码中,将原来控制LED的引脚定义从GPIO4改为新的引脚(例如GPIO13)。同时,在`setup()`函数中设置该引脚为输出模式,并在需要控制LED的地方使用该引脚。 3. **硬件连接(可选)**:如果板载LED灯是焊接在GPIO4上的,那么我们需要断开这个连接,并将LED灯的正极(通过适当限流电阻)连接到新的GPIO引脚上。但通常,板载LED是固定连接的,所以如果必须更换控制引脚,可能需要外接一个LED灯到新的GPIO引脚上。如果用户只是希望使用另一个LED而不是板载的LED,那么这一步是必要的。 但是,根据引用[2],我们得知板载的LED是直接连接到GPIO4的。因此,如果我们想继续使用板载LED,就无法更换引脚(除非进行硬件改造)。如果用户希望避免使用GPIO4,那么只能使用外接的LED灯。 因此,我们提供两种方案: - 方案一:继续使用板载LED,但必须占用GPIO4(无法更换)。 - 方案二:使用外接LED,将其连接到其他GPIO引脚(例如GPIO13),然后通过代码控制这个外接LED。 由于用户要求更换LED控制引脚以避免GPIO4被占用,我们采用方案二。 **示例代码(控制外接LED)**: ```cpp // 定义新的LED控制引脚 #define LED_PIN 13 // 使用GPIO13 void setup() { pinMode(LED_PIN, OUTPUT); // 设置新引脚为输出 digitalWrite(LED_PIN, LOW); // 初始关闭 } void loop() { // 示例:让LED闪烁 digitalWrite(LED_PIN, HIGH); delay(1000); digitalWrite(LED_PIN, LOW); delay(1000); } ``` **硬件连接**: - 将外接LED的正极(长脚)通过一个220Ω的限流电阻连接到ESP32-CAM的GPIO13。 - 将LED的负极(短脚)连接到GND。 **注意事项**: 1. 避免使用以下引脚,因为它们可能用于摄像头模块或其他关键功能: - GPIO0:通常用于摄像头模块的XCLK信号(也可能用于启动模式选择) - GPIO2:通常用于摄像头模块的SIOD信号(I2C数据) - GPIO14:通常用于摄像头模块的SIC信号(I2C时钟) - GPIO15:通常用于摄像头模块的SIOC信号(I2C时钟) - 等等 具体可参考ESP32-CAM的引脚分配图。我们选择的GPIO13通常没有被摄像头占用,所以可以使用。 2. 如果用户同时使用摄像头和WiFi,应确保所选引脚不会干扰这些功能。 3. 如果用户必须使用板载LED,那么只能继续使用GPIO4,无法更换。 **相关问题**: 1. 如何通过手机APP控制外接LED灯? 2. ESP32-CAM的摄像头模块占用了哪些GPIO引脚? 3. 如何同时控制板载LED和外接LED? 4. 如何调整LED的亮度(使用PWM)? 以上,我们提供了更换LED控制引脚的解决方案。</think>### 如何更换ESP32-CAM的LED控制引脚(避免GPIO4占用) 在ESP32-CAM开发中,当GPIO4被其他功能占用时(如摄像头模块、传感器等),可通过以下步骤更换LED控制引脚: #### 一、确定替代引脚 1. **推荐备用引脚**(根据ESP32-CAM引脚功能): - `GPIO2`:通用IO,启动时需保持高电平 - `GPIO12`:无特殊限制 - `GPIO13`:安全选择(无启动限制) - `GPIO14`:需避免用于I²C场景 - `GPIO15`:启动时需保持高电平 - `GPIO33`:部分型号板载LED备用引脚[^2] 2. **避免冲突的引脚**: ```mermaid graph LR A[禁用引脚] --> B(GPIO0/启动模式选择) A --> C(GPIO1/TX0) A --> D(GPIO3/RX0) A --> E(GPIO16/PSRAM) ``` #### 二、硬件修改(可选) 1. **外接LED方案**(推荐): ```circuit ESP32-CAM 外接LED GPIO13 -----R[220Ω]-----|>|--- GND ``` 2. **板载LED改造**(需焊接): - 切断GPIO4到LED的电路 - 飞线连接新引脚到LED正极 #### 三、代码修改(Arduino IDE) ```cpp #include <WiFi.h> // 步骤1:修改LED引脚定义 // #define LED_PIN 4 // 原引脚注释掉 #define NEW_LED_PIN 13 // 新引脚(示例用GPIO13) void setup() { pinMode(NEW_LED_PIN, OUTPUT); // 步骤2:设置新引脚模式 digitalWrite(NEW_LED_PIN, LOW); // 保持原有WiFi和服务器代码不变 WiFi.softAP("ESP32-CAM", "12345678"); server.begin(); } void loop() { WiFiClient client = server.available(); if (client) { String command = client.readStringUntil('\r'); // 步骤3:更新控制指令对应的引脚 if (command == "LED_ON") { digitalWrite(NEW_LED_PIN, HIGH); // 使用新引脚 client.println("LED已开启"); } else if (command == "LED_OFF") { digitalWrite(NEW_LED_PIN, LOW); client.println("LED已关闭"); } client.stop(); } } ``` #### 四、关键注意事项 1. **启动配置**: - 使用GPIO2/GPIO15时,需确保启动时引脚为**高电平** - 上电瞬间低电平会导致启动失败 2. **摄像头兼容性**: - 避免使用摄像头专用引脚: ``` GPIO0 - XCLK GPIO12 - MTDI GPIO13 - MTCK GPIO14 - MTMS GPIO15 - MTDO ``` 3. **PWM调光**(如需亮度控制): ```cpp // 添加PWM功能 const int freq = 5000; const int ledChannel = 0; const int resolution = 8; void setup() { ledcSetup(ledChannel, freq, resolution); ledcAttachPin(NEW_LED_PIN, ledChannel); // 绑定新引脚 ledcWrite(ledChannel, 128); // 50%亮度 } ``` #### 五、验证步骤 1. 使用万用表检测引脚电压: - LED_ON时应输出3.3V - LED_OFF时应输出0V 2. 串口监视器检查指令响应: ``` [状态] 收到指令: LED_ON [执行] GPIO13输出HIGH ``` > **替代方案**:若需完全保留GPIO4,可考虑使用**I²C GPIO扩展器**(如PCA9685),通过SDA(GPIO2)/SCL(GPIO14)控制额外16路IO[^5]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值