【寫在前面】最近在讀《ESP32-C物聯(lián)網(wǎng)開發(fā)實戰(zhàn)》,個人感覺書在這一塊講的并不太適合初學(xué)者。這里反過來,先通過實踐的方式實現(xiàn)功能,搞懂每一行代碼的實現(xiàn)機制之后,再去一個個研究硬件的原理和機制。
??如果還沒搭建好環(huán)境,或者碰到找不到頭文件的問題,以下指路:
環(huán)境搭建踩坑記http://t.csdn.cn/Z243W
目錄
一點必要的準(zhǔn)備知識
從點燈開始入坑
level1:光速點亮一顆燈
level2:點燈只是計劃的一部分——利用定時器控制
定時器句柄
結(jié)構(gòu)體初始化
部分API的實現(xiàn)過程梳理
level3:實現(xiàn)彩色光污染——利用PWM控制
拓展
?小結(jié)
一點必要的準(zhǔn)備知識
鏈接指路:
這是關(guān)于ESP32 C3最通用入門知識整理(此處先占坑,最近已經(jīng)在寫了)
從點燈開始入坑
level1:光速點亮一顆燈
? ? ? ? 一切就緒后,這里可以直接用例程,如果是默認安裝路徑的話,一般在C:\Espressif\frameworks\esp-idf-v4.3.2\examples\get-started,將blink文件用vscode打開即可,下面是blink.c的代碼:
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/gpio.h>
#include <sdkconfig.h>
#define BLINK_GPIO CONFIG_BLINK_GPIO//此處需要調(diào)整,下圖有兩種方法
void app_main(void)
{
gpio_reset_pin(BLINK_GPIO);
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
while(1) {
printf("Turning off the LED\n");
gpio_set_level(BLINK_GPIO, 0);
vTaskDelay(1000 / portTICK_PERIOD_MS);
printf("Turning on the LED\n");
gpio_set_level(BLINK_GPIO, 1);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
? ? ? ? 這段代碼還是比較好閱讀的,只采用了高低電平的方式控制LED,但是對于初學(xué)者仍有一些需要注意的地方,簡單的模塊也應(yīng)當(dāng)吃透。
?????????然后連接好開發(fā)板,依次按照以下三個步驟,設(shè)置好COM口,選擇對應(yīng)的型號,在使用一鍵編譯寫入和顯示按鈕即可
? ? ? ? 成功后你將得到一個閃爍的燈^ ^,由于每塊板子不一樣,這里以我手上的億研開發(fā)板為例
?????????僅需要將blink.c中的CONFIG_BLINK_GPIO 選擇10(代碼注釋那里我給了兩種方法),便可以實現(xiàn)。
level2:點燈只是計劃的一部分——利用定時器控制
? ? ? ? 肯定有細心的兄弟發(fā)現(xiàn)了Freertos中用來控制延時的函數(shù) vTaskDelay()?,雖然vTaskDelay易于上手和理解,但它功能也很笨【但是比delay()還是要高級點,不是做空循環(huán)(持續(xù)消耗cpu),而是把任務(wù)掛起和喚醒(中間不消耗cpu)】,不是控制周期任務(wù)頻率的好方法,其罪有二?qwq:
- 其它任務(wù)和中斷的活動會影響vTaskDelay()的調(diào)用頻率,甚至連其余模塊運行時間都會干擾到vTaskDelay()下一次調(diào)用的間隔時間
- 在vTaskDelay()的時間內(nèi),程序無法向后運行,相當(dāng)于被阻塞,除非只為了實現(xiàn)點燈,不然會影響整體的運行時間
? ? ? ? 所以此刻我們需要學(xué)習(xí)定時器來實現(xiàn)精準(zhǔn)的控制,所以在這個模塊中,我將分成led模塊和定時器模塊,因為上文主要拿紅燈作為舉例,所以為了簡化,我將綠燈和藍燈部分注釋掉了,如果想要加入的話稍加調(diào)整就可以實現(xiàn)了
//這是需要自己創(chuàng)建的led.h文件,請在文件根目錄下創(chuàng)建component文件夾并放入此文件
#ifndef _LED_H_
#define _LED_H_
#define LED_RED_IO 10 //這一塊對gpio口進行宏定義(上文有寫)
//#define LED_GREEN_IO 7
//#define LED_BLUE_IO 6
#define LED_ON 0
#define LED_OFF 1 //這一塊主要是控制亮滅的(上文提到的,低電平為亮)
void led_red(int on);
//void led_green(int on);
//void led_blue(int on);
//void initLed();
#endif
/*——————————————————————————————————————————————————————————————————*/
//下面是需要自己創(chuàng)建的led.c文件,請在文件根目錄下創(chuàng)建component文件夾并放入此文件
/*——————————————————————————————————————————————————————————————————*/
#include "driver/gpio.h"
#include "led.h"
void led_red(int on) //控制紅燈
{
if(on==LED_ON) gpio_set_level(LED_RED_IO, LED_ON);//開燈
else gpio_set_level(LED_RED_IO, LED_OFF);//關(guān)燈
}
/*
void led_green(int on) //控制綠燈
{
if(on==LED_ON) gpio_set_level(LED_GREEN_IO, LED_ON);//開燈
else gpio_set_level(LED_GREEN_IO, LED_OFF);//關(guān)燈
}
void led_blue(int on) //控制藍燈
{
if(on==LED_ON) gpio_set_level(LED_BLUE_IO, LED_ON);//開燈
else gpio_set_level(LED_BLUE_IO, LED_OFF);//關(guān)燈
}
*/
void initLed() //LED初始化
{
gpio_pad_select_gpio(LED_RED_IO);
//gpio_pad_select_gpio(LED_GREEN_IO);
//gpio_pad_select_gpio(LED_BLUE_IO);
gpio_set_direction(LED_RED_IO, GPIO_MODE_OUTPUT);
//gpio_set_direction(LED_GREEN_IO, GPIO_MODE_OUTPUT);
//gpio_set_direction(LED_BLUE_IO, GPIO_MODE_OUTPUT);
led_red(LED_OFF);
//led_green(LED_OFF);
//led_blue(LED_OFF);
}
? ? ? ? ?燈的控制還是比較好理解的,還是原來的高低電平控制,只是將level1中提到的部分重新做成了一個頭文件的形式。下面是定時器&主函數(shù)main.c部分的代碼:
#include <stdio.h>
#include "esp_types.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "soc/timer_group_struct.h"
#include "driver/periph_ctrl.h"
#include "driver/timer.h"
#include "esp_timer.h"
#include "led.h"
esp_timer_handle_t fw_timer_handle = 0; //定時器句柄,在創(chuàng)建任務(wù)之前,一般都指向0地址,下文會解釋
unsigned char led_flag=0; //LED燈標(biāo)志,用于閃燈
void fw_timer_cb(void *arg) //根據(jù)LED狀態(tài)閃燈
{
if(led_flag>0)
{
led_red(LED_ON);
led_flag=0;
}
else
{
led_red(LED_OFF);
led_flag=1;
}
}
void app_main()
{
initLed();//LED初始化
//定時器結(jié)構(gòu)體初始化↓↓
esp_timer_create_args_t fw_timer =
{
.callback = &fw_timer_cb, //回調(diào)函數(shù)
.arg = NULL, //參數(shù)
.name = "timer1" //定時器名稱
};
esp_err_t err = esp_timer_create(&fw_timer, &fw_timer_handle); //周期定時器啟動
err = esp_timer_start_periodic(fw_timer_handle, 1000 * 1000); //1秒回調(diào)一次
if(err == ESP_OK)
{
printf("timer1 cteate and start ok!\r\n");
}
}
其實代碼還是挺短的,沒有很復(fù)雜的地方,但是對初入門來說還是有很多問題存在的,比如:
定時器句柄
FreeRTOS經(jīng)常會用到句柄,簡而言之,句柄就是一個 void * 指針,指向了一塊內(nèi)存空間,這塊內(nèi)存才是我們需要的數(shù)據(jù)結(jié)構(gòu)。
結(jié)構(gòu)體初始化
部分API的實現(xiàn)過程梳理
|
? ? ? ? ?層級確實太豐富了,很多函數(shù)相互內(nèi)嵌,在整理的時候有點無從下手導(dǎo)致花了一些時間,其實對于初學(xué)者來說確實不容易定位和理解,這一塊建議直接查官方的編程指南,先從了解基本用法就可以,后續(xù)遇到需要深入調(diào)整和定制的時候再逐級研究。
? ? ? ? 總之,還是多看,多想,單一的教程或者指導(dǎo)確實很難顧及到具體的每一個問題。
? ? ? ? ?例程中遇到的函數(shù)基本在官方的編程指南中一些需要注意的地方和用法,可以在官網(wǎng)進行查詢,還是挺方便的。
level3:實現(xiàn)彩色光污染——利用PWM控制
? ? ? ? 一般常見的RGB燈模組占用3-5個GPIO口(R G B “冷光CW”?“暖光WW”),可以控制PWM的占空比來實現(xiàn)亮度、色溫、色彩的調(diào)節(jié)。以我使用的為例,占用了3個GPIO口(具體上文已經(jīng)介紹)。
? ? ? ? 這次咱們思路就比較清晰了,先了解主要使用的函數(shù)以及功能實現(xiàn)所需要的架構(gòu),這一塊書中講的還是不錯的,所以節(jié)選了一段《ESP32-C物聯(lián)網(wǎng)開發(fā)實戰(zhàn)》的內(nèi)容:
? ? ? ? ?按照書中的思路通過占空比的調(diào)節(jié),來實現(xiàn)漸變和交替顯示,多是一件美事。
? ? ? ? 下面是main.c的代碼,無需在component中添加.c.h文件,和書中的實現(xiàn)思路基本一致,由于是將例程修改和移植而來,所以如果是不同的板子或者想要實現(xiàn)不同的功能,還需要再做調(diào)整。
#include <stdio.h>
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"
#include "esp_event.h"
//定義LED燈的IO口
#define LED_RED_IO 10 //對應(yīng)紅燈的LED,綠燈為7,藍燈為6
#define LED_GREEN_IO 7 //對應(yīng)綠燈的LED,紅燈為10,藍燈為6
#define LED_BLUE_IO 6 //對應(yīng)藍燈的LED,紅燈為10,綠燈為7
#define LEDC_MAX_DUTY (8191) //2的13次方-1(13位PWM)
#define LEDC_FADE_TIME (1000) //漸變時間(ms)
#define PWM_RED_CHANNEL LEDC_CHANNEL_0 //定義紅燈通道
#define PWM_GREEN_CHANNEL LEDC_CHANNEL_1 //定義綠燈通道
#define PWM_BLUE_CHANNEL LEDC_CHANNEL_2 //定義藍燈通道
unsigned char pwm_mode=1; //PWM模塊,如果為1表示通過庫函數(shù)實現(xiàn)漸變功能
//ledc配置結(jié)構(gòu)體
ledc_channel_config_t g_ledc_red,g_ledc_green,g_ledc_blue;
void PWM_init(void)
{
//定時器配置結(jié)構(gòu)體
ledc_timer_config_t ledc_timer;
//定時器配置->timer0
ledc_timer.duty_resolution = LEDC_TIMER_13_BIT; //PWM分辨率
ledc_timer.freq_hz = 5000; //頻率
ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE; //速度
ledc_timer.timer_num = LEDC_TIMER_0; // 選擇定時器
ledc_timer.clk_cfg = LEDC_USE_APB_CLK;
ledc_timer_config(&ledc_timer); //設(shè)置定時器PWM模式
//PWM通道0配置->IO10->紅色燈
g_ledc_red.channel = PWM_RED_CHANNEL; //PWM通道
g_ledc_red.duty = LEDC_MAX_DUTY; //占空比
g_ledc_red.gpio_num = LED_RED_IO; //IO映射
g_ledc_red.speed_mode = LEDC_LOW_SPEED_MODE; //速度
g_ledc_red.timer_sel = LEDC_TIMER_0; //選擇定時器
ledc_channel_config(&g_ledc_red); //配置PWM
//PWM通道0配置->IO7->綠色燈
g_ledc_green.channel = PWM_GREEN_CHANNEL; //PWM通道
g_ledc_green.duty = LEDC_MAX_DUTY; //占空比
g_ledc_green.gpio_num = LED_GREEN_IO; //IO映射
g_ledc_green.speed_mode = LEDC_LOW_SPEED_MODE; //速度
g_ledc_green.timer_sel = LEDC_TIMER_0; //選擇定時器
ledc_channel_config(&g_ledc_green); //配置PWM
//PWM通道0配置->IO6->藍色燈
g_ledc_blue.channel = PWM_BLUE_CHANNEL; //PWM通道
g_ledc_blue.duty = LEDC_MAX_DUTY; //占空比
g_ledc_blue.gpio_num = LED_BLUE_IO; //IO映射
g_ledc_blue.speed_mode = LEDC_LOW_SPEED_MODE; //速度
g_ledc_blue.timer_sel = LEDC_TIMER_0; //選擇定時器
ledc_channel_config(&g_ledc_blue); //配置PWM
ledc_fade_func_install(0); //使能ledc漸變功能
}
//設(shè)置紅燈的PWM級別
//輸入level取值0~255
void CtrRBG_R(unsigned char level)
{
int duty=0;
if(level==255)
{
duty=LEDC_MAX_DUTY;
}
else if(level==0)
{
duty=0;
}
else
{
//計算占空比
duty=(level*LEDC_MAX_DUTY)/255;
}
ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_RED_CHANNEL, duty);//修改占空比
ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_RED_CHANNEL);//新的占空比生效
}
//設(shè)置綠燈的PWM級別
//輸入level取值0~255
void CtrRBG_G(unsigned char level)
{
int duty=0;
if(level==255)
{
duty=LEDC_MAX_DUTY;
}
else if(level==0)
{
duty=0;
}
else
{
//計算占空比
duty=(level*LEDC_MAX_DUTY)/255;
}
ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_GREEN_CHANNEL, duty);//修改占空比
ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_GREEN_CHANNEL);//新的占空比生效
}
//設(shè)置藍燈的PWM級別
//輸入level取值0~255
void CtrRBG_B(unsigned char level)
{
int duty=0;
if(level==255)
{
duty=LEDC_MAX_DUTY;
}
else if(level==0)
{
duty=0;
}
else
{
//計算占空比
duty=(level*LEDC_MAX_DUTY)/255;
}
ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_BLUE_CHANNEL, duty);//修改占空比
ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_BLUE_CHANNEL);//新的占空比生效
}
//通過漸變功能演示PWM任務(wù)
void task_pwm1(void *pvParameter)
{
int i=1;
while(1)
{
if(1==pwm_mode)
{
printf("pwm mode1 has run %d times.\r\n",i);
//漸變功能演示PWM
i++;
///
//紅燈占空比100%-->0%-->100%,時間2*LEDC_FADE_TIME
//紅燈:滅-->亮-->滅,的過程
///
//紅燈占空比100% 漸變至0%,時間LEDC_FADE_TIME
ledc_set_fade_with_time(g_ledc_red.speed_mode,
g_ledc_red.channel,
0,
LEDC_FADE_TIME);
//漸變開始
ledc_fade_start(g_ledc_red.speed_mode,
g_ledc_red.channel,
LEDC_FADE_NO_WAIT);
vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);
//紅燈占空比0%漸變至100%,時間LEDC_FADE_TIME
ledc_set_fade_with_time(g_ledc_red.speed_mode,
g_ledc_red.channel,
LEDC_MAX_DUTY,
LEDC_FADE_TIME);
//漸變開始
ledc_fade_start(g_ledc_red.speed_mode,
g_ledc_red.channel,
LEDC_FADE_NO_WAIT);
vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);
vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);
///
//綠燈占空比100%-->0%-->100%,時間2*LEDC_FADE_TIME
//綠燈:滅-->亮-->滅,的過程
//
//綠燈占空比100%漸變至0%,時間LEDC_FADE_TIME
ledc_set_fade_with_time(g_ledc_green.speed_mode,
g_ledc_green.channel,
0,
LEDC_FADE_TIME);
//漸變開始
ledc_fade_start(g_ledc_green.speed_mode,
g_ledc_green.channel,
LEDC_FADE_NO_WAIT);
vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);
//綠燈占空比0%漸變至100%,時間LEDC_FADE_TIME
ledc_set_fade_with_time(g_ledc_green.speed_mode,
g_ledc_green.channel,
LEDC_MAX_DUTY,
LEDC_FADE_TIME);
//漸變開始
ledc_fade_start(g_ledc_green.speed_mode,
g_ledc_green.channel,
LEDC_FADE_NO_WAIT);
vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);
vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);
///
//藍燈占空比100%-->0%-->100%,時間2*LEDC_FADE_TIME
//藍燈:滅-->亮-->滅,的過程
//
//藍燈占空比100%漸變至0%,時間LEDC_FADE_TIME
ledc_set_fade_with_time(g_ledc_blue.speed_mode,
g_ledc_blue.channel,
0,
LEDC_FADE_TIME);
//漸變開始
ledc_fade_start(g_ledc_blue.speed_mode,
g_ledc_blue.channel,
LEDC_FADE_NO_WAIT);
//延時LEDC_FADE_TIME,給LEDC控制時間
vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);
//藍燈占空比0%漸變至100%,時間LEDC_FADE_TIME
ledc_set_fade_with_time(g_ledc_blue.speed_mode,
g_ledc_blue.channel,
LEDC_MAX_DUTY,
LEDC_FADE_TIME);
//漸變開始
ledc_fade_start(g_ledc_blue.speed_mode,
g_ledc_blue.channel,
LEDC_FADE_NO_WAIT);
//延時LEDC_FADE_TIME,給LEDC控制時間
vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);
vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);
}
else{
vTaskDelay(5);//延時一下
}
}
}
void app_main()
{
PWM_init();//PWM初始化
xTaskCreate(&task_pwm1, "task_pwm1", 4096, NULL, 9, NULL);
}
拓展
? ? ? ? 怎么將上面的程序修改,實現(xiàn)從單色光源控制到模擬256*256*256色彩的變換?(可以自己先試試,后續(xù)會更新完整代碼和注釋,也可以在評論區(qū)交流)文章來源:http://www.zghlxwxcb.cn/news/detail-415062.html
?小結(jié)
? ? ? ? 通過將不同模塊和點燈結(jié)合,也算是拋磚引玉,在整理的過程中獲得了很多知識和靈感;本人新人一枚,之前在學(xué)STM32的時候,可以參考的教程和文章十分豐富,在CubeMx就可以實現(xiàn)所需要配置和調(diào)用的庫,然后在keil直接調(diào)用初始化函數(shù),自己寫子函數(shù)和主函數(shù)就可以了,所以上手還是很快的;而在ESP32的學(xué)習(xí)過程中,卻遇到了很多新的api、初始化設(shè)置、參數(shù)調(diào)用以及全新的編譯環(huán)境。所以在寫第一篇的時候也是一路摸黑,有時候一個問題看了很多資料仍然不太理解,但是摸著摸著也摸出了一些經(jīng)驗,點燈只是一個開始,下一章再和大家嘮嘮按鍵和中斷~文章來源地址http://www.zghlxwxcb.cn/news/detail-415062.html
到了這里,關(guān)于1·ESP32-C3入門教程——不止點亮一顆燈的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!