ESP32-C3入门教程 基础篇(二、GPIO中断、按键驱动测试)

简介: 测试第二课,主要了解GPIO中断使用,和测试按键驱动
测试第二课,主要了解GPIO中断使用,和测试按键驱动

前言

接下来的ESP32-C3 功能测试都是基于自己设计的开发板:

自己画一块ESP32-C3 的开发板(第一次使用立创EDA)(PCB到手)

开发环境是乐鑫官方的 ESP-IDF, 基于VScode插件搭建好的:

ESP32-C3 VScode开发环境搭建(基于乐鑫官方ESP-IDF——Windows和Ubuntu双环境)

1、GPIO示例测试

在开发板上面,我们预留了2个按键,一个普通按键接口 GPIO7:
在这里插入图片描述
此外还有一个用于观察启动模式的按键 GPIO9

(设计目的是可以根据按下与不按下观察 ESP32-C3的不同启动模式,同时检测一下芯片启动后是否能够当做普通 GPIO 口使用):
在这里插入图片描述

1.1 GPIO基础测试

基础测试是基于 官方的generic_gpio示例新建工程:
在这里插入图片描述
针对自己的开发板进行代码调整:

  • 使用GPIO7 和 GPIO9 两个按键
  • 添加代码注释
  • 注释掉示例中的以IO口作为中断的输出源的对应部分
/* GPIO Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#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 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
 *
 * myboard GPIO7 ,  GPIO9(test)
 */

// #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_IO_0  7
#define GPIO_INPUT_IO_1  9
// #define GPIO_INPUT_PIN_SEL  1ULL<<GPIO_INPUT_IO_0
#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); //freertos中断中发送消息队列
}

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)
{
    /*
    typedef struct {
    uint64_t pin_bit_mask;          !< GPIO pin: set with bit mask, each bit maps to a GPIO 
    gpio_mode_t mode;               !< GPIO mode: set input/output mode                     
    gpio_pullup_t pull_up_en;       !< GPIO pull-up                                         
    gpio_pulldown_t pull_down_en;   !< GPIO pull-down                                       
    gpio_int_type_t intr_type;      !< GPIO interrupt type                                  
    } gpio_config_t;
    */

    gpio_config_t io_conf;
    //disable interrupt
    // io_conf.intr_type = GPIO_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_INTR_NEGEDGE; //按键下降沿
    //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_POSEDGE);//单独改变某个IO口的中断设置

    //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
    This function is incompatible with gpio_isr_register() - if that function is used, 
    a single global ISR is registered for all GPIO interrupts. 
    If this function is used, 
    the ISR service provides a global GPIO ISR and individual pin handlers are registered via the gpio_isr_handler_add() function.
    */
    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);
    //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_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);

    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);//使用了FreeRTOS,这里必须要有延时,这里的while(1)也类似一个任务
        // gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2);
        // gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);
    }
}

测试结果基本正常,但是有一个小问题,就是对于按键中断类型设置为下降沿,结果却是按下和弹起都会触发,结果如下:
在这里插入图片描述
初步推测设置为下降沿触发(按下弹起都会引起中断),是因为 硬件电路设计的时候加了防抖动的电容的原因,为了检测这个问题,做了一个测试:

使用官方原来的那种方式,使用 GPIO 口 作为中断源,上面示例代码中注释掉的的初始化输出的正常使用:
在这里插入图片描述
然后看一下测试效果,在设置为GPIO_INTR_NEGEDGE的时候,下降沿触发,触发后值为0:
在这里插入图片描述
在设置为GPIO_INTR_POSEDGE的时候,上升沿触发,触发后值为1:在这里插入图片描述
测试完成,一切是正常的。

1.1.1 不额外创建FreeRTOS任务测试

但是对于示例中,使用了 FreeRTOS 的相关组件,但实际上并没有使用开始调度的函数(这句话是错误的),对于这点,目前还是不太习惯。

其实为了这点,还单独测试了一下不用消息队列,直接按照以前 STM32 系列芯片的方式,在中断中直接至位标志位或者简单的操作,延时函数还是用了vTaskDelay。
(printf打印一般不能在中断处理函数中使用,只能作为测试,这里确的确是在中断中使用了printf导致了错误):
在这里插入图片描述
程序编译运行倒是没有什么问题,就是一旦触发中断,就会报错复位:
在这里插入图片描述
上面的程序报错,考虑到可能是因为中断中使用了printf函数,所以稍微修改了一下:
在这里插入图片描述
测试按键GPIO7点亮LED灯, GPIO9关闭LED灯。测试正常!

1.1.2 ESP32-C3应用程序启动流程(更正上面错误说法!)

对于文中以前提到的使用了FreeRTOS ,但是没有开始调度的说法,是错误的!进行改正!

函数app_mian其实只是一个FreeRTOS的一个任务! 系统跑到app_mian的时候早就开始了调度!!!

在乐鑫官方,有详细的启动流程说明:乐鑫官方对于ESP32-C3 应用程序的启动流程 说明

在这里插入图片描述
FreeRTOS调度器会在组件初始化完成以后开启!
这才是为什么在示例中可以创建任务而且能够执行的原因!

在这里插入图片描述

1.2 按键驱动测试

在GItee仓库上有一个大佬的 基于ESP32-C3 的开源项目:

wumei-esp32-c3 Demo GItee 地址

在这个工程中有一个按键驱动,觉得非常好用,所以拿来测试一下。我们使用blink.c样例来添加一下这个驱动进行测试:

现在我们还不熟悉 ESP-IDF 下面的工程结构,如何添加自己的驱动文件,这个后面会单独用一篇文章来介绍

所以我们下面测试操作完一步就得编译一下,防止出错不知道怎么解决。首先,上面的例程下载下来是能够直接编译通过的:
在这里插入图片描述

1.2.1 驱动移植

标题虽然是驱动移植,其实就是拷贝一份 = =!。首先在main同目录下面新建一个components文件夹
(名字不能是别的,因为对于这个components 名字的文件夹 SDK 好像有支持,当然自己的驱动用别的文名字也是可以的,只是修改的地方会比较多,在我们不熟悉的情况下,还是少做修改):
在这里插入图片描述

然后我们将示例中components文件夹下的button文件夹直接拷贝过来,当然也包括文件夹里面的配置文件等:
在这里插入图片描述

复制过来以后编译一下 BLINK 工程(复制过来的文件对工程并无影响):
在这里插入图片描述
接下来在main文件夹下面新建一个my_button.c文件作为按键测试(下图中的注释,头文件需要额外多添加一些,具体可以看下面我放的修改的源码):
在这里插入图片描述
my_button.c文件中的内容是参考示例工程中drv_button.c文件:
在这里插入图片描述
我把简单拷贝修改的源码放一下:

#include <string.h>
#include "esp_log.h"
#include "iot_button.h"
#include "driver/gpio.h"
#include "button_gpio.h"
#include "esp_log.h"


static const char* TAG = "my_button";

#define IO_SWITCH_BUTTON 7

static void button_single_click_cb(void *arg){
    uint32_t gpio_num = (uint32_t) arg;
    ESP_LOGI(TAG, "BTN%d: BUTTON_SINGLE_CLICK\n", gpio_num);
}

static void button_long_press_start_cb(void *arg){
    uint32_t gpio_num = (uint32_t) arg;
    ESP_LOGI(TAG, "BTN%d: BUTTON_LONG_PRESS_START\n", gpio_num);
}

static void button_press_repeat_cb(void *arg){
    uint32_t gpio_num = (uint32_t) arg;
    ESP_LOGI(TAG, "BTN%d: BUTTON_PRESS_REPEAT\n",gpio_num);
}

void button_start()
{
    //初始化按键
    button_config_t cfg = {
        .type = BUTTON_TYPE_GPIO,
        .gpio_button_config = {
            .gpio_num = IO_SWITCH_BUTTON,
            .active_level = 0,
        },
    };
    button_handle_t gpio_btn = iot_button_create(&cfg);
    if(NULL == gpio_btn) { ESP_LOGE(TAG, "Button create failed"); }
    iot_button_register_cb(gpio_btn, BUTTON_SINGLE_CLICK, button_single_click_cb);            //短按
    iot_button_register_cb(gpio_btn, BUTTON_LONG_PRESS_START, button_long_press_start_cb);    //长按
    iot_button_register_cb(gpio_btn, BUTTON_PRESS_REPEAT, button_press_repeat_cb);            //连续短按
}

上面我们增加了一个.c 文件,直接编译工程还是没有问题的,但是要用起来,还得新建一个.h文件:
在这里插入图片描述

1.2.2 测试结果

需要修改的代码移植完毕,接下来在blink.c文件中包含以下这个头文件,调用button_start();函数:
在这里插入图片描述
编译通过烧录.......

最终改完后建议全清除一下,再次编译

测试结果,还是让人满意的(代码中直接复制的,没注意,把 arg 地址打出来了,应该是*arg的,不要在意这些细节= =!):
在这里插入图片描述
额外说明:如果不是用的 VScode 中的插件开发,可能修改完代码后是自己需要修改配置文件和重新配置环境变量的,使用插件好像会自动进行配置好,比如,编译完成后的CMakeLists.txt文件自动更新了:
在这里插入图片描述

2、 ESP32-C3 GPIO相关介绍

对于ESP32-C3 GPIO的介绍,在乐鑫的官网有很详细的说明,官方链接如下:

乐鑫官方ESP32-C3 GPIO部分说明

2.1 ESP32-C3 GPIO基础

实际上在我的博文 自己画一个ESP32-C3的开发板中,也有关于GPIO的说明,这里根据官方说明,简单介绍一下:

  1. ESP32-C3 一共22个GPIO口,其中 GPIO2、GPIO8、GPIO9决定着芯片的启动模式;

在这里插入图片描述

  1. GPIO12-17 用于 SPI flash 和 PSRAM,不建议用作其他功能;

在这里插入图片描述

  1. GPIO18 和 GPIO19 默认作为 USB-JTAG. 要把他们当做普通 GPIO,需要进行设置;

在上面的示例中,其实使用了 GPIO18 和 GPIO19 当做普通IO口,作为输出使用;

  1. 在深度睡眠模式下可以使用的GPIO口有 GPIO0-5.

在这里插入图片描述

2.2 ESP32-C3 GPIO函数

GPIO的函数,在官方文档也都都介绍,在工程文件中#include "driver/gpio.h"头文件中,也能全部看到, 总的来说 GPIO的操作是比较基础而且简单的。

相对于以前使用过 STM32 芯片的人来说,只需要了解一下 与中断有关的 带有 _isr 字样的函数,和更睡眠有关的带有_sleep字样的函数。
在这里插入图片描述GPIO使用还是比较简单,就不多废话了,需要用到什么函数,通过测试一下就能明白。

相关文章
|
20天前
|
机器学习/深度学习 人工智能 自然语言处理
智能化软件测试:AI驱动的自动化测试策略与实践####
本文深入探讨了人工智能(AI)在软件测试领域的创新应用,通过分析AI技术如何优化测试流程、提升测试效率及质量,阐述了智能化软件测试的核心价值。文章首先概述了传统软件测试面临的挑战,随后详细介绍了AI驱动的自动化测试工具与框架,包括自然语言处理(NLP)、机器学习(ML)算法在缺陷预测、测试用例生成及自动化回归测试中的应用实例。最后,文章展望了智能化软件测试的未来发展趋势,强调了持续学习与适应能力对于保持测试策略有效性的重要性。 ####
|
1月前
|
机器学习/深度学习 数据采集 人工智能
探索AI驱动的自动化测试新纪元###
本文旨在探讨人工智能如何革新软件测试领域,通过AI技术提升测试效率、精准度和覆盖范围。在智能算法的支持下,自动化测试不再局限于简单的脚本回放,而是能够模拟复杂场景、预测潜在缺陷,并实现自我学习与优化。我们正步入一个测试更加主动、灵活且高效的新时代,本文将深入剖析这一变革的核心驱动力及其对未来软件开发的影响。 ###
|
2月前
|
存储 测试技术 数据库
数据驱动测试和关键词驱动测试的区别
数据驱动测试 数据驱动测试或 DDT 也被称为参数化测试。
35 1
|
7月前
|
机器学习/深度学习 人工智能 自然语言处理
探索软件测试的未来:AI 驱动的自动化测试方法
【5月更文挑战第29天】随着人工智能(AI)技术的不断发展和成熟,其在软件测试领域的应用也日益广泛。本文旨在探讨 AI 如何改变软件测试的面貌,特别是自动化测试方法。我们将分析当前自动化测试的挑战,并介绍 AI 如何提供解决方案,包括智能化测试用例生成、测试执行优化、以及结果分析等。通过实际案例研究,我们还将讨论 AI 在提高测试效率、减少错误和提升软件质量保障中的作用。最后,文章将预测 AI 在自动化测试领域的未来趋势,并提出对测试工程师的建议。
|
3月前
|
机器学习/深度学习 人工智能 自然语言处理
AI驱动的自动化测试:提升软件质量的未来之路
【9月更文挑战第3天】AI驱动的自动化测试是提升软件质量的未来之路。它借助AI技术的力量,实现了测试用例的智能生成、测试策略的优化、故障预测与定位等功能的自动化和智能化。随着技术的不断进步和应用场景的不断拓展,AI驱动的自动化测试将在未来发挥更加重要的作用,为软件开发和运维提供更加高效、准确和可靠的解决方案。
|
4月前
|
机器学习/深度学习 人工智能 自然语言处理
探索软件自动化测试的未来:AI驱动的测试策略
【7月更文挑战第47天】 随着人工智能(AI)技术不断进步,其在软件测试领域的应用也日益广泛。本文将探讨如何整合AI技术与现有的自动化测试流程,提出一个面向未来的测试策略。文章重点分析了AI在测试用例生成、执行、结果分析和持续集成中的作用,同时预测了这种技术融合对测试工程师角色的影响,以及它如何提高软件测试的效率和准确性。
|
4月前
|
Java 测试技术
单元测试问题之想通过单元测试来驱动代码的设计与重构,如何实现
单元测试问题之想通过单元测试来驱动代码的设计与重构,如何实现
|
6月前
|
Web App开发 IDE Java
自动化测试谷歌浏览器和其驱动版本差不多却还是报错The chromedriver version (121.0.6167.184) detected in PATH at DPythonchromed
自动化测试谷歌浏览器和其驱动版本差不多却还是报错The chromedriver version (121.0.6167.184) detected in PATH at DPythonchromed
140 2
|
5月前
|
机器学习/深度学习 人工智能 自然语言处理
探索软件测试的新篇章:AI驱动的测试自动化
【7月更文挑战第10天】随着人工智能技术的蓬勃发展,软件测试领域迎来了革命性的变革。本文将深入探讨AI技术如何重塑软件测试流程,提升测试效率和准确性。从AI在测试用例生成、缺陷预测、测试执行以及结果分析的应用出发,我们将揭示AI技术如何使测试更加智能化、精准化,并展望AI在软件测试领域的未来发展趋势。
51 0
|
6月前
|
机器学习/深度学习 人工智能 算法
探索软件测试的新时代:AI驱动的自动化
【6月更文挑战第4天】随着人工智能技术的不断进步,软件测试领域正经历着一场革命。本文将探讨AI如何改变传统的软件测试方法,提高测试效率和准确性,以及这一趋势对测试工程师未来技能要求的影响。
57 6