esp32型号:
环境搭建
安装:就按这个来,别的试了好多次都不行,这个一次成功!!!!
vscode下ESP32开发环境配置(100%成功)_哔哩哔哩_bilibili
esp芯片的两种模式:
1.运行模式
2.下载模式
esp32s3程序下载
1.数据线插在TTL的那个口,下载过程中不用按什么按键
2.
3.点击小火花一键编译下载
ESP32的启动流程
ESP32_freeRTOS教程三:系统启动流程_哔哩哔哩_bilibili
注意:app_main()是在一个任务中被调用的,它是任务的一部分,与所以里面没有死循环,在执行app_main()之前就开启了任务调度器了
任务的创建与删除
实验现象:首先创建my_task任务,系统会输出十次my_task1这个任务的最小剩余堆栈大小,之后调用vTaskDelete()删除m_task1这个任务
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
void my_task1(void *param)
{
while (1)
{
UBaseType_t stack_remain = uxTaskGetStackHighWaterMark(NULL);
//显示及最小栈大小
printf("Stack remaining: %d\n", stack_remain);
//fflush(stdout); // 手动刷新缓冲区
vTaskDelay(50);
}
}
void app_main(void)
{
TaskHandle_t my_task1_handle=NULL;
xTaskCreate(my_task1, "my_task1", 3000, NULL, 3, &my_task1_handle);
vTaskDelay(500);
if(my_task1_handle!=NULL)
{
vTaskDelete(my_task1_handle);
}
}
注意:gcc的printf默认需要换行符\n才会输出,或者等缓冲区满了才输出,keil没有这个问题
任务创建时的输入参数
传给任务的参数类型时void *类型,void *类型它可以接收任何类型,
传递整数型
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
int num=9;
void my_task1(void *param)
{
int *tem=(int *)param;
while (1)
{
printf("get num: %d\n", *tem);
vTaskDelay(100);
}
}
void app_main(void)
{
TaskHandle_t my_task1_handle=NULL;
xTaskCreate(my_task1, "my_task1", 3000, (void *)&num, 3, &my_task1_handle);
}
传递数组:
传递结构体
传递字符串:
vTaskList()的使用
static char pcWriteBuffer[512]={0};
vTaskList(pcWriteBuffer);
printf("%s",pcWriteBuffer);
输出格式:
State表示:
Stack表示:最小剩余的堆栈大小
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
void my_task1(void *param)
{
while (1)
{
UBaseType_t stack_remain = uxTaskGetStackHighWaterMark(NULL);
//显示及最小栈大小
printf("my_task1 Stack: %d\n", stack_remain);
//fflush(stdout); // 手动刷新缓冲区
vTaskDelay(500/portTICK_PERIOD_MS);
}
}
void my_task2(void *param)
{
while (1)
{
UBaseType_t stack_remain = uxTaskGetStackHighWaterMark(NULL);
//显示及最小栈大小
printf("my_task2 Stack: %d\n", stack_remain);
//fflush(stdout); // 手动刷新缓冲区
vTaskDelay(500/portTICK_PERIOD_MS);
}
}
void app_main(void)
{
TaskHandle_t my_task1_handle=NULL;
TaskHandle_t my_task2_handle=NULL;
xTaskCreate(my_task1, "my_task1", 3000, NULL, 10, &my_task1_handle);
xTaskCreate(my_task2, "my_task2", 3000, NULL, 10, &my_task2_handle);
static char pcWriteBuffer[512]={0};
vTaskList(pcWriteBuffer);
printf("%s",pcWriteBuffer);
vTaskDelay(2000/portTICK_PERIOD_MS);
}
看门狗
中断看门狗:
当中断程序执行过长时间时可能会触发中断看门狗
任务看门狗:
任务看被狗是今天重点;
使用任务看门狗需要包含头文件: #include "esp_task_wdt.h"
#include "esp_task_wdt.h"
void my_task1(void *param)
{
//将任务添加到任务看门狗所监控的列表
esp_task_wdt(NULL);
while (1)
{
//喂狗
esp_task_wdt_reset();
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
TaskHandle_t my_task1_handle = NULL;
xTaskCreate(my_task1, "my_task1", 3000, NULL, 1, &my_task1_handle);
}
点亮一颗LED
led的引脚连接在了G19引脚
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#define LED_GPIO GPIO_NUM_19
void task_led(void *param)
{
int gpio_level = 0;
while(1)
{
gpio_level=gpio_level?0:1;
gpio_set_level(GPIO_NUM_19,gpio_level);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void app_main(void)
{
gpio_config_t led_cfg;
led_cfg.pin_bit_mask=(1<<GPIO_NUM_19);
led_cfg.mode=GPIO_MODE_OUTPUT;
led_cfg.intr_type=GPIO_INTR_DISABLE;
led_cfg.pull_down_en=GPIO_PULLDOWN_DISABLE;
led_cfg.pull_up_en=GPIO_PULLUP_DISABLE;
gpio_config(&led_cfg);
xTaskCreatePinnedToCore(task_led,"task_led",3000,NULL,2,NULL,1);
}
LEDC-(PWM)
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "driver/gpio.h"
#include "driver/ledc.h"
#define LED_GPIO GPIO_NUM_19
#define FULL_EV_BIT BIT0 //
#define EMPTY_EV_BIT BIT1
static EventGroupHandle_t ledc_event_handle; // 修正变量名
bool IRAM_ATTR ledc_flish_cb(const ledc_cb_param_t *param, void *user_arg) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (param->event == LEDC_FADE_END_EVT) {
if (param->duty == 0) {
xEventGroupSetBitsFromISR(ledc_event_handle, EMPTY_EV_BIT, &xHigherPriorityTaskWoken);
} else {
xEventGroupSetBitsFromISR(ledc_event_handle, FULL_EV_BIT, &xHigherPriorityTaskWoken);
}
}
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
return (xHigherPriorityTaskWoken == pdTRUE);
}
void task_led(void *param) {
EventBits_t ev;
while (1) {
ev = xEventGroupWaitBits(ledc_event_handle, FULL_EV_BIT | EMPTY_EV_BIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(5000));
if (ev & FULL_EV_BIT) {
ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0, 2000);
ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT);
}
if (ev & EMPTY_EV_BIT) {
ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 8191, 2000);
ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT);
}
}
}
void app_main(void) {
// 1. 初始化事件组
ledc_event_handle = xEventGroupCreate();
// 2. 配置LEDC定时器和通道
ledc_timer_config_t timer_cfg = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = LEDC_TIMER_0,
.duty_resolution = LEDC_TIMER_13_BIT,
.freq_hz = 5000,
.clk_cfg = LEDC_AUTO_CLK
};
ESP_ERROR_CHECK(ledc_timer_config(&timer_cfg));
ledc_channel_config_t channel_cfg = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LEDC_CHANNEL_0,
.timer_sel = LEDC_TIMER_0,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = LED_GPIO,
.duty = 0,
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&channel_cfg));
// 3. 安装渐变功能并注册回调
ledc_fade_func_install(0);
ledc_cbs_t cbs = { .fade_cb = ledc_flish_cb };
ledc_cb_register(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, &cbs, NULL);
// 4. 启动初始渐变
ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 8191, 2000);
ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT);
// 5. 创建任务
xTaskCreatePinnedToCore(task_led, "task_led", 3000, NULL, 2, NULL, 1);
}
WS2812
单个WS2812
ws2812的0码和1码都是由高低电平组成的
RES为复位电平:表示一帧数据传输结束
数据帧,RGB格式
多个WS2812
通常情况下,电路中会有不止一个WS2812串联起来,串联方式如下
第一个灯的输出连接到第二个灯的输入引脚上,第二个灯的输出连接到第三个灯的输入引脚上,以此类推
那么任何才能控制每一个灯珠的颜色呢?
这就得益于ws2812的独特控制方式:当我们发生一个24位数据帧后,第一个ws2812会记录并且锁存起来,当我们发生第二个24位数据帧时,第一个ws2812内部以及锁存了数据,此时会将数据通过out引脚输出给第二个ws2812灯,第二个ws2812灯会录并且锁存起来,当我们发生第三个24位数据帧时继续向下递交数据,直到我们发生了一个rest信号,本次传输才会结束,后续发送的数据又会从第一个ws2812开始记录
WIFI
WIFI模型:
AP:路由器,所有的设备和电脑通过ap热点进行数据交换
WIFI热点的启动流程
wifi driver被启动之后, 会发送WIFI_EVENT_AP_START这个事件到event Task中,如果有处理这个事件的回调函数,就会再这个回调函数中进行处理; 如果有设备进行连接,就会发送WIFI_EVENT_AP_STACONNECTED这个事件到event Task, 之后再wifi_event_handler中进行处理(实例代码中只是打印了相应的信息)如果有断开连接的事件WIFI_EVENT_AP_STADISCONNECTED,也会发送到event Task,event Task调用wifi_event_handler进行处理
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_mac.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"
/* The examples use WiFi configuration that you can set via project configuration menu.
If you'd rather not, just change the below entries to strings with
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_WIFI_CHANNEL CONFIG_ESP_WIFI_CHANNEL
#define EXAMPLE_MAX_STA_CONN CONFIG_ESP_MAX_STA_CONN
static const char *TAG = "wifi softAP";
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_id == WIFI_EVENT_AP_STACONNECTED) {
//有设备连接到我们的软件热点时会触发这个事件
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
MAC2STR(event->mac), event->aid);
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
//断开连接时
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
//打印断开设备的信息,如ip
ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d, reason=%d",
MAC2STR(event->mac), event->aid, event->reason);
}
}
void wifi_init_softap(void)
{
//通过调用esp_netif_init()来启动LWIPtask
ESP_ERROR_CHECK(esp_netif_init());
//通过esp_event_loop_create_default()来启动时间task--eventtask
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_ap();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
//esp_wifi_init() 初始化WIFI driver
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
//4.对我们所需处理的一些事件注册了一个回调函数wifi_event_handler
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
//配置driver
wifi_config_t wifi_config = {
.ap = {
.ssid = EXAMPLE_ESP_WIFI_SSID,//热点的名字
.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
.channel = EXAMPLE_ESP_WIFI_CHANNEL,
.password = EXAMPLE_ESP_WIFI_PASS,//热点的密码
.max_connection = EXAMPLE_MAX_STA_CONN,//热点的最大连接数目
#ifdef CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT
.authmode = WIFI_AUTH_WPA3_PSK,//授权的模式,登录的加密模式
.sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
#else /* CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT */
.authmode = WIFI_AUTH_WPA2_PSK,
#endif
.pmf_cfg = {
.required = true,
},
},
};
if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
//使用esp_wifi_set_mode()对driver进行配置
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
//启动driver
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
}
/*
wifi driver被启动之后, 会发送WIFI_EVENT_AP_START这个事件到event Task中,
如果有处理这个事件的回调函数,就会再这个回调函数中进行处理;
如果有设备进行连接,就会发送WIFI_EVENT_AP_STACONNECTED这个事件到event Task,
之后再wifi_event_handler中进行处理(实例代码中只是打印了相应的信息)
如果有断开连接的事件WIFI_EVENT_AP_STADISCONNECTED,也会发送到event Task,
event Task调用wifi_event_handler进行处理
*/
void app_main(void)
{
//初始化存储空间
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
wifi_init_softap();
}
NVS分区:主要是保存一些配置参数,即使设备重启之后,NVS中的数据仍然可以保存,比如说wifi的ssid和密码
socket编程流程
TCP与UDP对比
TCP是基于连接的,是可靠的,但传输速度慢,UDP是不基于连接的, 不可靠,传输速度快
UDP可以进行广播和组播
UDP没有建立连接的过程,所有recvfrom()与sendto()这两个函数在发生数据的时候会包含IP地址等信息,而TCP的recv()与send()不包含IP地址等信息