【寫在前面】經(jīng)過了點(diǎn)燈→定時器點(diǎn)燈→PWM點(diǎn)燈的學(xué)習(xí)之后,逐漸開始對ESP32 C3整體的框架有了一定認(rèn)識【?點(diǎn)燈模塊鏈接指路:http://t.csdn.cn/xOBmI】也掌握了一些理解和學(xué)習(xí)代碼的思路,這一章咱們聊一聊按鍵的控制。
目錄
GPIO輸出與按鍵控制
level 1:從一個樸實(shí)無華的點(diǎn)按開始
level 2:引入隊列、中斷——實(shí)現(xiàn)按鍵控制2.0
優(yōu)化代碼、引入隊列,實(shí)現(xiàn)多按鍵控制
通過線程的方式完成中斷
小結(jié)
GPIO輸出與按鍵控制
level 1:從一個樸實(shí)無華的點(diǎn)按開始
? ? ? ? 首先咱們了解下按鍵的硬件板塊(以我手上的板子為例),可以發(fā)現(xiàn)按鍵按下電路導(dǎo)通對應(yīng)低電平。
? ? ? ? 于是咱們可以使用最好理解的方法,從點(diǎn)按開始,光速上手按鍵模塊:
#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() //按鍵初始化(選擇和設(shè)置為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() //因?yàn)闊粼蹅円灿玫搅耍砸惨跏蓟?{
gpio_pad_select_gpio(LED_RED_IO);
gpio_set_direction(LED_RED_IO, GPIO_MODE_OUTPUT);
}
int key_read_key1(void) //一個很笨的方法,直到松開才觸發(fā)下一步,否則一直等待松開
{
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) //主函數(shù)一定要while(1),不然剛通電程序就結(jié)束了
{
if(key_read_key1()) gpio_set_level(LED_RED_IO, LED_ON);
if(key_read_key2()) gpio_set_level(LED_RED_IO, LED_OFF);
}
}
? ? ? ? 編譯,燒錄,監(jiān)視一氣呵成~ level 1輕松秒殺,那要不要試下長按呢?
????????想實(shí)現(xiàn)長按其實(shí)也挺簡單的,基于level1的代碼簡單魔改就能實(shí)現(xiàn),只需要引入"esp_system.h"中的函數(shù)esp_timer_get_time(),可以獲取內(nèi)置定時器當(dāng)時的時間,我們可以用這個作為flag,判斷低電平狀態(tài)和高電平的時間差。代碼如下:
#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();
}
}
? ? ? ? 恭喜,如果上面的代碼基本理解了,就已經(jīng)可以做出一些小的模塊了。但,這就滿足了嗎?
level 2:引入隊列、中斷——實(shí)現(xiàn)按鍵控制2.0
? ? ? ? 上面的方法用于實(shí)現(xiàn)按鍵控制,雖然很好理解,但在功能上總是不大理想的,如果只是考試還好,在一個具體的項(xiàng)目工程中,這種代碼比較冗余且不利于后續(xù)的拓展,此時就需要我們更進(jìn)一步,引入隊列、線程、中斷、回調(diào)的概念,來實(shí)現(xiàn)框架化、模塊化的按鍵控制2.0。
????????對于入門來說,需要注意的地方其實(shí)還是不少,但是有了level1 的信心,level2 開始上點(diǎn)難度總不過分吧qwq,本文的所有代碼我都統(tǒng)一了風(fēng)格,可以通過比較的方式看看為了實(shí)現(xiàn)新功能新增了什么,改動了什么。
優(yōu)化代碼、引入隊列,實(shí)現(xiàn)多按鍵控制
- 怎么樣實(shí)現(xiàn)按鍵不松手就能輸出一個按鍵長按?
- 拒絕重復(fù)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 //再用一個藍(lán)燈指示key2
#define LED_ON 0 //這個板子是共陽極,低電平亮
#define LED_OFF 1
static xQueueHandle gpio_evt_queue = NULL; //句柄地址先設(shè)置為0,點(diǎn)燈的文章中解釋過
void IRAM_ATTR gpio_isr_handler(void *arg) //創(chuàng)建了一個中斷回調(diào)函數(shù)
{
uint32_t gpio_num = (u_int32_t) arg;
xQueueSendFromISR(gpio_evt_queue,&gpio_num,NULL); //插入一個中斷到隊列中
}
void initLed() //因?yàn)闊粼蹅円灿玫搅?,所以也要初始?{
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; //上升下降沿中斷均可觸發(fā)
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); //上面是結(jié)構(gòu)體賦值,賦好值了這里就要調(diào)用了
gpio_evt_queue = xQueueCreate(2,sizeof(uint32_t)); //創(chuàng)建一個大小為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() //因?yàn)槲覀兩厦媾渲玫氖巧仙?、下降沿均可觸發(fā),所以按鍵按下就會立刻進(jìn)入中斷
{
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();
}
}
通過線程的方式完成中斷
? ? ? ? 實(shí)際工程中,我們當(dāng)然不可能把按鍵掃描放到主函數(shù)的循環(huán)中,因?yàn)榘存I的響應(yīng)速度會受到前面任務(wù)的影響,當(dāng)我們按下按鍵時,肯定希望的是立刻得到反饋,那么,怎樣實(shí)現(xiàn)不論程序進(jìn)行什么,只要按鍵按下就能立刻響應(yīng)??
? ? ? ? 經(jīng)過了前面的鋪墊,只需要在最后加入一個gpio_task_example(void* arg),并通過xTaskCreate調(diào)用即可,需要增添和修改的兩個部分如下圖所示:
? ? ? ? ?下載之后,我們在監(jiān)控中可以發(fā)現(xiàn),雖然while(1)中只做了日志打印,但是每當(dāng)按下按鍵的時候,中斷都會立刻響應(yīng)。
? ? ?
? ? ? ? 這個框架下想要加入和調(diào)整新功能都非常方便了(0 0總算搞定了),可以做成key.h和key.c,在根目錄下封裝到component里面。
小結(jié)
? ? ? ? 前期學(xué)習(xí)的時候,代碼一定不能CTRL C +V,最好理解之后自己手敲一遍,這一章全部都是手敲的,然后就發(fā)現(xiàn)了一些平常沒有注意到的問題,思路更加嚴(yán)謹(jǐn),也熟悉了整體的框架。
? ? ? ? 點(diǎn)燈和按鍵其實(shí)代表最基本的 input 和 output 的概念,而在我想要這個基礎(chǔ)上加入一點(diǎn)好玩的東西,通過層層遞進(jìn)的方式學(xué)到更多的概念,也許更加符合項(xiàng)目的需要和拓展。
????????從著手寫 "不止點(diǎn)亮一個燈" 到寫 "按鍵基本法" ,剛好是7天,春節(jié)期間經(jīng)常需要走親訪友,所以都是等到整塊的時間做的思考,畢竟一個完整的程序思路打斷了還是不好調(diào)整的。不過也挺好的,整段時間可以拿來研究一個完整的程序,零碎時間;可以學(xué)各種API的用法,還有補(bǔ)自己的漏洞(真的很多)有的時候不知不覺時間就過去了,也算是心流狀態(tài);這讓我保持了一個很好的心態(tài),碰到問題就記錄下自己的思路,學(xué)一段時間再回看,有時候忽然就理解了,再隔一段時間,甚至?xí)X得“怎么這么簡單的東西當(dāng)時都不會?”這是一個神奇的變化過程。文章來源:http://www.zghlxwxcb.cn/news/detail-418910.html
? ? ? ? 下一篇就要聊聊WIFI模塊了,畢竟還是要做物聯(lián)網(wǎng)板塊的,沒有wifi怎么行(●ˇ?ˇ●)文章來源地址http://www.zghlxwxcb.cn/news/detail-418910.html
到了這里,關(guān)于2·ESP32-C3入門教程——按鍵基本法的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!