OLED驱动时候使用的字库,如何考虑存放的数据类型可以更好的兼容不同的单片机。
在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; // 点阵数据(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.unicode);
#else
current_unicode = font_lib.unicode; // 直接访问
#endif
if (current_unicode == unicode) {
// 复制点阵数据
uint8_t data_size = font_lib.height * ((font_lib.width + 7) / 8);
if (buf_size < data_size) return false;
for (uint8_t j = 0; j < data_size; j++) {
#if defined(PLATFORM_AVR)
buffer = pgm_read_byte(&font_lib.data);
#else
buffer = font_lib.data; // 直接访问
#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.unicode == unicode) {
return &font_lib; // 直接返回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; // 足够存放最大字符
// 通用访问方式(全平台兼容)
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;
// ...
}
#endif
}
关键设计考量
三级访问抽象:
层1:FLASH_STORAGE 统一存储修饰符
层2:FLASH_READ_* 统一读取宏
层3:font_get_char_data() 平台无关接口
AVR特殊处理:
// AVR必须使用pgm_read_*函数
#if defined(PLATFORM_AVR)
buffer = pgm_read_byte(&font_lib.data);
#endif
性能优化路径:
// 非AVR平台可直接访问Flash数据
#if !defined(PLATFORM_AVR)
const FontChar* pChar = &font_lib;
uint8_t pixel = pChar->data; // 直接访问
#endif
数据结构设计技巧:
将元数据(宽高)与点阵数据打包存储
使用unicode代替索引便于国际化
固定高度变量宽度节省存储空间 不同平台对比表
平台存储修饰符访问方式直接指针访问特殊要求
8051code const编译器自动处理✅需Keil/SDCC特定语法
PICconst __flash编译器自动处理✅XC编译器特定修饰符
AVRconst PROGMEMpgm_read_byte()函数❌必须使用访问宏
MSP430const直接访问✅无特殊要求
ARM Cortexconst直接访问✅无特殊要求
分层隔离:
应用层 (oled_driver.c)
↑
服务层 (oled_font.c) → 平台抽象层 (flash_storage.h)
↑
数据层 (font_data.c) 条件编译集中化:
所有平台相关代码集中在flash_storage.h
避免在业务逻辑中散布#ifdef 双模式访问:
// 在头文件中提供两种访问模式
#ifdef __cplusplus
extern "C" {
#endif
// 安全但较慢的通用接口
bool font_get_char_data(uint16_t code, uint8_t* buf, uint8_t size);
// 快速访问(非AVR平台)
#if !defined(PLATFORM_AVR)
const FontChar* font_get_char(uint16_t code);
#endif
#ifdef __cplusplus
}
#endif
性能关键代码:
// 对于需要高性能的场景
#if defined(PLATFORM_AVR)
// AVR专用优化代码
#else
// 通用优化代码
#endif
字库生成工具:
开发Python工具自动生成font_data.c
自动添加平台修饰符
示例命令:
python font_converter.py font.ttf -s 16 -o font_data.c --platform auto
这种设计允许:
在MSP430/ARM上获得最佳性能
保持AVR平台的兼容性
最小化8051/PIC的特殊处理
添加新平台只需修改一个头文件
通过集中处理平台差异和提供统一的抽象接口,可以创建真正可移植的OLED字库实现,显著提高代码复用率并降低维护成本。
这个方案很全面,通过统一Flash存储和访问方式,确实可以提高字库的兼容性。
还有一个就是把所有的控制放到数组里,方便用DMA。 一直找不到好用的液晶屏驱动库。 这是个难题啊,没有最好用的。 采用无符号字节型(uint8_t)存储 OLED 字库数据,按行列扫描顺序排列像素。统一使用 ASCII 码映射字符,汉字用 GB2312 编码索引。数据结构用二维数组,避免指针和动态分配。字库文件独立,通过宏定义适配不同单片机的存储地址,确保跨平台兼容性 OLED 驱动字库选择数据类型时,需优先考虑通用二进制格式,避免依赖特定单片机的自定义数据类型(如某些芯片的 “sfr” 特殊功能寄存器类型),以兼容 8 位、16 位、32 位不同架构单片机。
建议采用标准无符号整数类型(如 uint8_t、uint16_t)定义字库数据,通过头文件(如 stdint.h)统一类型声明,消除不同编译器对 “char”“int” 的位数差异影响。
字库存储结构选用数组形式,按 “行 / 列扫描顺序”(如横向 8 点 / 16 点编码)组织数据,避免使用指针或结构体嵌套,降低不同单片机内存寻址方式的适配难度。
同时,预留数据长度标识位,方便不同单片机通过通用 IO 口或 SPI/I2C 接口读取时,自动识别字符宽度 / 高度,减少硬件差异导致的兼容性问题。
使用无符号字符型(uint8_t)存储 OLED 字库数据,兼容多数单片机。按字节数组形式组织,每个字符占固定字节(如 16×16 字符占 32 字节),用 const 修饰存程序区。避免依赖特定架构类型,通过宏定义字符尺寸,便于不同单片机移植。
页:
[1]