基于探索者串口更新字库笔记
W25Q128
通信方式是SPI,读数据可以从任何地方读,写数据和擦出数据需要按照页或者扇区或者簇为单位进行。
写数据:一次最多写一页,如果超出一页数据长度,则分几次完成。本芯片一个扇区为4096个字节,那么需要写16页,要进行至少16次按页写数据。
擦数据:擦数据的最小单位是一个扇区,也可以直接擦出整个芯片。
容量:16Mbytes 一页为256个字节 一个扇区为4096个字节
额,还没有自己独立写驱动代码的能力,直接使用的正点原子例程。
DMA
DMA全称DirectMemoryAccess,即直接存储器访问。
DMA传输将数据从一个地址空间复制到另一个地址空间。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。
DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和I0设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。
作用: 为CPU减负。
串口配置
重要代码
DMA_InitTypeDef DMA_InitStructure;
//DMA配置
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2 时钟使能
DMA_DeInit(DMA2_Stream5); //恢复默认值 串口1接收是DMA2数据流2通道4
while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE){}//等待 DMA 可配置
/* 配置 DMA Stream */
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;//DMA 外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)USART1_Rece_Buf0;//DMA 存储器 0 地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//外设到存储器模式
DMA_InitStructure.DMA_BufferSize = USART1_DMA_Len;//数据传输量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8 位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据长度:8 位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//注意:这里设置为循环模式,不然不能启动第二次传输
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//FIFO 模式禁止
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;//FIFO 阈值
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
DMA_DoubleBufferModeConfig(DMA2_Stream5, (uint32_t)USART1_Rece_Buf1, DMA_Memory_0); //USART1_Rece_Buf0 先缓冲
DMA_DoubleBufferModeCmd(DMA2_Stream5, ENABLE);
DMA_Init(DMA2_Stream5, &DMA_InitStructure);//初始化 DMA Stream
DMA_Cmd(DMA2_Stream5, ENABLE); //开启 DMA 传输
DMA_ITConfig(DMA2_Stream5,DMA_IT_TC,ENABLE); //使能DMA传输完成中断
//开启一次 DMA 传输
//DMA_Streamx:DMA 数据流,DMA1_Stream0~7/DMA2_Stream0~7
//ndtr:数据传输量
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)
{
DMA_Cmd(DMA_Streamx, DISABLE); //关闭 DMA 传输
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){} //确保 DMA 可以被设置
DMA_SetCurrDataCounter(DMA_Streamx,ndtr); //数据传输量
DMA_Cmd(DMA_Streamx, ENABLE); //开启 DMA 传输
}
void USART1_IRQHandler(void) //串口1中断服务程序
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
}
}
void DMA2_Stream5_IRQHandler(void)
{
if(DMA_GetFlagStatus(DMA2_Stream5,DMA_FLAG_TCIF5)==SET)
{
DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5);
//**********************数据帧处理******************//
if(1==DMA_GetCurrentMemoryTarget(DMA2_Stream5))
GBK_BUF_Flag=0;
else
GBK_BUF_Flag=1;
//**************************************************//
}
}
void send(u8 ch)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
}
DMA配置过程
更新过程
使用串口将GBK文件通过DMA跳过CPU直接写入W25Q128
重要代码:
“fontupd.h”
//字体信息保存地址,占33个字节,第1个字节用于标记字库是否存在.后续每8个字节一组,分别保存起始地址和文件大小
extern u32 FONTINFOADDR;
//字库信息结构体定义
//用来保存字库基本信息,地址,大小等
//增加新的字库需要在结构体中添加所需的字体地址及大小
__packed typedef struct
{
u8 fontok; //字库存在标志,0XAA,字库正常;其他,字库不存在
u32 ugbkaddr; //unigbk的地址
u32 ugbksize; //unigbk的大小
u32 f12addr; //gbk12地址
u32 gbk12size; //gbk12的大小
u32 f16addr; //gbk16地址
u32 gbk16size; //gbk16的大小
u32 f24addr; //gbk24地址
u32 gkb24size; //gbk24的大小
u32 f32addr; //gbk24地址
u32 gkb32size; //gbk24的大小
}_font_info;
extern _font_info ftinfo; //字库信息结构体
u32 fupd_prog(u16 x,u16 y,u8 size,u32 fsize,u32 pos); //显示更新进度
u8 updata_fontx(u16 x,u16 y,u8 size,u8 fx); //更新指定字库
u8 update_font(u16 x,u16 y,u8 size); //更新全部字库
u8 font_init(void); //初始化字库
“fontupd.c”
//字库区域占用的总扇区数大小(3个字库+unigbk表+字库信息=3238700字节,约占791个W25QXX扇区)
#define FONTSECSIZE 1539 //1539个扇区6303000字节 必须能够整除4096 因为一个扇区4096
//字库存放起始地址
#define FONTINFOADDR 1024*1024*0 //字库存放首地址
//定义各个字库的大小
#define UNIGBK 171*1024 //171KB
#define GBK12_FONSIZE 562*1024 //562KB
#define GBK16_FONSIZE 749*1024 //749KB
#define GBK24_FONSIZE 1684*1024 //1684KB
#define GBK32_FONSIZE 2993*1024 //1684KB
//用来保存字库基本信息,地址,大小等
_font_info ftinfo;
//更新某一个
//x,y:坐标
//size:字体大小
//fx:更新的内容 0,ungbk;1,gbk12;2,gbk16;3,gbk24;
//返回值:0,成功;其他,失败.
u8 updata_fontx(u16 x,u16 y,u8 size,u8 fx)
{
u32 flashaddr=0;
u8 res;
u32 offx=0;
u32 fsize=0;
switch(fx)
{
//添加新字库需要修改的地方
case 0: //更新UNIGBK.BIN
ftinfo.ugbkaddr=FONTINFOADDR+sizeof(ftinfo); //信息头之后,紧跟UNIGBK转换码表
fsize=ftinfo.ugbksize=UNIGBK; //UNIGBK大小
flashaddr=ftinfo.ugbkaddr;
printf("Please send UNIGBK.bin\r\n");
break;
case 1:
ftinfo.f12addr=ftinfo.ugbkaddr+ftinfo.ugbksize; //UNIGBK之后,紧跟GBK12字库
fsize=ftinfo.gbk12size=GBK12_FONSIZE; //GBK12字库大小
flashaddr=ftinfo.f12addr; //GBK12的起始地址
printf("Please send GBK12.FON\r\n");
break;
case 2:
ftinfo.f16addr=ftinfo.f12addr+ftinfo.gbk12size; //GBK12之后,紧跟GBK16字库
fsize=ftinfo.gbk16size=GBK16_FONSIZE; //GBK16字库大小
flashaddr=ftinfo.f16addr; //GBK16的起始地址
printf("Please send GBK16.FON\r\n");
break;
case 3:
ftinfo.f24addr=ftinfo.f16addr+ftinfo.gbk16size; //GBK16之后,紧跟GBK24字库
fsize=ftinfo.gkb24size=GBK24_FONSIZE; //GBK24字库大小
flashaddr=ftinfo.f24addr; //GBK24的起始地址
printf("Please send GBK24.FON\r\n");
break;
case 4:
ftinfo.f32addr=ftinfo.f24addr+ftinfo.gkb24size; //GBK16之后,紧跟GBK24字库
fsize=ftinfo.gkb32size=GBK32_FONSIZE; //GBK24字库大小
flashaddr=ftinfo.f32addr; //GBK24的起始地址
printf("Please send GBK32.FON\r\n");
break;
}
fupd_prog(x,y,size,fsize,offx); //进度显示
while(1)//死循环执行
{
if(GBK_OVER_Flag)
GBK_OVER_Flag++;
if(GBK_BUF_Flag!=2)
{
GBK_OVER_Flag=1;
if(GBK_BUF_Flag==0)
W25QXX_Write(USART1_Rece_Buf0,offx+flashaddr,USART1_DMA_Len); //开始写入USART1_DMA_Len个数据
else if(GBK_BUF_Flag==1)
W25QXX_Write(USART1_Rece_Buf1,offx+flashaddr,USART1_DMA_Len); //开始写入USART1_DMA_Len个数据
offx+=USART1_DMA_Len;
GBK_BUF_Flag=2;
fupd_prog(x,y,size,fsize,offx); //进度显示
}
delay_us(100);
if(GBK_OVER_Flag>(WATE_TIME+10)*10) //超过正常时间10ms则说明此字库发送完毕
break;
}
if(DMA_GetCurrentMemoryTarget(DMA2_Stream5)==1)
W25QXX_Write(USART1_Rece_Buf1,offx+flashaddr,USART1_DMA_Len-DMA_GetCurrDataCounter(DMA2_Stream5));//将DMA最后的一帧数据写入FLASH
else
W25QXX_Write(USART1_Rece_Buf0,offx+flashaddr,USART1_DMA_Len-DMA_GetCurrDataCounter(DMA2_Stream5));//将DMA最后的一帧数据写入FLASH
printf("This Font updated successfull!\r\n");
uart_init(BAUD_RATE); //重新初始化串口及DMA
GBK_OVER_Flag=0;
return res;
}
//更新字体文件,UNIGBK,GBK12,GBK16,GBK24一起更新
//x,y:提示信息的显示地址
//size:字体大小
//提示信息字体大小
//返回值:0,更新成功;
// 其他,错误代码.
u8 update_font(u16 x,u16 y,u8 size)
{
u16 i,j;
LCD_ShowString(x,y,240,320,size,(u8*)"Erasing sectors... ");//提示正在擦除扇区
for(i=0;i<FONTSECSIZE;i++) //先擦除字库区域,提高写入速度
{
fupd_prog(x+20*size/2,y,size,FONTSECSIZE,i);//进度显示
W25QXX_Read((u8*)USART1_Rece_Buf1,((FONTINFOADDR/4096)+i)*4096,4096);//读出整个扇区的内容(借用一下DMA缓冲区)
for(j=0;j<4096;j++)//校验数据
{
if(USART1_Rece_Buf1[j]!=0XFF)break;//需要擦除
}
if(j!=4096)W25QXX_Erase_Sector((FONTINFOADDR/4096)+i); //需要擦除的扇区
}
delay_ms(100);
LCD_ShowString(x,y,240,320,size,(u8*)"Updating UNIGBK.BIN ");
updata_fontx(x+20*size/2,y,size,0); //更新GBK12.FON
LCD_ShowString(x,y,240,320,size,(u8*)"Updating GBK12.FON ");
updata_fontx(x+20*size/2,y,size,1); //更新GBK12.FON
LCD_ShowString(x,y,240,320,size,(u8*)"Updating GBK16.FON ");
updata_fontx(x+20*size/2,y,size,2); //更新GBK16.FON
LCD_ShowString(x,y,240,320,size,(u8*)"Updating GBK24.FON ");
updata_fontx(x+20*size/2,y,size,3); //更新GBK16.FON
LCD_ShowString(x,y,240,320,size,(u8*)"Updating GBK32.FON ");
updata_fontx(x+20*size/2,y,size,4); //更新GBK16.FON
//全部更新好了
ftinfo.fontok=0XAA;
W25QXX_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo)); //保存字库信息
printf("All Font file updated successfull!!!\r\n");
LCD_Clear(WHITE);
return 0;
}
使用字库相关代码
text.h
void Get_HzMat(unsigned char *code,unsigned char *mat,u8 size); //得到汉字的点阵码
void Show_Font(u16 x,u16 y,u8 *font,u8 size,u8 mode); //在指定位置显示一个汉字
void Show_Str(u16 x,u16 y,u16 width,u16 height,u8*str,u8 size,u8 mode); //在指定位置显示一个字符串
void Show_Str_Mid(u16 x,u16 y,u8*str,u8 size,u8 len);
text.c
//code 字符指针开始
//从字库中查找出字模
//code 字符串的开始地址,GBK码
//mat 数据存放地址 (size/8+((size%8)?1:0))*(size) bytes大小
//size:字体大小
void Get_HzMat(unsigned char *code,unsigned char *mat,u8 size)
{
unsigned char qh,ql;
unsigned char i;
unsigned long foffset;
u8 csize=(size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数
qh=*code;
ql=*(++code);
if(qh<0x81||ql<0x40||ql==0xff||qh==0xff)//非 常用汉字
{
for(i=0;i<csize;i++)*mat++=0x00;//填充满格
return; //结束访问
}
if(ql<0x7f)ql-=0x40;//注意!
else ql-=0x41;
qh-=0x81;
foffset=((unsigned long)190*qh+ql)*csize; //得到字库中的字节偏移量
switch(size)
{
case 12:
W25QXX_Read(mat,foffset+ftinfo.f12addr,csize);
break;
case 16:
W25QXX_Read(mat,foffset+ftinfo.f16addr,csize);
break;
case 24:
W25QXX_Read(mat,foffset+ftinfo.f24addr,csize);
break;
case 32:
W25QXX_Read(mat,foffset+ftinfo.f32addr,csize);
break;
}
}
//在指定位置开始显示一个字符串
//支持自动换行
//(x,y):起始坐标
//width,height:区域
//str :字符串
//size :字体大小
//mode:0,非叠加方式;1,叠加方式
void Show_Str(u16 x,u16 y,u16 width,u16 height,u8*str,u8 size,u8 mode)
{
u16 x0=x;
u16 y0=y;
u8 bHz=0; //字符或者中文
while(*str!=0)//数据未结束
{
if(!bHz)
{
if(*str>0x80)bHz=1;//中文
else //字符
{
if(x>(x0+width-size/2))//换行
{
y+=size;
x=x0;
}
if(y>(y0+height-size))break;//越界返回
if(*str==13)//换行符号
{
y+=size;
x=x0;
str++;
}
else LCD_ShowChar(x,y,*str,size,mode);//有效部分写入
str++;
x+=size/2; //字符,为全字的一半
}
}else//中文
{
bHz=0;//有汉字库
if(x>(x0+width-size))//换行
{
y+=size;
x=x0;
}
if(y>(y0+height-size))break;//越界返回
Show_Font(x,y,str,size,mode); //显示这个汉字,空心显示
str+=2;
x+=size;//下一个汉字偏移
}
}
}