关于字库中字模的偏移量算法

本文是写给这篇文章的博主的:

http://blog.csdn.net/king_bingge/article/details/8779494

 

CSDN博客回复限制1000个字,装不下这些东西;发私信又得互相关注才行,留言也有字数限制发不出去,加博主的QQ呢他又设置了验证问题,我实在猜不出他的名字,所以,还是直接在这儿发一篇吧。

不知道博主的公式现在看懂了没有,我假装他仍然没懂,来帮他解答一下吧。以下文字仅代表个人意见。
博主曾转过一篇文章叫《信息处理交换用汉字编码字符集 GB2312-80》相信这一篇文章他肯定已经看懂了。

汉字的输入法中有一款叫做区位码输入法,就是根据他转载的这篇文章中的分区分位的原理来的。

我小时候曾经整理过常用汉字的区位码,记得第一个汉字是“啊”,区位码是1601。当时印象特别深的就是,区位码是从01到94共94个区,每个区都是从01位开始到94位,共94个字符。我印象中前3个区是标点符号,中间还有一些比如①②③之类的特殊符号区。汉字是从16区开始的。这个我们就不去管它了。

先来具体说说区位码和汉字内码的关系。

汉字的内码分为2个字节,高字节对应区位码的区,低字节对应位,但是要注意的是并不是直接相等,而是,2个字节分别是区码和位码加上160。至于为什么要加160我就不知道了,可能是为了区分ASCII的127个字符吧,但我认为+128就可以区分了,可它还是加了160。这也正是为什么区位码只能有94个区的原因。160+94=254,一字节最大是255,但255是个特殊值(二进制中的全1和全0都是特殊值)不能用的。

这样的话,以“啊”为例,它的内码的算法,高字节=160+区码16=176,低字节=160+位码1=161,十六进制就是0xB0A1,十进制是176*256+161=45217,验证一下,打开记事本,按住ALT键不要松手,用数字键区(注意只能是大键盘右侧的小键盘即NumLock数字键区,笔记本上可能不行),输入45217,就会出现“啊”字。

另一个例子,我姓韩,“韩”的区位码是2611,它的内码,高字节=160+区码16=186,低字节=160+位码11=171,十六进制是0xBAAB,十进制就是 (26+160)*256+(11+160)=47787,打开记事本,ALT+47787,记事本上可以出现“韩”字。

好,接下来说说字模。既然是16x16的字库,可以想到的是,每个汉字占用的是16行*16列=16行*8列*2,即16*2字节。

偏移量计算就简单了,顺着说的话就是,第n个文字的偏移是offset=(n-1)*16*2。

我们假设字库是从第1区第1位开始的,每个区94个文字,那么第j区第k位的文字,它就相当于第(j-1)*94+k个文字,套用到上面的式子,n=(j-1)*94+k,它的字模偏移量是offset=((j-1)*94+k-1)*16*2。

但是上面说了,第一个汉字“啊”它在16区,而不是在1区。1~15区没有汉字,所以呢,有的字库文件为了节省空间就把前15个区的字模去掉了(这实在不应该,因为这样就把全角的标点符号给去掉了,全角标点无法显示),这样的话,第j区(j≥16)第k位的文字,就相当于字库中的第 (j-16)*94+k 个文字了,它的字模偏移量就是 offset=((j-16)*94+k-1)*16*2。

结合内码和区位码的关系,高字节hi低字节low的汉字,对应的内码是:

j=hi-160

k=low-160

字模偏移量就是


1区开始的字库:offset=((j-1)*94+k-1)*16*2=((hi-160-1)*94+low-160-1)*16*2=((hi-0xa1)*94+low-0xa1)*16*2

16区开始的字库:offset=((j-16)*94+k-1)*16*2=((hi-160-16)*94+low-160-1)*16*2=((hi-0xb0)*94+low-0xa1)*16*2

举例子,“啊”

在1区开始的字库中,偏移为((176-0xa1)*94+161-0xa1)*16*2=45120=0xb040

在16区开始的字库中,偏移为((176-0xb0)*94+161-0xa1)*16*2=0

“韩”

在1区开始的字库中,偏移为((186-0xa1)*94+171-0xa1)*16*2=75520=0x012700

在16区开始的字库中,偏移为((186-0xb0)*94+171-0xa1)*16*2=30400=0x76c0

 

看一下博主的2句代码:

    pos = ((High8bit-0xb0)*94+Low8bit-0xa1)*2*16;

这一句很明显是用的16区开始的字库;  

    //    pos = ((High8bit-0xa0)*94+Low8bit-0xa0)*2*16;

这句看上去是用的1区开始的字库,但是里面的高位应该是减去0xa1而不是0xa0,否则是取不到对应的字的,会取到后一个区同一个位的字。

 

就是这些了。我的QQ是34368900,希望能多交流交流,我刚入门STM32,有很多问题需要博主赐教。

<think>好的,我现在要帮助用户解决关于STM32 SD卡字体库实现的问题。首先,我需要理解用户的需求。用户提到他们参考了引用[1]和[2],其中涉及基于STM32的小说阅读器和OLED屏与SD卡的结合使用,特别是字库的取模方法。用户现在想寻找具体的实现方案,包括如何利用SD卡存储字体库并在STM32上显示。 首先,我应该回忆STM32处理字库的常见方法。通常,字库可以存储在外部存储器如SD卡中,尤其是中文字库,因为其数据量较大,内部Flash可能不够。用户提到的PCtoLCD2002软件用于生成字模,这可能是关键步骤。 接下来,我需要考虑如何将生成的字体文件存储在SD卡中。用户可能需要将字库文件(比如GB2312字库)按特定格式(如二进制或数组形式)保存到SD卡中。STM32通过文件系统(如FATFS)读取这些文件。 然后,字库索引的建立很重要。GB2312编码有区码和位码,需要计每个字符在字库中的偏移量,这样才能快速定位到SD卡中的对应位置。例如,计偏移量公式:offset = ((区码-1)*94 + 位码-1) * 字体大小。 显示部分需要结合显示屏驱动,比如OLED或TFT LCD。用户提到他们使用SPI接口的OLED,这可能涉及SPI通信和屏幕的初始化配置。读取SD卡时,需要使用FATFS库来挂载文件系统,打开文件并读取特定位置的数据。 还需要考虑缓存机制,因为频繁读取SD卡可能影响性能。可以设置一个缓存区,存储最近使用的字符点阵数据,减少IO操作。 此外,用户可能需要示例代码的结构,比如初始化SD卡和文件系统,读取字库的函数,以及显示字符的函数。确保代码中正确使用硬件接口,如SPI或SDIO,并处理可能的错误情况。 最后,需要提醒用户注意字库文件的大小和存储位置,以及不同显示器的扫描模式(如逐行或逐列),这些会影响取模设置。同时,优化读取速度和内存使用是关键,可能需要调整缓存策略或使用更高效的文件访问方法。 总结步骤:生成字库文件、存储到SD卡、建立索引、读取并显示、优化缓存。需要结合用户提供的引用内容,特别是PCtoLCD2002的使用和SPI/SD卡操作,确保实现方案可行。</think>### STM32 SD卡字库实现方法 #### 1. 字库生成与存储 使用 **PCtoLCD2002** 生成字模数据: - 选择 **字符模式**,设置字体属性(如宋体/12px) - 配置取模方式为 **纵向取模,字节倒序**(需匹配显示器扫描模式) - 生成二进制字库文件保存到SD卡,文件路径如:`/FONT/GBK12.FON`[^2] ```c /* 字库结构示例 */ typedef struct { uint16_t font_width; // 字符宽度 uint16_t font_height; // 字符高度 uint32_t file_offset; // 文件起始偏移量 } FontLib_TypeDef; ``` #### 2. 硬件连接配置 | SD卡引脚 | STM32连接 | |---------|-----------| | CLK | SPI_SCK | | CMD | SPI_MOSI | | DAT0 | SPI_MISO | | CS | GPIO输出 | ```c // SPI初始化示例(SDIO模式更高效) SD_HandleTypeDef hsd; void HAL_SD_MspInit(SD_HandleTypeDef* hsd) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF12_SDIO; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); } ``` #### 3. 文件系统集成 使用 **FATFS** 中间件挂载SD卡: ```c FATFS fs; // 文件系统对象 FIL font_file; // 字库文件对象 FRESULT res = f_mount(&fs, "", 1); // 挂载SD卡 if(res == FR_OK){ res = f_open(&font_file, "0:/FONT/GBK12.FON", FA_READ); } ``` #### 4. 字符读取算法 ```c // 计GB2312字符偏移量 uint32_t Get_CharOffset(uint8_t* gb_code) { uint8_t qh = gb_code[0] - 0xA0; uint8_t wh = gb_code[1] - 0xA0; return ((qh-1)*94 + (wh-1)) * FONT_SIZE; } // 读取点阵数据 FRESULT Read_FontData(uint8_t* buffer, uint32_t offset) { f_lseek(&font_file, offset); UINT bytes_read; return f_read(&font_file, buffer, FONT_SIZE, &bytes_read); } ``` #### 5. OLED显示集成 结合引用[2]的SPI OLED驱动: ```c void OLED_ShowChar(uint16_t x, uint16_t y, uint8_t* font_data) { OLED_SetWindow(x, y, x+FONT_W-1, y+FONT_H-1); for(int i=0; i<FONT_SIZE; i++){ OLED_WriteData(font_data[i]); } } ``` #### 6. 性能优化 - **缓存机制**:建立LRU缓存存储常用字符 - **预读取**:利用SD卡多块读取特性 - **DMA传输**:SPI传输使用DMA减少CPU占用 ```c #define CACHE_SIZE 50 typedef struct { uint16_t gb_code; uint8_t bitmap[32]; } CharCache_TypeDef; CharCache_TypeDef font_cache[CACHE_SIZE]; ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值