ESP32学习笔记(6)——Log日志库使用

一、printf是不可重入函数

printf不能在中断中被调用的原因是它是一个不可重入函数,而在中断中要避免调用不可重入函数,首先我们先说说什么是不可重入函数。

简单说来,区分一个函数是否可重入就是看这个函数能否在未返回的时候再次被调用。而造成一个函数不可重入的原因往往是使用了全局变量,如果一个函数未返回再执行一次会导致对全局变量的操作是不安全的。就例如我们常用的printf、malloc、free都是不可重入的函数,printf会引用全局变量stdout,malloc,free会引用全局的内存分配表,在多线程的环境下,如果没有很好的处理数据保护和互斥访问,就会发生错误。

二、ESP_LOGx日志

日志记录库提供了两种设置日志详细程度的方法:

  • 在编译时:在menuconfig中,使用选项设置详细程度CONFIG_LOG_DEFAULT_LEVEL。详细程度高于的所有日志记录语句CONFIG_LOG_DEFAULT_LEVEL 将被预处理器删除。

  • 在运行时:详细级别低于的所有日志CONFIG_LOG_DEFAULT_LEVEL默认情况下启用。该功能esp_log_level_set()可用于按模块设置日志记录级别。模块由其标签标识,这些标签是可读的ASCII零终止字符串。

该功能esp_log_level_set()无法将日志记录级别设置为高于CONFIG_LOG_DEFAULT_LEVEL。要在编译时增加特定文件的日志级别,请使用宏LOG_LOCAL_LEVEL

有以下详细级别:

  • ESP_LOGE -错误(最低)
  • ESP_LOGW - 警告
  • ESP_LOGI -信息
  • ESP_LOGD -调试
  • ESP_LOGV -详细(最高)

此外,ESP_EARLY_LOGx每个宏都有相应的版本,例如ESP_EARLY_LOGE。在初始化堆分配器和syscalls之前,只能在早期启动代码中显式使用这些版本。普通ESP_LOGx宏也可以在编译引导加载程序时使用,但是它们将退回到与ESP_EARLY_LOGx宏相同的实现方式。

考虑到线程安全,在FreeRTOS中尽量使用ESP_LOGx来输出调试信息和打印消息。

ESP-IDF 编程指南——记录库

三、包含头文件

#include "esp_log.h"

四、如何使用这个库

4.1 打印信息

在每个使用日志记录功能的C文件中,定义TAG变量,如下所示:

static const char* TAG = "MyModule";

然后使用日志记录宏之一生成输出,例如:

uint16_t ap_count = 0;
ESP_LOGI(TAG, "Total APs scanned = %u", ap_count);

4.2 更改详细级别

要在文件或组件范围内覆盖默认的详细级别,请定义LOG_LOCAL_LEVEL宏。

在文件范围内,在包含之前定义它esp_log.h,例如:

#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "esp_log.h"

在组件范围内,在组件makefile中定义它:

CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG

要在运行时配置每个模块的日志记录输出,请esp_log_level_set()按以下方式向函数添加调用:

esp_log_level_set("*", ESP_LOG_ERROR);        // set all components to ERROR level
esp_log_level_set("wifi", ESP_LOG_WARN);      // enable WARN logs from WiFi stack
esp_log_level_set("dhcpc", ESP_LOG_INFO);     // enable INFO logs from DHCP client

4.3 通过JTAG登录到主机

默认情况下,日志记录库使用类似于vprintf的函数将格式化的输出写入专用UART。通过调用一个简单的API,所有日志输出都可以路由到JTAG,从而使日志记录速度提高了几倍。有关详细信息,请参阅“登录到主机”部分


• 由 Leung 写于 2021 年 4 月 20 日

• 参考:ESP32 开发笔记(三)源码示例 0、Hello Bug ESP_LOGX与printf

<think>嗯,用户遇到了OLED上无法调用drawBitmap的问题,需要找出原因并提供解决方案。首先,我得回忆一下U8g2库的使用情况,以及常见的drawBitmap问题。 用户可能没有正确初始化显示屏对象或者分辨率设置不匹配。比如,U8g2库支持多种构造函数,不同的OLED型号可能需要不同的初始化参数,比如缓冲区的选择(全缓冲、页面缓冲等)。如果用户选错了构造函数,可能导致drawBitmap无法正常工作。 然后,内存不足也是一个可能的原因。特别是如果用户使用的开发板内存较小,比如ESP32的某些型号,如果图像数据过大,可能会耗尽内存,导致函数调用失败。这时候需要检查图像尺寸是否合适,或者是否使用了动态内存分配导致碎片化。 接下来是图像数据格式的问题。drawBitmap需要的数据格式是XBM格式,如果用户直接用了其他格式的图片,比如BMP或PNG,而没有转换,那肯定无法正确显示。需要确认用户是否使用了正确的转换工具,比如Image2CPP或者在线转换器生成XBM数组。 代码层面的错误也不能忽视。例如,drawBitmap的坐标参数是否正确,是否在调用drawBitmap之后漏掉了sendBuffer,导致图像没有实际发送到屏幕上。另外,检查是否在绘制前调用了clearBuffer,确保缓冲区是干净的。 硬件连接问题也可能导致此问题。比如,I2C地址是否正确,线缆接触不良,或者电源不稳定。用户需要确认接线是否正确,特别是SCL和SDA引脚是否与代码中的定义一致。 还有,U8g2库的版本可能影响功能。旧版本可能存在bug,更新到最新版或许能解决问题。用户需要检查库的版本,必要时进行升级。 最后,参考引用中的内容,引用[2]提到正确选择显示屏对象和分辨率是关键,还提到了函数的使用方法和参数,这支持了初始化设置的重要性。另外,引用[4]中提到的字体转换工具可能对图像数据转换有帮助,虽然用户的问题是关于bitmap,但类似的转换步骤可能适用。 综合这些可能性,解决方案应包括检查初始化参数、验证图像数据格式、确保内存足够、核对代码逻辑、检查硬件连接以及更新库版本等步骤。需要一步步引导用户排查,从最常见的错误开始,逐步深入。</think>### OLED调用drawBitmap失败的原因及解决方案 #### 一、可能原因分析 1. **显示屏对象初始化错误** U8g2库支持多种OLED控制器型号(如SSD1306、SH1106等),若选择的构造函数与硬件不匹配,会导致绘制函数失效[^2]。例如: ```cpp U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0); // 正确示例 ``` 2. **图像数据格式错误** `drawBitmap`要求使用XBM格式的位图数据,若直接使用其他格式(如BMP/PNG)未经转换,会导致渲染失败。例如: ```cpp // 错误示例:未转换的BMP数据 static unsigned char bitmap[] = {0xFF, 0x80...}; // 正确示例:XBM格式数据 static unsigned char bitmap_bits[] = {0x0F, 0x1E...}; ``` 3. **内存不足** 开发板(如ESP32)内存较小的情况下,过大的位图数据可能超出堆栈或动态内存限制。例如: ```cpp // 128x64像素的XBM图像占用空间计算: // (128/8) * 64 = 1024字节 ``` 4. **硬件连接问题** I2C地址错误(常见地址0x3C或0x3D)或接线错误会导致通信失败。 --- #### 二、解决方案步骤 1. **验证显示屏初始化** 检查是否匹配显示屏型号和分辨率: ```cpp // 正确初始化示例(SSD1306 128x64) U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); u8g2.begin(); // 必须调用初始化 ``` 2. **转换图像为XBM格式** 使用工具转换图像: - 在线工具:[Image2CPP](https://javl.github.io/image2cpp/) - GIMP导出XBM格式文件 生成代码示例: ```cpp static const unsigned char epd_bitmap_heart[] = { 0x00, 0xE0, 0x1C, 0x02, 0x02, 0x1C, 0xE0, 0x00... }; ``` 3. **优化内存使用** - 使用PROGMEM存储常量数据(针对AVR) ```cpp const uint8_t bitmap[] PROGMEM = {0x00, 0x1F...}; ``` - 降低位图分辨率(如64x32代替128x64) 4. **检查绘制流程** 确保完整的绘制周期: ```cpp void loop() { u8g2.clearBuffer(); u8g2.drawBitmap(x, y, width, height, bitmap); u8g2.sendBuffer(); delay(1000); } ``` 5. **硬件排查** - 用I2C扫描工具确认地址 - 测量SCL/SDA电压(正常范围3.3V) - 检查上拉电阻(通常4.7kΩ) --- #### 三、进阶调试方法 1. **启用U8g2日志** 在库配置文件中启用调试输出: ```cpp #define U8G2_DEBUG_DRAW 1 ``` 2. **分阶段测试** 先测试基础图形绘制: ```cpp u8g2.drawFrame(0,0,128,64); // 绘制边框 ``` 3. **内存分析** 使用ESP32内存监控工具: ```cpp Serial.printf("Free heap: %d\n", ESP.getFreeHeap()); ``` ---
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leung_ManWah

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值