打印
[PIC®/AVR®/dsPIC®产品]

OLED驱动时候使用的字库,如何考虑存放的数据类型可以更好的兼容不同的单片机。

[复制链接]
165|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
huangcunxiake|  楼主 | 2025-7-17 09:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在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
}



使用特权

评论回复
沙发
huangcunxiake|  楼主 | 2025-7-17 09:01 | 只看该作者
关键设计考量
三级访问抽象:

层1:FLASH_STORAGE 统一存储修饰符

层2:FLASH_READ_* 统一读取宏

层3:font_get_char_data() 平台无关接口

使用特权

评论回复
板凳
huangcunxiake|  楼主 | 2025-7-17 09:02 | 只看该作者
AVR特殊处理:
// AVR必须使用pgm_read_*函数
#if defined(PLATFORM_AVR)
buffer[j] = pgm_read_byte(&font_lib[i].data[j]);
#endif


性能优化路径:
// 非AVR平台可直接访问Flash数据
#if !defined(PLATFORM_AVR)
const FontChar* pChar = &font_lib[index];
uint8_t pixel = pChar->data[0]; // 直接访问
#endif


数据结构设计技巧:

将元数据(宽高)与点阵数据打包存储

使用unicode代替索引便于国际化

固定高度变量宽度节省存储空间

使用特权

评论回复
地板
huangcunxiake|  楼主 | 2025-7-17 09:02 | 只看该作者
不同平台对比表
平台
存储修饰符
访问方式
直接指针访问
特殊要求
8051code const编译器自动处理需Keil/SDCC特定语法
PICconst __flash编译器自动处理XC编译器特定修饰符
AVRconst PROGMEMpgm_read_byte()函数必须使用访问宏
MSP430const直接访问无特殊要求
ARM Cortexconst直接访问无特殊要求

使用特权

评论回复
5
huangcunxiake|  楼主 | 2025-7-17 09:03 | 只看该作者
分层隔离:
应用层 (oled_driver.c)
  ↑
服务层 (oled_font.c) → 平台抽象层 (flash_storage.h)
  ↑
数据层 (font_data.c)

使用特权

评论回复
6
huangcunxiake|  楼主 | 2025-7-17 09:03 | 只看该作者
条件编译集中化:

所有平台相关代码集中在flash_storage.h

避免在业务逻辑中散布#ifdef

使用特权

评论回复
7
huangcunxiake|  楼主 | 2025-7-17 09:04 | 只看该作者
双模式访问:
// 在头文件中提供两种访问模式
#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字库实现,显著提高代码复用率并降低维护成本。

使用特权

评论回复
8
破晓战神| | 2025-7-18 11:50 | 只看该作者
这个方案很全面,通过统一Flash存储和访问方式,确实可以提高字库的兼容性。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

221

主题

3654

帖子

11

粉丝