OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3495|回复: 4

关于字库地址安排的问题

[复制链接]

8

主题

124

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
212
金钱
212
注册时间
2015-8-1
在线时间
7 小时
发表于 2015-8-20 20:37:11 | 显示全部楼层 |阅读模式

今天看汉字库示例,发现字库在扩展内存中的布局是一个一个紧挨着的,最开始是字库的基本信息。但是这样的安排有个问题,每个字库起始地址都不是 4k 的整数倍。这样导致随后更新该字库时,每次写 4096 字节时,都会横跨两个扇区,也就是写一个扇区都会对两个扇区进行读写甚至擦除操作。(即一个扇区可能会被擦除两遍)。

因此,考虑牺牲一点空间,让字库更新更顺畅些。这里就要求每个字库起始地址都是 4k 的整数倍即可。

相关的核心代码如下

[mw_shl_code=c,true]bool _UpdateFontMatrix(u8 aFontIndex) { u8 *lBuffer; u32 lBufferLength; u32 lSize; u32 lByteIndex; u32 lBaseAddress = 0; lBuffer = MEM_Alloc(InSRAM, ExFLASH_SECTOR_SIZE); // 分配 4096 个字节空间 if(lBuffer == NULL) return FALSE; oDebug("@"); // 从串口读取点阵大小 lSize = 0; while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != SET); lSize |= (u32)(USART_ReceiveData(USART1) & 0xFF); while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != SET); lSize |= (u32)(USART_ReceiveData(USART1) & 0xFF) << 8; while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != SET); lSize |= (u32)(USART_ReceiveData(USART1) & 0xFF) << 16; while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != SET); lSize |= (u32)(USART_ReceiveData(USART1) & 0xFF) << 24; oDebug("@"); switch(aFontIndex) { case 0: // 更新UNIGBK.BIN gGBKInfo.gbkUniAddr = ExFLASH_FONT_BASE + (sizeof(gGBKInfo) + ExFLASH_SECTOR_SIZE - 1) / ExFLASH_SECTOR_SIZE * ExFLASH_SECTOR_SIZE; // 信息头之后,紧跟UNIGBK转换码表 gGBKInfo.gbkUniSize = lSize; // UNIGBK大小 lBaseAddress = gGBKInfo.gbkUniAddr; break; case 16: gGBKInfo.gbk16Addr = gGBKInfo.gbkUniAddr + (gGBKInfo.gbkUniSize + ExFLASH_SECTOR_SIZE - 1) / ExFLASH_SECTOR_SIZE * ExFLASH_SECTOR_SIZE; // UNIGBK之后,紧跟GBK16字库,地址对齐 4096 字节。 gGBKInfo.gbk16Size = lSize; // GBK16字库大小 lBaseAddress = gGBKInfo.gbk16Addr; // GBK16的起始地址 break; case 24: gGBKInfo.gbk24Addr = gGBKInfo.gbk16Addr + (gGBKInfo.gbk16Size + ExFLASH_SECTOR_SIZE - 1) / ExFLASH_SECTOR_SIZE * ExFLASH_SECTOR_SIZE; // GBK16之后,紧跟GBK24字库,地址对齐 4096 字节。 gGBKInfo.gbk24Size = lSize; // GBK24字库大小 lBaseAddress = gGBKInfo.gbk24Addr; // GBK24的起始地址 break; case 32: gGBKInfo.gbk32Addr = gGBKInfo.gbk24Addr + (gGBKInfo.gbk24Size + ExFLASH_SECTOR_SIZE - 1) / ExFLASH_SECTOR_SIZE * ExFLASH_SECTOR_SIZE; // GBK24之后,紧跟GBK32字库,地址对齐 4096 字节。 gGBKInfo.gbk32Size = lSize; // GBK32字库大小 lBaseAddress = gGBKInfo.gbk32Addr; // GBK32的起始地址 break; } lBufferLength = 0; for(lByteIndex = 1; lByteIndex <= lSize; lByteIndex++) { while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != SET); lBuffer[lBufferLength++] = USART_ReceiveData(USART1) & 0xFF; // 缓冲区满 if(lBufferLength == ExFLASH_SECTOR_SIZE || lByteIndex == lSize) { ExFLASH_Write(lBuffer, lBaseAddress + lByteIndex - lBufferLength, lBufferLength); // 写入 4096 个数据 lBufferLength = 0; LCD_DrawNum(FONTSIZE_16, lByteIndex * 100 / lSize, 138, 221, 3, FALSE, COLOR_BLACK, COLOR_WHITE); oDebug("@"); } } MEM_Free(InSRAM, lBuffer); // 释放内存 return TRUE; }[/mw_shl_code]


自己在Windows 上写了个 .Net 程序,通过串口将字库发送到战舰,更新字库成功。

顺便解释下,我的字库去掉了12点阵的,加了个32点阵的,字库起始从 8M 开始。供需空间将近 6M。

有软件开发经验,从0硬件基础学习STM32开发。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

8

主题

124

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
212
金钱
212
注册时间
2015-8-1
在线时间
7 小时
 楼主| 发表于 2015-8-20 20:46:16 | 显示全部楼层

另外,有时会重刷字库,但是大部分字库内容可能不会变化(比如有几个字的点阵有问题),这样更新整个字库时,大部分其实和原始扇区内容是一致的。所以,在判断是否需要擦除扇区前,可以加一步:判断是否要写入的内容和原先内容一致。如果一致,干脆就直接跳过该扇区了。

相关代码如下

[mw_shl_code=c,true]u8 gExFLASH_SectorBuffer[4096]; void ExFLASH_Write(u8* aBuffer, u32 aAddress, u16 aWriteLength) { u16 i; u8* lSectorBuffer = gExFLASH_SectorBuffer; u32 lSectorIndex = aAddress / ExFLASH_SECTOR_SIZE; // 扇区地址 u16 lOffsetInSector = aAddress % ExFLASH_SECTOR_SIZE; // 在扇区内的偏移 u16 lRemainBytesInSector = ExFLASH_SECTOR_SIZE - lOffsetInSector; // 扇区剩余空间大小 if(aWriteLength <= lRemainBytesInSector) lRemainBytesInSector = aWriteLength; while(1) { ExFLASH_Read(lSectorBuffer, lSectorIndex * ExFLASH_SECTOR_SIZE, ExFLASH_SECTOR_SIZE); // 读出整个扇区的内容 // 判断是否要写入的数据与该扇区原先的数据一致 for(i = 0; i < lRemainBytesInSector; i++) { if(lSectorBuffer[lOffsetInSector + i] != aBuffer) // 如果要写入的数据与原先的不同 break; } // 不一致 if(i < lRemainBytesInSector) { // 判断要写入的扇区位置是否已经擦除 for(i = 0; i < lRemainBytesInSector; i++) { if(lSectorBuffer[lOffsetInSector + i] != 0xFF) break; } // 需要擦除 if(i < lRemainBytesInSector) { ExFLASH_EraseSector(lSectorIndex); // 擦除这个扇区 for(i = 0; i < lRemainBytesInSector; i++) // 复制待写入数据 lSectorBuffer[lOffsetInSector + i] = aBuffer; ExFLASH_WritePages(lSectorBuffer, lSectorIndex * ExFLASH_SECTOR_SIZE, ExFLASH_SECTOR_SIZE); // 写入整个扇区 } else ExFLASH_WritePages(aBuffer, aAddress, lRemainBytesInSector); // 写已经擦除了的,直接写入扇区剩余区间. } aWriteLength -= lRemainBytesInSector; if(aWriteLength == 0) // 写入结束了 break; lSectorIndex++; // 扇区地址增1 lOffsetInSector = 0; // 偏移位置为0 aBuffer += lRemainBytesInSector; // 指针偏移 aAddress += lRemainBytesInSector; // 写地址偏移 lRemainBytesInSector = (aWriteLength > ExFLASH_SECTOR_SIZE) ? ExFLASH_SECTOR_SIZE : aWriteLength; }; }[/mw_shl_code]

以上代码均修改自原子的示例代码,并测试成功。不过没有完全测试所有情况,也许有 BUG 也说不准。

有软件开发经验,从0硬件基础学习STM32开发。
回复 支持 反对

使用道具 举报

8

主题

124

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
212
金钱
212
注册时间
2015-8-1
在线时间
7 小时
 楼主| 发表于 2015-8-20 20:57:58 | 显示全部楼层

这是更新字体过程中的截图,相关有些信息被隐藏。

有软件开发经验,从0硬件基础学习STM32开发。
回复 支持 反对

使用道具 举报

8

主题

124

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
212
金钱
212
注册时间
2015-8-1
在线时间
7 小时
 楼主| 发表于 2015-8-20 21:09:23 | 显示全部楼层

这个是刷完汉字字库后,战舰启动后通过串口1输出的初始化信息界面(通过PC上的串口接收),可以通过汉字字库初始化信息后面显示的相关信息判断字库地址(一共四个,UNIGBK,GBK16,GBK24,GBK32)


有软件开发经验,从0硬件基础学习STM32开发。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2015-8-21 23:12:14 | 显示全部楼层
谢谢分享....
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-16 07:28

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表