2·ESP32-C3入门教程——按键基本法

【写在前面】经过了点灯→定时器点灯→PWM点灯的学习之后,逐渐开始对ESP32 C3整体的框架有了一定认识【 点灯模块链接指路:http://t.csdn.cn/xOBmI】也掌握了一些理解和学习代码的思路,这一章咱们聊一聊按键的控制。

目录

GPIO输出与按键控制

level 1:从一个朴实无华的点按开始

level 2:引入队列、中断——实现按键控制2.0

优化代码、引入队列,实现多按键控制

通过线程的方式完成中断

小结


GPIO输出与按键控制

level 1:从一个朴实无华的点按开始

        首先咱们了解下按键的硬件板块(以我手上的板子为例),可以发现按键按下电路导通对应低电平。

        于是咱们可以使用最好理解的方法,从点按开始,光速上手按键模块:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "sdkconfig.h"

#define KEY1_IO      9
#define KEY2_IO      8

#define LED_RED_IO 		10  //咱们借用红灯作为按键指示灯

#define LED_ON          0   //这个板子是共阳极,低电平亮
#define LED_OFF         1


void initKey()  //按键初始化(选择和设置为IO输入)
{
    gpio_pad_select_gpio(KEY1_IO);
    gpio_pad_select_gpio(KEY2_IO);
    gpio_set_direction(KEY1_IO, GPIO_MODE_INPUT);
    gpio_set_direction(KEY2_IO, GPIO_MODE_INPUT);
}

void initLed()   //因为灯咱们也用到了,所以也要初始化
{
    gpio_pad_select_gpio(LED_RED_IO);
    gpio_set_direction(LED_RED_IO, GPIO_MODE_OUTPUT);  
}


int key_read_key1(void)     //一个很笨的方法,直到松开才触发下一步,否则一直等待松开
{
    if(gpio_get_level(KEY1_IO)==0)
    {
        while (gpio_get_level(KEY1_IO)==0)
        {
            vTaskDelay(1);
        }
        return 1;
    }
    return 0;
}

int key_read_key2(void)     //同上
{
    if(gpio_get_level(KEY2_IO)==0)
    {
        while (gpio_get_level(KEY2_IO)==0)
        {
            vTaskDelay(1);
        }
        return 1;
    }
    return 0;
}

void main()
{
    initKey();
    initLed();      
    while(1)        //主函数一定要while(1),不然刚通电程序就结束了        
    {
        if(key_read_key1())      gpio_set_level(LED_RED_IO, LED_ON);
        if(key_read_key2())      gpio_set_level(LED_RED_IO, LED_OFF);
    }
}

        编译,烧录,监视一气呵成~ level 1轻松秒杀,那要不要试下长按呢?

        想实现长按其实也挺简单的,基于level1的代码简单魔改就能实现,只需要引入"esp_system.h"中的函数esp_timer_get_time(),可以获取内置定时器当时的时间,我们可以用这个作为flag,判断低电平状态和高电平的时间差。代码如下:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "esp_system.h"

#define KEY1_IO      9
#define KEY2_IO      8

#define LED_RED_IO 		10  

#define LED_ON          0    
#define LED_OFF         1


void initKey()  //和短按无变化,具体注释看短按
{
    gpio_pad_select_gpio(KEY1_IO);
    gpio_pad_select_gpio(KEY2_IO);
    gpio_set_direction(KEY1_IO, GPIO_MODE_INPUT);
    gpio_set_direction(KEY2_IO, GPIO_MODE_INPUT);
}

void initLed()   //相比短按无变化,具体注释看短按
{
    gpio_pad_select_gpio(LED_RED_IO);
    gpio_set_direction(LED_RED_IO, GPIO_MODE_OUTPUT);  
}

void key_read_key1(void)     // //相比短按,引入了flag和
{
    int flag = 0;
    if(gpio_get_level(KEY1_IO)==0)
    {
        flag = esp_timer_get_time();
        while(gpio_get_level(KEY1_IO)==0)
        {
            vTaskDelay(1);
        }
        flag = esp_timer_get_time() - flag;
        if(flag > 500*1000)  gpio_set_level(LED_RED_IO, LED_ON);
        else                 gpio_set_level(LED_RED_IO, LED_OFF);
    }
}


void app_main()
{
    initKey();
    initLed(); 
    while (1)
    {
        key_read_key1();
    }
}

        恭喜,如果上面的代码基本理解了,就已经可以做出一些小的模块了。但,这就满足了吗?

level 2:引入队列、中断——实现按键控制2.0

        上面的方法用于实现按键控制,虽然很好理解,但在功能上总是不大理想的,如果只是考试还好,在一个具体的项目工程中,这种代码比较冗余且不利于后续的拓展,此时就需要我们更进一步,引入队列、线程、中断、回调的概念,来实现框架化、模块化的按键控制2.0。

        对于入门来说,需要注意的地方其实还是不少,但是有了level1 的信心,level2 开始上点难度总不过分吧qwq,本文的所有代码我都统一了风格,可以通过比较的方式看看为了实现新功能新增了什么,改动了什么。

优化代码、引入队列,实现多按键控制

  • 怎么样实现按键不松手就能输出一个按键长按?
  • 拒绝重复if else ,如何简化多个按键之间的代码?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "freertos/queue.h"
#include "esp_system.h"     //多用了几个新的头文件
#include "sdkconfig.h"

#define KEY1_IO      9
#define KEY2_IO      8

#define LED_RED_IO 		10  //咱们借用红灯作为按键指示灯key1
#define LED_BLUE_IO     6   //再用一个蓝灯指示key2

#define LED_ON          0   //这个板子是共阳极,低电平亮
#define LED_OFF         1

static xQueueHandle gpio_evt_queue = NULL; //句柄地址先设置为0,点灯的文章中解释过

void IRAM_ATTR gpio_isr_handler(void *arg)  //创建了一个中断回调函数
{
    uint32_t gpio_num = (u_int32_t) arg;  
    xQueueSendFromISR(gpio_evt_queue,&gpio_num,NULL);   //插入一个中断到队列中
}

void initLed()   //因为灯咱们也用到了,所以也要初始化
{
    gpio_pad_select_gpio(LED_RED_IO);
    gpio_set_direction(LED_RED_IO, GPIO_MODE_OUTPUT);
    gpio_set_level(LED_RED_IO, LED_OFF);
    gpio_pad_select_gpio(LED_BLUE_IO);
    gpio_set_direction(LED_BLUE_IO, GPIO_MODE_OUTPUT);    
    gpio_set_level(LED_BLUE_IO, LED_OFF);
}

void initKey()  //这里和上面的代码变化很大,是另一种配置IO口的方法
{
    gpio_config_t io_conf;      //这边赋值的方式不够简洁,但是好理解
    io_conf.intr_type = GPIO_INTR_ANYEDGE;      //上升下降沿中断均可触发
    io_conf.pull_up_en = 1;     //上拉使能
    io_conf.mode = GPIO_MODE_INPUT;             //io口输入模式
    io_conf.pin_bit_mask = (1<<KEY1_IO) | (1<<KEY2_IO);  //通过操控寄存器的方式


    gpio_config(&io_conf); //上面是结构体赋值,赋好值了这里就要调用了

    gpio_evt_queue = xQueueCreate(2,sizeof(uint32_t));      //创建一个大小为2的队列

    gpio_install_isr_service(0);                                   //释放资源
    gpio_isr_handler_add(KEY1_IO,gpio_isr_handler,(void*)KEY1_IO); //初始化,返回esp_ok
    gpio_isr_handler_add(KEY2_IO,gpio_isr_handler,(void*)KEY2_IO);
}

void key_scan()    //因为我们上面配置的是上升沿、下降沿均可触发,所以按键按下就会立刻进入中断
{
    uint32_t io_num;
    bool     ret=0;
    xQueueReceive(gpio_evt_queue,&io_num,portMAX_DELAY);
    if(gpio_get_level(io_num)==0)
    {
        int flag=esp_timer_get_time();
        while(gpio_get_level(io_num)==0)
        {
            vTaskDelay(1);
            if(flag < esp_timer_get_time()-1000*1000)
            {
                gpio_set_level(LED_RED_IO, LED_ON);
                printf("按键长按\n");
                flag = esp_timer_get_time();
                ret = 1;
            }
        }
        if(ret == 0 && flag > esp_timer_get_time()-1000*1000)
            {
                gpio_set_level(LED_RED_IO, LED_OFF);
                printf("按键短按\n");
                ret = 0;
            }
    }

}

void app_main()
{  
    initKey();
    initLed();
    while(1)
    {
        vTaskDelay(1);
        key_scan();
    }
}

通过线程的方式完成中断

        实际工程中,我们当然不可能把按键扫描放到主函数的循环中,因为按键的响应速度会受到前面任务的影响,当我们按下按键时,肯定希望的是立刻得到反馈,那么,怎样实现不论程序进行什么,只要按键按下就能立刻响应? 

        经过了前面的铺垫,只需要在最后加入一个gpio_task_example(void* arg),并通过xTaskCreate调用即可,需要增添和修改的两个部分如下图所示:

         下载之后,我们在监控中可以发现,虽然while(1)中只做了日志打印,但是每当按下按键的时候,中断都会立刻响应。

     

        这个框架下想要加入和调整新功能都非常方便了(0 0总算搞定了),可以做成key.h和key.c,在根目录下封装到component里面。

小结

        前期学习的时候,代码一定不能CTRL C +V,最好理解之后自己手敲一遍,这一章全部都是手敲的,然后就发现了一些平常没有注意到的问题,思路更加严谨,也熟悉了整体的框架。

        点灯和按键其实代表最基本的 input output 的概念,而在我想要这个基础上加入一点好玩的东西,通过层层递进的方式学到更多的概念,也许更加符合项目的需要和拓展。

        从着手写 "不止点亮一个灯" 到写 "按键基本法" ,刚好是7天,春节期间经常需要走亲访友,所以都是等到整块的时间做的思考,毕竟一个完整的程序思路打断了还是不好调整的。不过也挺好的,整段时间可以拿来研究一个完整的程序,零碎时间;可以学各种API的用法,还有补自己的漏洞(真的很多)有的时候不知不觉时间就过去了,也算是心流状态;这让我保持了一个很好的心态,碰到问题就记录下自己的思路,学一段时间再回看,有时候忽然就理解了,再隔一段时间,甚至会觉得“怎么这么简单的东西当时都不会?”这是一个神奇的变化过程。

        下一篇就要聊聊WIFI模块了,毕竟还是要做物联网板块的,没有wifi怎么行(●ˇ∀ˇ●)

  • 25
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
esp32-c3入门教程 环境篇③——vs code ide快速入门是关于如何使用VS Code IDE进行ESP32-C3开发的教程。VS Code是一种功能强大且流行的集成开发环境,它提供了许多便捷工具和插件来简化代码编写和调试的过程。 首先,我们需要在电脑上安装VS Code。你可以从官方网站上下载并安装VS Code。 安装完成后,打开VS Code并点击"Extensions"图标,搜索并安装ESP-IDF插件。这个插件是为了和ESP32-C3开发框架进行集成而开发的。 接下来,我们需要配置ESP-IDF插件。首先,点击左侧的扩展图标,找到ESP-IDF插件并点击设置图标。然后,找到"espidf.espIdfPath"选项,将其设置为你的ESP-IDF框架的路径。 现在,我们可以开始创建一个新的ESP32-C3项目。点击"File"菜单,选择"New File",然后输入项目的名称。然后,点击"File"菜单中的"Save"来保存项目文件。 接下来,打开终端窗口,点击"Terminal"菜单,选择"New Terminal"。在终端中,输入以下命令来初始化ESP-IDF环境: idf.py set-target esp32c3 idf.py menuconfig 这将打开ESP-IDF的配置菜单,在这里你可以配置各种参数和选项。 配置完成后,我们可以开始编写和调试代码了。在VS Code中,点击左侧的扩展图标,找到ESP-IDF插件并点击设置图标。然后,点击"Build"按钮来构建项目。 构建完成后,我们可以使用VS Code的调试功能来调试代码。点击左侧的调试图标,然后点击"Add Configuration"按钮来添加一个调试配置。然后,点击"Debug"按钮来启动调试模式。 总之,使用VS Code IDE进行ESP32-C3开发是非常方便和高效的。它提供了许多工具和插件来简化开发流程,并提供了强大的调试功能来帮助我们调试代码。希望这个快速入门教程对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牧海Movesea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值