在OLED驱动中,字库(通常是大数组形式的点阵数据)的存储和访问是跨平台移植的关键挑战。以下是如何设计兼容不同单片机架构的字库存储方案:
兼容性设计原则
统一存储位置:确保字库始终存放在Flash中
抽象访问方式:隐藏底层架构差异
最小化平台依赖:集中处理编译器特定代码
实现方案(分步骤)
步骤1:创建统一的Flash存储定义头文件
// flash_storage.h
#ifndef FLASH_STORAGE_H
#define FLASH_STORAGE_H
#include <stdint.h>
// 平台检测 ========================
#if defined(__C51__) || defined(SDCC) // 8051
#define PLATFORM_8051
#elif defined(__XC8__) || defined(__XC16__) // PIC
#define PLATFORM_PIC
#elif defined(__AVR__) // AVR
#define PLATFORM_AVR
#elif defined(__MSP430__) || defined(__TI_COMPILER_VERSION__) // MSP430
#define PLATFORM_MSP430
#endif
// Flash存储修饰符定义 ================
#if defined(PLATFORM_8051)
#define FLASH_STORAGE code const
#elif defined(PLATFORM_PIC)
#define FLASH_STORAGE const __flash
#elif defined(PLATFORM_AVR)
#include <avr/pgmspace.h>
#define FLASH_STORAGE const PROGMEM
#else // MSP430及其他
#define FLASH_STORAGE const // 默认const足够
#endif
// Flash数据访问宏 ==================
#if defined(PLATFORM_AVR)
#define FLASH_READ_BYTE(addr) pgm_read_byte(addr)
#define FLASH_READ_WORD(addr) pgm_read_word(addr)
#else
// 统一编址架构可直接访问
#define FLASH_READ_BYTE(addr) (*(const uint8_t *)(addr))
#define FLASH_READ_WORD(addr) (*(const uint16_t *)(addr))
#endif
#endif // FLASH_STORAGE_H
步骤2:定义字库数据结构
// font_data.h
#ifndef FONT_DATA_H
#define FONT_DATA_H
#include "flash_storage.h"
// 通用字库结构(示例:16x16点阵汉字)
typedef struct {
uint16_t unicode; // 字符编码
uint8_t width; // 实际宽度
uint8_t height; // 实际高度
uint8_t data[32]; // 点阵数据(16x16/8=32字节)
} FontChar;
// 声明字库(实际定义在.c文件)
extern FLASH_STORAGE FontChar font_lib[];
extern const uint16_t font_lib_size;
#endif // FONT_DATA_H
步骤3:实现字库数据(平台无关)
// font_data.c
#include "font_data.h"
// 使用统一修饰符定义字库
FLASH_STORAGE FontChar font_lib[] = {
{0x4E2D, 16, 16, { // 汉字"中"
0x00,0x40,0x00,0x40,0x7F,0xFE,0x40,0x02,
0x5F,0xFA,0x50,0x0A,0x50,0x0A,0x5F,0xFA,
0x50,0x0A,0x50,0x0A,0x5F,0xFA,0x40,0x02,
0x7F,0xFE,0x00,0x40,0x00,0x40,0x00,0x00}},
// ...其他字符
};
const uint16_t font_lib_size = sizeof(font_lib)/sizeof(FontChar);
步骤4:实现跨平台字库访问函数
// oled_font.c
#include "font_data.h"
#include "flash_storage.h"
// 获取字符点阵数据(兼容所有平台)
bool font_get_char_data(uint16_t unicode, uint8_t* buffer, uint8_t buf_size) {
for (uint16_t i = 0; i < font_lib_size; i++) {
// 平台安全的Unicode读取
uint16_t current_unicode;
#if defined(PLATFORM_AVR)
current_unicode = pgm_read_word(&font_lib[i].unicode);
#else
current_unicode = font_lib[i].unicode; // 直接访问
#endif
if (current_unicode == unicode) {
// 复制点阵数据
uint8_t data_size = font_lib[i].height * ((font_lib[i].width + 7) / 8);
if (buf_size < data_size) return false;
for (uint8_t j = 0; j < data_size; j++) {
#if defined(PLATFORM_AVR)
buffer[j] = pgm_read_byte(&font_lib[i].data[j]);
#else
buffer[j] = font_lib[i].data[j]; // 直接访问
#endif
}
return true;
}
}
return false; // 未找到字符
}
// 优化版本:直接访问(非AVR平台)
const FontChar* font_get_char(uint16_t unicode) {
#if !defined(PLATFORM_AVR)
for (uint16_t i = 0; i < font_lib_size; i++) {
if (font_lib[i].unicode == unicode) {
return &font_lib[i]; // 直接返回Flash中的指针
}
}
#endif
return NULL; // AVR不支持直接返回指针
}
步骤5:OLED驱动中使用字库(示例)
// oled_driver.c
#include "font_data.h"
void oled_draw_char(uint16_t unicode, uint8_t x, uint8_t y) {
uint8_t char_data[64]; // 足够存放最大字符
// 通用访问方式(全平台兼容)
if (font_get_char_data(unicode, char_data, sizeof(char_data))) {
// 使用char_data绘制...
}
// 优化路径(非AVR平台)
#if !defined(PLATFORM_AVR)
const FontChar* font_char = font_get_char(unicode);
if (font_char) {
// 直接访问font_char->data绘制...
// 示例:绘制一个字节
uint8_t pixel_data = font_char->data[0];
// ...
}
#endif
}
|