OLED Animation Optimization

Created on: December 22, 2024

Answered using O1 by Chat01

Question

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
int fps=40;
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, 20, 21, U8X8_PIN_NONE);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void setup() {
Serial.begin(115200);
u8g2.setI2CAddress(0x3D*2);
u8g2.begin();
u8g2.enableUTF8Print();
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}// Pause for 2 seconds

// Clear the buffer.

}

void loop() {

display.clearDisplay();
display.drawBitmap(0, 0, gImage_1, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_2, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_3, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_4, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_5, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_6, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_7, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_8, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_9, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_10, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_11, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_12, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_13, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_14, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_15, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_16, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_17, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_18, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_19, 128, 64, 1);
display.display();
delay(fps);

display.clearDisplay();
display.drawBitmap(0, 0, gImage_20, 128, 64, 1);
display.display();
delay(fps);

}
对代码内容进行优化

Thought

优化循环结构

我发现Adafruit的SSD1306 OLED显示库代码存在重复,刍除不必要的库,并调整fps变量以达到预期的帧率。

改进性能

建议使用位图指针数组循环遍历图像,去除冗余代码,修正fps变量,可能的话选择单一显示库以提高性能。

思考,持续 几秒

Answer

Certainly! Your current code displays a sequence of 20 bitmaps on an SSD1306 OLED display using the Adafruit library. However, it contains a lot of repetitive code, which can be optimized for better readability, maintainability, and efficiency. Below are the key optimizations and an improved version of your code:

  1. Eliminate Redundant Libraries:

    • Remove U8G2 Library: You're initializing both U8G2_SSD1306 and Adafruit_SSD1306 libraries, but only using the Adafruit library in the loop(). Unless you have a specific reason to use both, it's better to stick with one to conserve memory and reduce complexity.
  2. Use Arrays for Bitmaps:

    • Bitmap Array: Instead of individually calling each bitmap, store pointers to all your bitmaps in an array. This allows you to loop through them efficiently.
  3. Correct Frame Rate Handling:

    • Frame Delay: The fps variable is set to 40, which is used as a delay in milliseconds (delay(fps);). If you intend fps to represent frames per second, the delay should be calculated as 1000 / fps. For example, fps = 25 would correspond to 40 ms per frame.
  4. Optimize Display Updates:

    • Minimize clearDisplay() Calls: Clearing the display before each new frame ensures no ghosting, but you can structure the code to make it cleaner.
  5. Enhance Code Readability:

    • Constants and Definitions: Use const for variables that don't change, such as the number of frames and frame rate.

Optimized Code

cpp
#include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> // Display dimensions #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 // OLED reset pin (set to -1 if sharing Arduino reset pin) #define OLED_RESET -1 // Frame rate settings const int FPS = 25; // Frames per second const int FRAME_DELAY = 1000 / FPS; // Delay per frame in milliseconds // Number of frames const int NUM_FRAMES = 20; // Initialize the display Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Array of bitmap pointers const unsigned char* frames[NUM_FRAMES] = { gImage_1, gImage_2, gImage_3, gImage_4, gImage_5, gImage_6, gImage_7, gImage_8, gImage_9, gImage_10, gImage_11, gImage_12, gImage_13, gImage_14, gImage_15, gImage_16, gImage_17, gImage_18, gImage_19, gImage_20 }; void setup() { Serial.begin(115200); // Initialize the OLED display if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for most modules Serial.println(F("SSD1306 allocation failed")); while(true); // Don't proceed, loop forever } display.clearDisplay(); display.display(); // Ensure the display is cleared at start } void loop() { for(int i = 0; i < NUM_FRAMES; i++) { display.clearDisplay(); // Clear the buffer // Draw the current frame display.drawBitmap(0, 0, frames[i], SCREEN_WIDTH, SCREEN_HEIGHT, WHITE); display.display(); // Update the display with the new frame delay(FRAME_DELAY); // Wait for the next frame } }

Explanation of Changes

  1. Removed U8G2 Library:

    • Since only the Adafruit SSD1306 library is used in the loop(), the U8G2-related code has been removed to simplify the setup.
  2. Defined Constants:

    • FPS and FRAME_DELAY are defined to make frame rate adjustments easier.
    • NUM_FRAMES specifies the total number of frames, making it easier to manage if you add or remove frames in the future.
  3. Bitmap Array:

    • An array frames holds pointers to all your bitmap images (gImage_1 to gImage_20). This allows for easy iteration using a for loop.
  4. Loop Optimization:

    • The loop() function now iterates through the frames array, displaying each bitmap sequentially with the specified delay.
    • drawBitmap uses WHITE as the color parameter, which is standard for monochrome displays. Adjust if your display uses a different color scheme.
  5. Error Handling:

    • If the display initialization fails, the program prints an error message and enters an infinite loop to prevent further execution.
  6. Code Readability:

    • Clear comments and structured code blocks improve readability and maintainability.

Additional Recommendations

  • Memory Management:

    • Ensure that all gImage_x bitmaps are properly defined and stored in program memory (PROGMEM) if they are large, to conserve SRAM.
  • Dynamic Frame Handling:

    • For even greater flexibility, consider dynamically loading frames or using external storage if the number of frames increases significantly.
  • Non-blocking Delays:

    • Using delay() is simple but blocks the CPU. For more advanced applications, consider using non-blocking timing (e.g., millis()) to allow other tasks to run concurrently.
  • Power Optimization:

    • If your project is battery-powered, implement sleep modes or power-saving techniques to extend battery life.

By implementing these optimizations, your code becomes more efficient, easier to manage, and better suited for future enhancements.

Question

我所使用的iic接口为scl20,sda21,请你帮我完成代码的编写。并加上中文注释

Thought

精简代码

用户正在请求优化代码,并补全针对于I2C引脚SCL20和SDA21的部分,还特别要求添加中文注释。

简化代码

真心优化代码:移除U8G2代码,配置使用自定义SDA和SCL引脚(21和20),使用Adafruit_SSD1306,并加入中文注释。

删减与初始化

用户优化代码,精简为20帧,并将延迟设为25ms以匹配40帧每秒的帧率。

更新引脚

我正在调整代码以确保SDA指向21和SCL指向20,并且用中文例子讲解如何配置这些引脚。

思考,持续 4 秒

Answer

好的,以下是优化后的代码,使用Adafruit_SSD1306库,并配置I2C接口的SCL为20,SDA为21。同时,代码中添加了详细的中文注释以便理解和维护。

cpp
#include <Wire.h> // 引入I2C通信库 #include <Adafruit_GFX.h> // 引入Adafruit图形库 #include <Adafruit_SSD1306.h> // 引入Adafruit SSD1306 OLED显示库 // 显示屏的宽度和高度 #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 // OLED复位引脚(如果不使用复位引脚,则设置为-1) #define OLED_RESET -1 // 帧率设置 const int FPS = 25; // 每秒帧数(Frames Per Second) const int FRAME_DELAY = 1000 / FPS; // 每帧延迟时间(毫秒) // 帧数总数 const int NUM_FRAMES = 20; // 初始化SSD1306显示对象,使用Wire库,指定SDA为21,SCL为20 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // 存储所有位图的数组 const unsigned char* frames[NUM_FRAMES] = { gImage_1, gImage_2, gImage_3, gImage_4, gImage_5, gImage_6, gImage_7, gImage_8, gImage_9, gImage_10, gImage_11, gImage_12, gImage_13, gImage_14, gImage_15, gImage_16, gImage_17, gImage_18, gImage_19, gImage_20 }; void setup() { // 初始化串口,波特率为115200,用于调试 Serial.begin(115200); // 初始化I2C通信,指定SDA为21,SCL为20 Wire.begin(21, 20); // 初始化OLED显示屏 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // 通常地址为0x3C Serial.println(F("SSD1306 初始化失败")); while(true); // 如果初始化失败,进入无限循环 } // 清空显示缓冲区 display.clearDisplay(); display.display(); // 确保显示屏在开始时是清空的 } void loop() { // 遍历所有帧 for(int i = 0; i < NUM_FRAMES; i++) { display.clearDisplay(); // 清空显示缓冲区 // 在显示屏的(0,0)位置绘制当前帧的位图 display.drawBitmap(0, 0, frames[i], SCREEN_WIDTH, SCREEN_HEIGHT, WHITE); display.display(); // 将缓冲区内容显示到屏幕上 delay(FRAME_DELAY); // 延迟以控制帧率 } }

代码说明

  1. 库的引入:

    • Wire.h:用于I2C通信。
    • Adafruit_GFX.h:Adafruit的图形库,提供绘图功能。
    • Adafruit_SSD1306.h:专门用于SSD1306 OLED显示屏的库。
  2. 显示屏配置:

    • 定义了显示屏的宽度和高度为128x64像素。
    • OLED_RESET设置为-1,表示不使用复位引脚。
  3. 帧率设置:

    • FPS定义了每秒显示的帧数,这里设置为25帧每秒。
    • FRAME_DELAY根据FPS计算每帧的延迟时间。
  4. 显示对象初始化:

    • 使用Adafruit_SSD1306库初始化显示对象,指定I2C的SDA和SCL引脚为21和20。
  5. 位图数组:

    • frames数组存储了所有需要显示的位图指针(gImage_1gImage_20)。请确保这些位图在代码的其他部分正确定义,并且大小为128x64像素。
  6. setup()函数:

    • 初始化串口,用于调试信息输出。
    • 初始化I2C通信,指定SDA和SCL引脚。
    • 初始化OLED显示屏,如果初始化失败,会在串口输出错误信息并进入无限循环。
    • 清空显示缓冲区并刷新显示屏,确保开始时屏幕是空白的。
  7. loop()函数:

    • 使用一个for循环遍历所有帧。
    • 对于每一帧,清空显示缓冲区,绘制当前帧的位图,然后刷新显示屏。
    • 使用delay(FRAME_DELAY)控制每帧的显示时间,以达到设定的帧率。

进一步优化建议

  • 内存管理:

    • 如果位图较大,建议将它们存储在程序存储器(PROGMEM)中,以节省SRAM。例如:
      cpp
      const unsigned char gImage_1[] PROGMEM = { ... };
  • 非阻塞延迟:

    • 使用delay()会阻塞CPU,影响其他任务的执行。可以考虑使用millis()实现非阻塞延迟,以便在显示帧的同时执行其他操作。
  • 动态帧处理:

    • 如果帧数增加,或者需要动态加载帧,可以考虑从外部存储(如SD卡)读取位图,提高灵活性。
  • 错误处理:

    • 增加更多的错误处理机制,如重新尝试初始化显示屏,或在显示失败时进行其他操作。

通过上述优化,代码不仅更加简洁高效,而且更易于维护和扩展。如果有任何进一步的问题或需求,请随时告知!

Share this Q&A