第10章 FreeRTOS

news2024/11/26 23:23:32

ESP32 FREERTOS

打印ESP32任务

  1. menuconfig中,打开FreeRTOS的trace打印功能
    menuconfig中配置trace
  2. menuconfig中,增加app_main主任务的栈大小
    menuconfig中配置主任务堆栈
  3. 测试代码
    ESP32最小工程
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

static void print_task(void)
{
    char buffer[512] = {0};

    printf("%s\t\t%s\t%s\t%s\t%s\t%s\n", "任务名", "状态", "优先级", "堆栈", "序号", "CPU");
    vTaskList(buffer);
    printf("%s", buffer);
}

void app_main(void)
{
    print_task();
}
  1. 运行结果
    ESP32最小工程的任务:
    打印全部任务信息
  2. WIFI扫描工程
    在WIFI扫描功能最后打印FreeRTOS任务
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_event.h"

#define MAX_SCAN_NUM    20

const char *TAG = "WIFI";

void print_task(void)
{
    char buffer[512] = {0};

    printf("%s\t\t%s\t%s\t%s\t%s\t%s\n", "任务名", "状态", "优先级", "堆栈", "序号", "CPU");
    vTaskList(buffer);
    printf("%s", buffer);
}

void app_main(void)
{
    ESP_LOGI(TAG, "0. 初始化NVS存储");
    nvs_flash_init();

    ESP_LOGI(TAG, "1. WiFi初始化阶段");
    esp_netif_init();
    esp_event_loop_create_default();
    esp_netif_create_default_wifi_sta();
    wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&wifi_config);

    ESP_LOGI(TAG, "2. WiFi配置阶段");
    esp_wifi_set_mode(WIFI_MODE_STA);
    

    ESP_LOGI(TAG, "3. WiFi启动阶段");
    esp_wifi_start();

    ESP_LOGI(TAG, "4. WiFi扫描阶段");
    wifi_country_t ccode_config = {
        .cc = "CN",
        .schan = 1,
        .nchan = 13,
        .policy = WIFI_COUNTRY_POLICY_AUTO,
    };
    printf("WiFi scan start...\n");
    esp_wifi_set_country(&ccode_config);
    esp_wifi_scan_start(NULL, true);

    uint16_t ap_count = 0;
    esp_wifi_scan_get_ap_num(&ap_count);
    uint16_t number = MAX_SCAN_NUM;
    wifi_ap_record_t ap_records[MAX_SCAN_NUM];
    memset(ap_records, 0x00, sizeof(ap_records));
    esp_wifi_scan_get_ap_records(&number, ap_records);
        printf("%-5s %-30s %-10s %-5s %-5s\n", "seq", "ssid", "channel", "rssi", "mac");
    for (int i = 0; i < number; i++) {
        printf("%-5d %-30s %-10d %-5d %02x:%02x:%02x:%02x:%02x:%02x\n", i, ap_records[i].ssid, ap_records[i].primary, ap_records[i].rssi,
            ap_records[i].bssid[0], ap_records[i].bssid[1], ap_records[i].bssid[2], ap_records[i].bssid[3], ap_records[i].bssid[4], ap_records[i].bssid[5]);
    }
    printf("WiFi scan done!\n");

    print_task();
}

执行结果:
扫描任务

文档和代码的对应关系

  1. 任务对应
    任务与文档对应关系
  2. 时序对应
    任务与文档对应的启动流程

ESP32 EventLoop

EventLoop官方文档链接

  1. 常用的API
    EventLoop API
  2. 官方文档中给出了参考示例
// 1. Define the event handler
void run_on_event(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data)
{
    // Event handler logic
}
  1. 使用esp_event_handler_register()函数,来执行WIFI扫描。这里有两个特别值得注意的宏定义:
    esp_event_base.h
typedef const char*  esp_event_base_t; /**< unique pointer to a subsystem that exposes events */

// Defines for registering/unregistering event handlers
#define ESP_EVENT_ANY_BASE     NULL             /**< register handler for any event base */
#define ESP_EVENT_ANY_ID       -1               /**< register handler for any event id */

测试代码中打印了EVENT事件base和id

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_event.h"

#define MAX_SCAN_NUM    20

void run_on_event(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data)
{
    ESP_LOGE("EVENT_HANDLE", "base:%s id:%d", base, id);
}

void app_task(void *argv)
{
    ESP_LOGI("APP_TASK", "APP TASK创建完成");
    esp_event_handler_register(ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, run_on_event, NULL);

    while (1) {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    ESP_LOGI("MAIN_TASK", "0. 初始化NVS存储");
    nvs_flash_init();

    ESP_LOGI("MAIN_TASK", "1. WiFi初始化阶段");
    esp_netif_init();
    esp_event_loop_create_default();
    esp_netif_create_default_wifi_sta();
    wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&wifi_config);

    xTaskCreate(app_task, "app_task", 1024 * 12, NULL, 1, NULL);

    ESP_LOGI("MAIN_TASK", "2. WiFi配置阶段");
    esp_wifi_set_mode(WIFI_MODE_STA);
    
    ESP_LOGI("MAIN_TASK", "3. WiFi启动阶段");
    esp_wifi_start();

    ESP_LOGI("MAIN_TASK", "4. WiFi扫描阶段");
    wifi_country_t ccode_config = {
        .cc = "CN",
        .schan = 1,
        .nchan = 13,
        .policy = WIFI_COUNTRY_POLICY_AUTO,
    };
    printf("WiFi scan start...\n");
    esp_wifi_set_country(&ccode_config);
    esp_wifi_scan_start(NULL, true);

    uint16_t ap_count = 0;
    esp_wifi_scan_get_ap_num(&ap_count);
    uint16_t number = MAX_SCAN_NUM;
    wifi_ap_record_t ap_records[MAX_SCAN_NUM];
    memset(ap_records, 0x00, sizeof(ap_records));
    esp_wifi_scan_get_ap_records(&number, ap_records);
        printf("%-5s %-30s %-10s %-5s %-5s\n", "seq", "ssid", "channel", "rssi", "mac");
    for (int i = 0; i < number; i++) {
        printf("%-5d %-30s %-10d %-5d %02x:%02x:%02x:%02x:%02x:%02x\n", i, ap_records[i].ssid, ap_records[i].primary, ap_records[i].rssi,
            ap_records[i].bssid[0], ap_records[i].bssid[1], ap_records[i].bssid[2], ap_records[i].bssid[3], ap_records[i].bssid[4], ap_records[i].bssid[5]);
    }
    printf("WiFi scan done!\n");

    vTaskDelete(NULL);
}

打印事件的base和id。可以看到图中红字部分打印的base为字符串WIFI_EVENT,id分别为2和1。
打印Event事件base和id
这里的字符串WIFI_EVENT和ID=2 ID:1表示什么意思?
WIFI启动过程
参考官方给出的启动过程,打印WiFi启动阶段后,打印了ID:2,猜测ID:2应该是WIFI_EVENT_STA_START;而在打印了WiFi scan start后,打印了ID:1,猜测ID:1应该是完成扫描。
我们的猜测对不对?要看官方文档
3. 官方文档中的事件描述
官网事件描述链接
官网给出的WIFI事件
WIFI事件
对应的代码:esp_wifi_types.h

/** WiFi event declarations */
typedef enum {
    WIFI_EVENT_WIFI_READY = 0,           /**< ESP32 WiFi ready */
    WIFI_EVENT_SCAN_DONE,                /**< ESP32 finish scanning AP */
    WIFI_EVENT_STA_START,                /**< ESP32 station start */
    WIFI_EVENT_STA_STOP,                 /**< ESP32 station stop */
    WIFI_EVENT_STA_CONNECTED,            /**< ESP32 station connected to AP */
    WIFI_EVENT_STA_DISCONNECTED,         /**< ESP32 station disconnected from AP */
    WIFI_EVENT_STA_AUTHMODE_CHANGE,      /**< the auth mode of AP connected by ESP32 station changed */

    WIFI_EVENT_STA_WPS_ER_SUCCESS,       /**< ESP32 station wps succeeds in enrollee mode */
    WIFI_EVENT_STA_WPS_ER_FAILED,        /**< ESP32 station wps fails in enrollee mode */
    WIFI_EVENT_STA_WPS_ER_TIMEOUT,       /**< ESP32 station wps timeout in enrollee mode */
    WIFI_EVENT_STA_WPS_ER_PIN,           /**< ESP32 station wps pin code in enrollee mode */
    WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP,   /**< ESP32 station wps overlap in enrollee mode */

    WIFI_EVENT_AP_START,                 /**< ESP32 soft-AP start */
    WIFI_EVENT_AP_STOP,                  /**< ESP32 soft-AP stop */
    WIFI_EVENT_AP_STACONNECTED,          /**< a station connected to ESP32 soft-AP */
    WIFI_EVENT_AP_STADISCONNECTED,       /**< a station disconnected from ESP32 soft-AP */
    WIFI_EVENT_AP_PROBEREQRECVED,        /**< Receive probe request packet in soft-AP interface */

    WIFI_EVENT_FTM_REPORT,               /**< Receive report of FTM procedure */

    /* Add next events after this only */
    WIFI_EVENT_STA_BSS_RSSI_LOW,         /**< AP's RSSI crossed configured threshold */
    WIFI_EVENT_ACTION_TX_STATUS,         /**< Status indication of Action Tx operation */
    WIFI_EVENT_ROC_DONE,                 /**< Remain-on-Channel operation complete */

    WIFI_EVENT_STA_BEACON_TIMEOUT,       /**< ESP32 station beacon timeout */

    WIFI_EVENT_MAX,                      /**< Invalid WiFi event ID */
} wifi_event_t;

可以看到,与我们的之前的猜测一致。ID=2表示WIFI_EVENT_STA_START,ID=1表示WIFI_EVENT_SCAN_DONE。
我们在event_handler中打印事件id,代码如下:

void run_on_event(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data)
{
    switch (id) {
        case WIFI_EVENT_SCAN_DONE:
            ESP_LOGE("EVENT_HANDLE", "WIFI_EVENT_SCAN_DONE");
            break;
        case WIFI_EVENT_STA_START:
            ESP_LOGE("EVENT_HANDLE", "WIFI_EVENT_STA_START");
            break;
        default:
            break;
    }
}

执行结果:
打印事件
4. 能不能在event_handler中,收到WIFI_EVENT_STA_START后执行WIFI扫描?
测试代码:

void wifi_scan(void)
{
    wifi_country_t ccode_config = {
        .cc = "CN",
        .schan = 1,
        .nchan = 13,
        .policy = WIFI_COUNTRY_POLICY_AUTO,
    };
    printf("WiFi scan start...\n");
    esp_wifi_set_country(&ccode_config);
    esp_wifi_scan_start(NULL, true);

    uint16_t ap_count = 0;
    esp_wifi_scan_get_ap_num(&ap_count);
    uint16_t number = MAX_SCAN_NUM;
    wifi_ap_record_t ap_records[MAX_SCAN_NUM];
    memset(ap_records, 0x00, sizeof(ap_records));
    esp_wifi_scan_get_ap_records(&number, ap_records);
    printf("Scan %d AP\n", ap_count);
    printf("%-5s %-30s %-10s %-5s %-5s\n", "seq", "ssid", "channel", "rssi", "mac");
    for (int i = 0; i < number; i++) {
        printf("%-5d %-30s %-10d %-5d %02x:%02x:%02x:%02x:%02x:%02x\n", i, ap_records[i].ssid, ap_records[i].primary, ap_records[i].rssi,
            ap_records[i].bssid[0], ap_records[i].bssid[1], ap_records[i].bssid[2], ap_records[i].bssid[3], ap_records[i].bssid[4], ap_records[i].bssid[5]);
    }
    printf("WiFi scan done!\n");
}

void run_on_event(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data)
{
    switch (id) {
        case WIFI_EVENT_SCAN_DONE:
            ESP_LOGE("EVENT_HANDLE", "WIFI_EVENT_SCAN_DONE");
            break;
        case WIFI_EVENT_STA_START:
            ESP_LOGE("EVENT_HANDLE", "WIFI_EVENT_STA_START");
            wifi_scan();
            break;
        default:
            break;
    }
}

执行结果:可以看到,在收到WIFI_EVENT_STA_START执行wifi_scan,在sys_evt任务中产生了栈溢出。为什么?
看下之前打印的任务堆栈,sys_ent只剩下1170字节,所以我们不能在event中处理比较复杂的工作。
event_task栈溢出
sys_evt默认堆栈配置:
sys_event menuconfig
5. EVENT转任务
5.1 直接临时创建任务,执行完之后删除

void wifi_scan_start(void)
{
    wifi_country_t ccode_config = {
        .cc = "CN",
        .schan = 1,
        .nchan = 13,
        .policy = WIFI_COUNTRY_POLICY_AUTO,
    };
    printf("WiFi scan start...\n");
    esp_wifi_set_country(&ccode_config);
    esp_wifi_scan_start(NULL, true);
}

void wifi_scan_result(void)
{
    uint16_t ap_count = 0;
    esp_wifi_scan_get_ap_num(&ap_count);
    uint16_t number = MAX_SCAN_NUM;
    wifi_ap_record_t ap_records[MAX_SCAN_NUM];
    memset(ap_records, 0x00, sizeof(ap_records));
    esp_wifi_scan_get_ap_records(&number, ap_records);
    printf("Scan %d AP\n", ap_count);
    printf("%-5s %-30s %-10s %-5s %-5s\n", "seq", "ssid", "channel", "rssi", "mac");
    for (int i = 0; i < number; i++) {
        printf("%-5d %-30s %-10d %-5d %02x:%02x:%02x:%02x:%02x:%02x\n", i, ap_records[i].ssid, ap_records[i].primary, ap_records[i].rssi,
            ap_records[i].bssid[0], ap_records[i].bssid[1], ap_records[i].bssid[2], ap_records[i].bssid[3], ap_records[i].bssid[4], ap_records[i].bssid[5]);
    }
    printf("WiFi scan done!\n");
}

void scan_start_task(void *argv)
{
    wifi_scan_start();
    vTaskDelete(NULL);
}

void scan_result_task(void *argv)
{
    wifi_scan_result();
    vTaskDelete(NULL);
}

void run_on_event(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data)
{
    switch (id) {
        case WIFI_EVENT_SCAN_DONE:
            ESP_LOGE("EVENT_HANDLE", "WIFI_EVENT_SCAN_DONE");
            xTaskCreate(scan_result_task, "scan_result_task", 1024 * 12, NULL, 1, NULL);
            break;
        case WIFI_EVENT_STA_START:
            ESP_LOGE("EVENT_HANDLE", "WIFI_EVENT_STA_START");
            xTaskCreate(scan_start_task, "scan_start_task", 1024 * 12, NULL, 1, NULL);
            break;
        default:
            break;
    }
}

执行结果:
WIFI扫描任务

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/167093.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

php sql注入

文章目录一、什么是sql注入二、sql注入处理1、使用内置函数2、使用pdo预处理语句三、安全注意事项一、什么是sql注入 在应用程序中&#xff0c;为了和用户交互&#xff0c;允许用户提交输入数据&#xff0c;假如应用程序并没有对用户输入数据进行处理&#xff0c;攻击者可以输…

linux开发工具

文章目录linux开发工具1.软件包管理器yum2.编辑器vim3.gcc4.make与makefile5.git6.gdb7.小程序进度条linux开发工具 1.软件包管理器yum yum等同于手机上的应用商店.yum自动解决软件之间的耦合问题 yum list 显示软件清单 yum install 下载 yum remove 删除 2.编辑器vim 其…

opencv开发之numpy使用

打开Spyder, 在IPython控制台中输入 import numpy as np 引入numpy库并使用numpy构造一个ndarray对象: np.zeros((2,4),np.uint8),该对象为一个二维数组 ,构造一个2行4列的二维数组(矩阵) ,并初始化所有元素为0,及指定数据类型为uint8 创建并初始化:数据可视化:取矩阵类型: typ…

基于YOLOv4的车辆检测 MATLAB实现

目录 摘要 研究背景 算法设计及实现过程 车辆目标数据集的构建 基于YOLOv4的目标检测 对YOLOv4模型进行改进 实验结果及分析 结论与展望 代码实现 摘要 针对车辆检测&#xff0c;本文提出了一种基于YOLOv4车辆检测算法。制作了一个多天侯、多时段、多场景的车辆目标数…

怎么拆分PDF文件,教你用最简单的方法

PDF文档拆分是一种很常见的需求。因为有时候文档页数过多&#xff0c;打开和阅读都不太方便&#xff0c;我们可以通过拆分把想要的部分提取出来。拆分PDF的时候可以借助一些工具更快更有效地达成目的&#xff0c;这里就给大家介绍几款比较受欢迎的处理工具&#xff0c;它们在辅…

Linux——系统管理篇

1、、Linux 中的进程和服务 计算机中、一个正在执行的程序或命令&#xff0c;叫进程&#xff08;process&#xff09;。 启动之后一直存在、常驻内存的进程&#xff0c;一般称为“服务”&#xff08;Service&#xff09; // 我更喜欢叫它守护进程 Daemon 比如windows的那一堆…

软件测试面试,如何自我介绍?

01 如何自我介绍 面试过程中一定要放慢语速&#xff0c;做到条理清晰。特别是做自我介绍时&#xff0c;可以适当多介绍自己会什么&#xff0c;有哪些重要经验。 例如&#xff1a; 面试官&#xff0c;上午/下午好。 我是XXX&#xff0c;今天来面试贵公司的软件测试工程师岗位&a…

利用QT 的 Graphics View 系统实现一个 简易的 Graph Editor

QT 中的 Graphics View 系统. 是一个相对成熟的渲染引擎的上层框架&#xff0c;通常也可以会叫做 Scene - View。 通常会有 QGraphicsView, QGraphicsScene, QGraphicsItem 这几个类构成。 view是视口(viewport)&#xff1b;scene是一个场景&#xff0c;负责容纳各种item&…

JS语言基础

目录 语法 关键字与保留字 变量 var关键字 let声明 暂时性死区 全局变量 for循环中的let声明 条件声明 const声明 语法 1. 区分大小写 无论是变量、函数名还是操作符&#xff0c;都区分大小写。 2. 标识符 所谓标识符&#xff0c;就是变量、函数、属性或函数参数的名…

centos7配置(nvidia+cuda+cudnn+anaconda+tensorflow)gpu开发环境

一、安装准备 1、查看nvidia显卡&#xff0c;我的是T4显卡 lspci | grep -i nvidia2、查看linux系统版本 uname -m && cat /etc/redhat-release3、安装依赖 yum install gcc kernel-devel kernel-headers二、安装nvidia驱动 1、禁用nouveau lsmod | grep nouveau…

Powershell渗透框架

文章目录Powershell基础Powershell简介什么是 Windows PowerShell为什么使用 Windows PowerShell如何启动 Windows PowerShellPowerShell和Cmd命令提示符的区别PowerShellcmd管理员运行 PowerShellWindows PowerShell ISE创建并运行脚本文本编辑器创建脚本集成脚本环境创建脚本…

第二章.线性回归以及非线性回归—特征缩放,交叉验证法,过拟合

第二章.线性回归以及非线性回归 2.9 特征缩放 1.数据归一化 1).作用&#xff1a; 把数据的取值范围处理为0-1或者-1-1 2).数据范围处理为0-1之间的方法&#xff1a; newValue(oldValue-min)/(max-min) 例如&#xff1a;数组:&#xff08;1,3,5&#xff09;,value1:(1-1)/(5-1)0…

MyBatis-Plus分析打印SQL(开发环境)

项目创建POM依赖 <!-- https://mvnrepository.com/artifact/p6spy/p6spy --> <dependency><groupId>p6spy</groupId><artifactId>p6spy</artifactId><version>3.9.1</version> </dependency> YML配置 spring:datasource…

silicon labs Gateway HOST-NCP MQTT网关搭建

一、背景 目前正在开发一款中控网关,网关mcu跑Android系统,NCP采用EFR32MG21开发板,需要跑MQTT协议控制zigbee的网络。基于以上需求,下载了simplicity studio V5版本和最新的EmberZNet 7.2.0.0协议栈进行验证,发现新的GSDK已经不再支持MQTT功能,官方回答是EmberZNet 6.7…

论文解读 - 城市自动驾驶车辆运动规划与控制技术综述 (第2部分)

文章目录&#x1f697; II. Overview of the decision-making hierarchy used in driverless cars&#xff08;无人驾驶汽车的决策层综述&#xff09;&#x1f534; A. Route Planning&#xff08;路径规划&#xff09;&#x1f7e0; B. Behavioral Decision Making&#xff08…

论文工具大全+软件简介

文章目录**1.使用说明用哪个文库就打开&#xff0c;****2.在软件中复制粘贴网址点下载**3.点已下载文件右击鼠标另外保存**腾讯微云-https://share.weiyun.com/5U3fAjF**1.安装并上传论文点检测2.检测等待时间3.打开检测报告查看回复[文献]&#xff1a;参考文献自动生成器参考文…

贪心算法专题

1.Acwing 1055. 股票买卖 II 题目链接&#xff1a;1055. 股票买卖 II - AcWing题库 思路&#xff1a;逢涨就买 #include<iostream> using namespace std;int main() {int n;long long ans0;int a[100005];cin>>n;cin>>a[0];for(int i1;i<n;i){cin>&…

C语言—动态内存管理

专栏&#xff1a;C语言 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;本专栏主要更新一些C语言的基础知识&#xff0c;也会实现一些小游戏和通讯录&#xff0c;学时管理系统之类的&#xff0c;有兴趣的朋友可以关注一下。 动态内存管理前言一、为什么会存在动态内存分配二…

磨金石教育分享||CG特效技术主要应用在哪几个领域

前面我们介绍了很多关于CG特效的知识&#xff0c;我们知道CG特效发展的历史以及重大意义。那么我们今天再来详细讨论一下CG特效主要应用的几个领域。近几年文化艺术的发展伴随着互联网信息技术高速传播。文化艺术产业变得多元&#xff0c;动漫、3A大作游戏、商业大片、虚拟现实…

Java 元注解

​ 元注解是负责对其它注解进行说明的注解&#xff0c;自定义注解时可以使用元注解。Java 5 定义了 4 个注解&#xff0c;分别是 Documented、Target、Retention 和 Inherited。Java 8 又增加了 Repeatable 和 Native 两个注解。这些注解都可以在 java.lang.annotation 包中找到…