1. 完整封装并拓展了SD卡文件管理类,支持基本文件管理功能
2. 简单封装了LVGL渲染类,已经封装好了gif渲染功能 3. 修复了硬件厂商提供的驱动的Bug 4. 初步定义了宠物基类的抽象信息
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/8.
|
||||
//
|
||||
|
||||
#include "ThreadManager.h"
|
||||
#include "LVGLRender.h"
|
||||
#include "I2C_Driver.h"
|
||||
#include "ST77916.h"
|
||||
#include "LVGL_Driver.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
LVGLRender* LVGLRender::LVGLRenderInstance = nullptr;
|
||||
std::mutex LVGLRender::instance_mutex;
|
||||
uint16_t LVGLRender::fps = 30;
|
||||
|
||||
LVGLRender* LVGLRender::getInstance() {
|
||||
// 双检锁(DCLP),C++11 起 atomic+mutex 组合保证线程安全
|
||||
LVGLRender* tmp = LVGLRenderInstance;
|
||||
if (tmp == nullptr) {
|
||||
std::lock_guard<std::mutex> lock(instance_mutex);
|
||||
tmp = LVGLRenderInstance;
|
||||
if (tmp == nullptr) {
|
||||
tmp = new LVGLRender();
|
||||
LVGLRenderInstance = tmp;
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void LVGLRender::LVGL_Update() {
|
||||
while (true) {
|
||||
vTaskDelay(LVGL_DELAY_FROM_FPS(LVGLRender::fps));
|
||||
lv_timer_handler();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 构造函数
|
||||
LVGLRender::LVGLRender() {
|
||||
ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...初始化媒体驱动...");
|
||||
I2C_Init();
|
||||
LCD_Init();
|
||||
LVGL_Init();
|
||||
ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...初始化媒体驱动成功...");
|
||||
|
||||
ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...创建LVGL心跳...");
|
||||
|
||||
ThreadConfig trickConfig;
|
||||
trickConfig.core_id = 1; // 渲染分配给核1
|
||||
trickConfig.name = "LVGL_Render_Heartbeat";
|
||||
trickConfig.priority = 5;
|
||||
trickConfig.stack_size = 4096; // 给LVGL一个较大的堆栈,避免栈溢出
|
||||
|
||||
std::thread tick_thread = ThreadManager::createMemberThread(trickConfig, this, &LVGLRender::LVGL_Update);
|
||||
|
||||
tick_thread.detach(); // 线程分离 生命周期跟随主线程结束,线程结束后自动销毁
|
||||
|
||||
ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...创建LVGL心跳成功...");
|
||||
}
|
||||
|
||||
LVGLRender::~LVGLRender() {
|
||||
|
||||
}
|
||||
|
||||
void LVGLRender::log() {
|
||||
ESP_LOGI("LVGL_Render", "LVGL_Render log...");
|
||||
}
|
||||
|
||||
// 静态:拼出完整路径
|
||||
std::string LVGLRender::makeFullPath(const std::string& filename)
|
||||
{
|
||||
return "/sdcard/" + filename;
|
||||
}
|
||||
|
||||
#include "SDFileManager.h"
|
||||
std::vector<uint8_t> LVGLRender::readWholeFile(const std::string& path)
|
||||
{
|
||||
ESP_LOGI("LVGLRender", "开始读取文件: %s", path.c_str());
|
||||
|
||||
// 直接用 SDFileManager 同步读整个文件 TODO: 考虑修改为异步读
|
||||
std::string content = SDFileManager::getInstance()->readFileSync(path.c_str());
|
||||
if (content.empty()) {
|
||||
ESP_LOGE("LVGLRender", "readFileSync 失败或文件为空");
|
||||
return {};
|
||||
}
|
||||
|
||||
// string -> vector,零拷贝 move
|
||||
return {content.begin(), content.end()};
|
||||
}
|
||||
|
||||
bool LVGLRender::getGifWH(const uint8_t* raw, uint32_t& w, uint32_t& h)
|
||||
{
|
||||
if (!raw || memcmp(raw, "GIF", 3) != 0) {
|
||||
ESP_LOGE("LVGLRender", "不是合法 GIF 文件头");
|
||||
return false;
|
||||
}
|
||||
// GIF87a/89a 宽高偏移 6~9 字节,小端
|
||||
w = raw[6] | (raw[7] << 8);
|
||||
h = raw[8] | (raw[9] << 8);
|
||||
ESP_LOGI("LVGLRender", "GIF 尺寸: %lu x %lu", w, h);
|
||||
return true;
|
||||
}
|
||||
void LVGLRender::renderGifInternal(const std::vector<uint8_t>& data,
|
||||
uint32_t w, uint32_t h)
|
||||
{
|
||||
// 构造 lv_img_dsc_t —— 数据指针直接指向 vector 内部
|
||||
static lv_img_dsc_t gif_desc;
|
||||
gif_desc.header.cf = LV_IMG_CF_RAW_CHROMA_KEYED;
|
||||
gif_desc.header.always_zero = 0;
|
||||
gif_desc.header.reserved = 0;
|
||||
gif_desc.header.w = (lv_coord_t)w;
|
||||
gif_desc.header.h = (lv_coord_t)h;
|
||||
gif_desc.data_size = data.size();
|
||||
gif_desc.data = data.data(); // 指向 vector 内部
|
||||
|
||||
// 创建 lv_gif 对象
|
||||
lv_obj_t* gif = lv_gif_create(lv_scr_act());
|
||||
lv_gif_set_src(gif, &gif_desc);
|
||||
lv_obj_center(gif);
|
||||
|
||||
ESP_LOGI("LVGLRender", "GIF 已渲染到屏幕");
|
||||
}
|
||||
|
||||
void LVGLRender::RenderGif(const std::string &filename) {
|
||||
std::string fullPath = makeFullPath(filename);
|
||||
|
||||
// 读文件
|
||||
std::vector<uint8_t> gifBin = readWholeFile(fullPath);
|
||||
if (gifBin.empty()) return;
|
||||
|
||||
// 校验并解析宽高
|
||||
uint32_t w = 0, h = 0;
|
||||
if (!getGifWH(gifBin.data(), w, h)) return;
|
||||
|
||||
// LVGL 渲染函数
|
||||
renderGifInternal(gifBin, w, h);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Created by misaki on 2025/9/8.
|
||||
//
|
||||
/**
|
||||
* 本类为单例类,用于实现LVGL的渲染
|
||||
* 封装了一整套的LVGL渲染流
|
||||
* 同时兼顾了底层的显示驱动
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define LVGL_DELAY_FROM_FPS(fps) pdMS_TO_TICKS(1000 / (fps))
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
class LVGLRender {
|
||||
public:
|
||||
static std::string makeFullPath(const std::string& filename);
|
||||
|
||||
public:
|
||||
static LVGLRender* getInstance();
|
||||
|
||||
/**
|
||||
* 全屏渲染gif动画
|
||||
* @brief 渲染GIF文件
|
||||
* @param filename GIF文件路径
|
||||
*/
|
||||
void RenderGif(const std::string &filename);
|
||||
|
||||
void log();
|
||||
|
||||
// gif渲染
|
||||
private:
|
||||
/* 同步读整个文件到 vector */
|
||||
std::vector<uint8_t> readWholeFile(const std::string& path);
|
||||
|
||||
/* 从原始数据解析 GIF 宽高 */
|
||||
bool getGifWH(const uint8_t* raw, uint32_t& w, uint32_t& h);
|
||||
|
||||
/* 真正的渲染实现(空壳,先打印日志) */
|
||||
void renderGifInternal(const std::vector<uint8_t>& data,
|
||||
uint32_t w, uint32_t h);
|
||||
|
||||
private:
|
||||
explicit LVGLRender(); // 构造函数私有化
|
||||
~LVGLRender();
|
||||
|
||||
LVGLRender(LVGLRender const&) = delete; // 拷贝构造函数私有化
|
||||
LVGLRender& operator=(LVGLRender const&) = delete;// 赋值运算符私有化
|
||||
|
||||
void LVGL_Update(); // 渲染lvgl上下文(持久性线程)
|
||||
|
||||
private:
|
||||
static LVGLRender* LVGLRenderInstance; /// 单例实例
|
||||
static std::mutex instance_mutex; /// 单例锁
|
||||
static uint16_t fps; /// 帧率
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user