ESP32 IDF开发 驱动篇④调试打印hello world讲解
别迷路-导航栏
快速导航找到你想要的(文章目录)
此篇文章如果对你有用,请点赞收藏,您的支持就是博主坚持的动力。
1、博主写这篇技术文章的目的:
(1)熟悉esp_log相关API;
(2)掌握esp_log相关API的使用方法;
(3)学会如何建自己的功能;
(4)系统启动流程的基本分析;
2、前言
我们在开发项目中,很多时候为了方便调试,会在关键的地方打印出自己想输出的信息,以便观察程序运行的当前状态。
3、原理
打印调试信息采用的是串口通讯(具体的串口通讯将在后续介绍)是ESP32 自有一个串口用于程序下载和 log 打印。
4、相关函数
esp32 idf串口打印的在esp_log.h文件中定义
*void esp_log_level_set(const char tag, esp_log_level_t level)
设置给定标签的日志级别。此功能不能将日志级别提高到menuconfig中的CONFIG_LOG_DEFAULT_LEVEL所设置的级别。要将日志级别提高到给定文件的默认级别以上,请在esp_log.h包含在文件中之前,将LOG_LOCAL_LEVEL定义为ESP_LOG_ *值之一。
tag:要启用的日志条目的标签。 必须为非NULL 0终止字符串。 值“ *”将所有标签的日志级别重置为给定值。
level:选择要启用的日志级别。 仅显示此级别和较低详细级别的日志。
uint32_t esp_log_timestamp(void)
返回在日志输出中使用的时间戳的函数,以毫秒为单位。(注:系统启动后将以FreeRTOS滴答计数。)
*char esp_log_system_timestamp(void)
该函数返回要在日志输出中使用的系统时间戳,格式HH:MM:SS.sss
uint32_t esp_log_early_timestamp(void)
返回时间戳,以毫秒为单位。该功能使用硬件周期计数器,并且不依赖于操作系统,因此可以在应用程序崩溃后安全使用。
**void esp_log_write(esp_log_level_tlevel, const char tag, const char format, …)
将消息写入日志。此功能不能直接使用。 而是使用ESP_LOGE,ESP_LOGW,ESP_LOGI,ESP_LOGD和ESP_LOGV宏之一。不应在中断中使用此功能或这些宏。
ESP_LOGE(tag, format, …) E error级别(最低)
ESP_LOGW(tag, format, …) W warning 级别
ESP_LOGI(tag, format, …) I info 级别
ESP_LOGD(tag, format, …) D debug 级别
ESP_LOGV(tag, format, …) V verbose 级别(最高)
ESP_LOG_LEVEL(level, tag, format, …)
参数
tag:日志的标签,可在运行时通过esp_log_level_set更改日志级别。
level:输出日志的级别。
format:输出日志的格式。 参考printf
…:要替换到日志中的变量。参考printf
Log level 的枚举过程:
typedef enum {
ESP_LOG_NONE, /*没有日志输出 */
ESP_LOG_ERROR, /*严重错误,软件模块无法自行恢复 错误消息*/
ESP_LOG_WARN, /*已采取恢复措施的错误条件 警告消息*/
ESP_LOG_INFO, /* 描述事件正常流程的信息消息 正常消息*/
ESP_LOG_DEBUG, /*正常使用不需要的额外信息(值,指针,大小等) 调试消息*/
ESP_LOG_VERBOSE /*较大的调试信息块或频繁出现的消息 详细消息*/
} esp_log_level_t;
5、新建自己的工程
在msys32\esp-idf\examples目录下复制get-started基本工程的文件夹,改名字为自己的工程名字,我这里改为hello_world,然后在Makefile文件中将PROJECT_NAME := 工程名字
PROJECT_NAME := hello_world
CMakeLists相关文件是在使用cmake编译使用的,这里不使用。
6、实例分析
Make menuconfig
进入配置界面在此目录下默认的输出级别是ESP_LOG_INFO
component config-> Log output-> Default log verbosity
然后需要设置下载的串口Serial flasher config -> Default serial port修改为自己的端口
make flash monitor开始编译
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
static const char *TAG = "hello_world";
void app_main(void)
{
//系统默认是ESP_LOG_INFO级别
ESP_LOGE(TAG, "ESP_LOGE test!\n");
ESP_LOGW(TAG, "ESP_LOGW test!\n");
ESP_LOGI(TAG, "ESP_LOGI test!\n");
ESP_LOGD(TAG, "ESP_LOGD test!\n");//不输出
ESP_LOGV(TAG, "ESP_LOGV test!\n");//不输出
ESP_LOG_LEVEL(ESP_LOG_ERROR, TAG, "ESP_LOG_LEVEL test!\n") ;
ESP_LOGI(TAG,"----------------------\n");
while(1)
{
esp_log_level_set("*", ESP_LOG_INFO);//设置输出所有的ESP_LOG_INFO消息
ESP_LOGI(TAG, "TAG=* test!\n");
ESP_LOGI("test", "hello world test!\n");
ESP_LOGW(TAG,"ESP_LOGW test!\n");
ESP_LOGE(TAG,"ESP_LOGE test!\n");
ESP_LOGI(TAG,"----------------------\n");
vTaskDelay(1000 / portTICK_RATE_MS);
esp_log_level_set(TAG, ESP_LOG_INFO);//设置只输出 TAG 的ESP_LOG_INFO消息
ESP_LOGI(TAG, "TAG=ESP_LOG_INFO test!\n");
ESP_LOGI("test", "hello world test!\n");//输出TAG=test 系统默认是ESP_LOG_INFO级别
ESP_LOGW(TAG,"ESP_LOGW test!\n");
ESP_LOGE(TAG,"ESP_LOGE test!\n");
ESP_LOGI(TAG,"----------------------\n");
vTaskDelay(1000 / portTICK_RATE_MS);
esp_log_level_set(TAG, ESP_LOG_DEBUG);//设置只输出 TAG 的ESP_LOG_DEBUG消息
ESP_LOGD(TAG, "ESP_LOGD test!\n");//不输出,只能设置高于默认级别的
ESP_LOGI(TAG, "ESP_LOGI test!\n");
ESP_LOGW(TAG,"ESP_LOGW test!\n");
ESP_LOGE(TAG,"ESP_LOGE test!\n");
esp_log_level_set(TAG, ESP_LOG_WARN);//设置只输出 TAG 的ESP_LOG_DEBUG消息
ESP_LOGD(TAG, "ESP_LOGD test!\n");//不输出
ESP_LOGI(TAG, "ESP_LOGI test!\n");//不输出
ESP_LOGW(TAG,"ESP_LOGW test!\n");
ESP_LOGE(TAG,"ESP_LOGE test!\n");
vTaskDelay(1000 / portTICK_RATE_MS);
esp_log_level_set(TAG, ESP_LOG_DEBUG);//设置只输出 TAG 的ESP_LOG_DEBUG消息,使用函数ESP_LOG_LEVEL
ESP_LOG_LEVEL(ESP_LOG_DEBUG, TAG, "----------------------\n") ;
ESP_LOGI(TAG, "timestamp Start Time %d!\n",esp_log_timestamp());
vTaskDelay(1000 / portTICK_RATE_MS);
ESP_LOGI(TAG, "timestamp End Time %d!\n",esp_log_timestamp());
ESP_LOGI(TAG, "timestamp system Time %s!\n",esp_log_system_timestamp());
ESP_LOG_LEVEL(ESP_LOG_DEBUG, TAG, "----------------------\n") ;
}
}
输出结果如下:
从输出结果可以看出在调整esp_log_level_set(TAG, ESP_LOG_DEBUG)时调用ESP_LOG_LEVEL这个函数才可以输出调试信息。
7、系统启动调用简单分析
ESP32 开发程序中有且只能有一个 app_main 函数,该函数是用户程序的入口,相当于其它系统中的 main 函数。但在 app_main 之前,系统还有一段初始化的过程,其大致可以分为以下三个过程:
1. ROM 中的第一级引导加载程序将闪存偏移 0x1000 的第二级引导加载程序映像加载到 RAM(IRAM 和 DRAM);
2. 第二级引导程序从闪存加载分区表和主应用程序映像,主应用程序包含 RAM 段和通过闪存缓存映射的只读段;
3. 主应用程序执行,此时可以启动第二个 CPU 和 RTOS 调度程序。
在cpu_start.c文件中app_main函数会被main_task调用,而main_task函数在start_cpu0_default函数中使用任务分配的方式创建一个任务
portBASE_TYPE res = xTaskCreatePinnedToCore(&main_task, “main”, ESP_TASK_MAIN_STACK,// 3584字节
NULL, ESP_TASK_MAIN_PRIO,//默认为1
NULL, 0);
main_task 的任务是可以配置主任务堆栈大小和优先级,当然我们可以使用此任务进行
初始的应用程序特定设置,例如启动其它任务。应用程序还可以使用事件循环。但是需要注意的是,如果 app_main 函数返回,main_task 将被删除。
详细的启动流程及分区表比较复杂,请参考
【ESP32 IDF开发 驱动篇⑩ 存储NVS高级应用和自定义分区表】
【ESP32 IDF开发 系统篇⑪ 系统启动流程及硬件复位问题分析】
所有文章源代码:https://download.csdn.net/download/lu330274924/88518092