乐鑫Esp32学习之旅③ 认识并学习使用esp32的GPIO接口,开始点亮您的第一盏 LED和中断回调实现按键功能 。(附带Demo)



一 、前言;

  • GPIO口一直是单片机的主要功能,今天小徐带来的是正是GPIO使用;本博文使用的是安信可的esp32s模组;
  • 本篇主要学习了怎么使用esp32的GPIO口,包括高低电平输入、高低电平输出和GPIO的中断使用;

二 、输出低电平,点亮一盏LED

  • 电路图接法如下图:

这里写图片描述


  • 第一种方法,较为简单:

gpio_pad_select_gpio(GPIO_NUM_16);//选择一个GPIO
gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT);//把这个GPIO作为输出
gpio_set_level(BLINK_GPIO, 0);//把这个GPIO输出低电平

  • 第二种方法,使用结构体来定义:
    gpio_config_t io_conf;
    //进制中断
    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    //选择为输出模式
    io_conf.mode = GPIO_MODE_OUTPUT;
    //配置GPIO_OUT寄存器
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
    //禁止下拉
    io_conf.pull_down_en = 0;
    //禁止上拉
    io_conf.pull_up_en = 0;
    //最后配置使能
    gpio_config(&io_conf);

  • 输出低电平:
gpio_set_level(BLINK_GPIO, 0); //第一个参数是GPIO,第二个是0或1

三 、获取某个GPIO的电平,并且打印出来;


  • 如果你要获取当前的电平状态,请把此配置io_conf.mode模式为GPIO_MODE_INPUT,表示为输入模式;见下面的源码可看到有多个输入输出模式,还有把此GPIO设置为不可用!可见esp32API丰富啊!

这里写图片描述

  • 下面的代码示范效果为: 每时隔 500ms读取GPIO16的输入电平状态,并且打印出来!
    //第一种方式配置
    //gpio_pad_select_gpio(BLINK_GPIO);
    //gpio_set_direction(BLINK_GPIO, GPIO_MODE_INPUT);

    //第二种方式配置
    gpio_config_t io_conf;
    //进制中断
    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    //选择为输出模式
    io_conf.mode = GPIO_MODE_INPUT;
    //配置GPIO_OUT寄存器
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
    //禁止下拉
    io_conf.pull_down_en = 0;
    //禁止上拉
    io_conf.pull_up_en = 0;
    //最后配置使能
    gpio_config(&io_conf);

    // 挂起500ms
    const portTickType xDelay = 500 / portTICK_RATE_MS;

    while (1) {
        printf(" Current Gpio16 Level is : %d \r\n\r\n",
                gpio_get_level(BLINK_GPIO));
        vTaskDelay(xDelay);
    }

这里写图片描述


四 、配置某个GPIO的低/高电平触发事件,并且打印出来;


4.1:下降沿触发中断:

这里写图片描述


  • 下面的代码实现的效果的是:中断触发按键按下来,触发中断,执行回调函数;
    • 注意①:中断触发按键连接的是GNDGPIO14,因为是下降沿触发,所以必须一边接地!!
    • 注意②:代码中要设置输入模式,不下拉,内部上拉!原因在于内部上拉,就是高电平短脚,可以检测到下降沿。

    //GPIO口结构体定义
    gpio_config_t io_conf;
    //下降沿触发中断方式
    io_conf.intr_type = GPIO_INTR_NEGEDGE;
    //选择为输出模式
    io_conf.mode = GPIO_MODE_INPUT;
    //配置GPIO_OUT寄存器
    io_conf.pin_bit_mask = GPIO_SEL_4;
    //内部不下拉
    io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
    //内部上拉
    io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
    //最后配置使能
    gpio_config(&io_conf);

4.2:上升沿触发中断:

这里写图片描述


  • 下面的代码实现的效果的是:中断触发按键按下来,触发中断,执行回调函数;
    • 注意①:中断触发按键连接的是VCCGPIO14,因为是上升沿触发,所以必须一边接高电平!!
    • 注意②:代码中要设置输入模式,不上拉,内部下拉!原因在于内部下拉时候,就是低电平短脚输入,可以检测到上升沿。

    //GPIO口结构体定义
    gpio_config_t io_conf;
    //上升沿触发
    io_conf.intr_type = GPIO_INTR_POSEDGE;
    //选择为输出模式
    io_conf.mode = GPIO_MODE_INPUT;
    //配置GPIO_OUT寄存器
    io_conf.pin_bit_mask = GPIO_SEL_4;
    //内部下拉
    io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
    //禁止上拉
    io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
    //最后配置使能
    gpio_config(&io_conf);

  • 【其他】中断类型枚举:
GPIO_INTR_DISABLE //禁用GPIO中断
GPIO_INTR_POSEDGE //GPIO中断类型:上升沿
GPIO_INTR_NEGEDGE //下降沿
GPIO_INTR_ANYEDGE //上升沿和下降沿
GPIO_INTR_LOW_LEVEL //输入低电平触发
GPIO_INTR_HIGH_LEVEL //输入高电平触发

四 、配置某个GPIO的低/高电平触发事件,并且打印出来;


这里写图片描述


  • 嵌入式系统rtos的强大之处可以自行调度任务的优先级、任务的自由切换,最大程度的省下了MCU的空间资源。我也是初入rtos这趟深水,以上的图片是我个人的见解,如果有错,请留言,我立刻纠正!

    过程如下:

    • ①:中断发生的中断,中断回调方法发送一个消息队列,消息队列包含了GPIO对应的端口号!
    • ②:另外一个任务不断从这个消息队列的句柄中获取消息并且可以做其他操作!
    • ③:上面可以看到,这是一个异步的操作,其中在串口中断的回调函数中,是加载在IRAM_ATTR中,注意连个简单的printf()函数调用都会报错。

  • 以下为完整的代码:
#include <stdio.h>
#include "esp_types.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"

//宏定义一个中断端口
#define GPIO_INPUT_IO_0     4
static xQueueHandle gpio_evt_queue = NULL; //定义一个队列返回变量

void IRAM_ATTR gpio_isr_handler(void* arg) {
    //把中断消息插入到队列的后面,将gpio的io参数传递到队列中
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

//低电平触发的回调方法
void gpio_low_interrupt_callBack(void* arg) {
    printf(" \r\n into gpio_low_interrupt_callBack ...\r\n  ");
    uint32_t io_num;
    while (1) {
        //不断读取gpio队列,读取完后将删除队列
        if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
            printf("GPIO[%d] 中断触发, 当前的电压: %d\n", io_num,
                    gpio_get_level(io_num));
        }
    }
}

void fun_set_gpio_low_interrupt() {

    //GPIO口结构体定义
    gpio_config_t io_conf;
    //下降沿触发
    io_conf.intr_type = GPIO_INTR_NEGEDGE;
    //选择为输出模式
    io_conf.mode = GPIO_MODE_INPUT;
    //配置GPIO_OUT寄存器
    io_conf.pin_bit_mask = GPIO_SEL_4;
    //设置下拉
    io_conf.pull_down_en = 0;
    //设置上拉
    io_conf.pull_up_en = 1;
    //最后配置使能
    gpio_config(&io_conf);

    //注册中断服务
    gpio_install_isr_service(1);
    //设置GPIO的中断回调函数
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler,
            (void*) GPIO_INPUT_IO_0);

    //创建一个消息队列,从中获取队列句柄
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));

    //新建队列的
    xTaskCreate(gpio_low_interrupt_callBack //任务函数
            , "gpio_task_example" //任务名字
            , 2048  //任务堆栈大小
            , NULL  //传递给任务函数的参数
            , 10   //任务优先级
            , NULL); //任務句柄

}

这里写图片描述


猜你喜欢

转载自blog.csdn.net/xh870189248/article/details/80354565