用正点原子的mini板子做EMWIN显示汉字需要把字库放在外置的FLASH中,给的例程是先将做好的字库放到SD卡中,然后使用FATFS把SD卡的字库拷贝到FLASH中,手上没有SD卡,考虑用串口+DMA传输将字库直接拷到FLASH中。
主要注意取模方式的选择,EMWIN取模方式跟正点原子基础例程汉字汉字显示的取模不同,根据需要自行选择。
将做好的字库放在一个文件夹中,共三种大小的字体。
同时右键->属性 可查看字库的大小,这个大小在程序里面会用上。
注意是大小,不是占用空间。
//串口1初始化 采用了串口空闲中断+DMA双缓冲接收的方式
void uart_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启串口空闲中断
USART_ClearITPendingBit(USART1,USART_IT_TC);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); //起用DMA传输
USART_Cmd(USART1, ENABLE); //使能串口1
}
//DMA配置函数
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
DMA_DeInit(DMA_CHx); //将DMA的通道x寄存器重设为缺省值
DMA1_MEM_LEN=cndtr;
DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,外设->内存
DMA_InitStructure.DMA_BufferSize = cndtr; //DMA通道的DMA缓存的大小
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_Normal; //工作在正常缓存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
NVIC_InitStructure.NVIC_IRQChannel=DMA1_Channel5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ITConfig(DMA_CHx, DMA_IT_TC, ENABLE); //使能传输完成中断
DMA_Init(DMA_CHx, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
DMA_Cmd(DMA_CHx, ENABLE);
}
//开启一次DMA传输
// buf_flag - 标记第几个接收缓冲
// DMA_RCV_FLAG - 标记 往FLASH里面写入哪一个缓冲的数据
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
DMA_Cmd(DMA_CHx, DISABLE ); //关闭USART1 RX DMA1 所指示的通道
if (buf_flag ==0 ){
//切换接收缓冲
MYDMA_Config(DMA1_Channel5,(u32)&USART1->DR,(u32)Font_Buf1,RECV_LEN);
buf_flag=1;
DMA_RCV_FLAG = 1;
}
else //切换接收缓冲
if (buf_flag == 1){
buf_flag=0;
MYDMA_Config(DMA1_Channel5,(u32)&USART1->DR,(u32)Font_Buf,RECV_LEN);
DMA_RCV_FLAG = 2;
}
}
void USART1_IRQHandler(void)
//串口1中断服务程序
{
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) {
/*接收完一帧数据,切换接收缓冲区 */
MYDMA_Enable(DMA1_Channel5);
/*清除空闲中断*/
USART1->SR;
USART1->DR;
}
}
//字库存放起始地址
#define FONTINFOADDR (4916+100)*1024 //MiniSTM32是从4.8M+100K地址开始的
//字库信息结构体.
//用来保存字库基本信息,地址,大小等
_font_info ftinfo;
//显示当前字体更新进度
//x,y:坐标
//size:字体大小
//fsize:整个文件大小
//pos:当前文件指针位置
u32 fupd_prog(u16 x,u16 y,u8 size,u32 fsize,u32 pos)
{
float prog;
u8 t=0XFF,res =0;
prog=(float)pos/fsize;
prog*=100;
if(t!=prog)
{
//LCD_ShowString(x+3*size/2,y,size,"%",1);
t=prog;
if(t>=100){t=100;res=2;}
LCD_ShowNum(x,y,t,3,size);//显示数值
}
return res;
}
//更新某一个
//x,y:坐标
//size:字体大小
//fxpath:路径
//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 = 1;
u32 offx=0;
u32 file_size = 0;
switch(fx)
{
case 0: //更新UNIGBK.BIN
// ftinfo.ugbkaddr=FONTINFOADDR+sizeof(ftinfo); //信息头之后,紧跟UNIGBK转换码表
// ftinfo.ugbksize=fftemp->fsize; //UNIGBK大小
// flashaddr=ftinfo.ugbkaddr;
break;
case 1:
ftinfo.f12addr=ftinfo.ugbkaddr+ftinfo.ugbksize; //UNIGBK之后,紧跟GBK12字库
ftinfo.gbk12size=574560; //GBK12字库大小
flashaddr=ftinfo.f12addr; //GBK12的起始地址
file_size = ftinfo.gbk12size;
break;
case 2:
ftinfo.f16addr=ftinfo.f12addr+ftinfo.gbk12size; //GBK12之后,紧跟GBK16字库
ftinfo.gbk16size=766080; //GBK16字库大小
flashaddr=ftinfo.f16addr; //GBK16的起始地址
file_size = ftinfo.gbk16size;
break;
case 3:
ftinfo.f24addr=ftinfo.f16addr+ftinfo.gbk16size; //GBK16之后,紧跟GBK24字库
ftinfo.gkb24size=1723680; //GBK24字库大小
flashaddr=ftinfo.f24addr; //GBK24的起始地址
file_size = ftinfo.gkb24size;
break;
}
while(1)//死循环执行
{
if(DMA_RCV_FLAG ==1){
DMA_RCV_FLAG=0;
SPI_Flash_Write(Font_Buf,offx+flashaddr,1024); //从0开始写入1024个数据
offx+=1024;
res = fupd_prog(x,y,size,file_size,offx); //进度显示
memset(Font_Buf,0,RECV_LEN);
}
else if(DMA_RCV_FLAG == 2){
DMA_RCV_FLAG=0;
SPI_Flash_Write(Font_Buf1,offx+flashaddr,1024); //从0开始写入1024个数据
offx+=1024;
res = fupd_prog(x,y,size,file_size,offx); //进度显示
memset(Font_Buf1,0,RECV_LEN);
}
if(res == 2){
res =0 ;
break;
}
}
return res;
}
//更新字体文件,UNIGBK,GBK12,GBK16,GBK24一起更新
//x,y:提示信息的显示地址
//size:字体大小
//提示信息字体大小
//返回值:0,更新成功;
// 其他,错误代码.
u8 update_font(u16 x,u16 y,u8 size)
{
u8 res = 1;
res=0XFF;
ftinfo.fontok=0XFF;
SPI_Flash_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo)); //清除之前字库成功的标志.防止更新到一半重启,导致的字库部分数据丢失.
SPI_Flash_Read((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo)); //重新读出ftinfo结构体数据
LCD_ShowString(x,y,size,"Updating GBK12.BIN",0);
res=updata_fontx(x+20*size/2,y,size,1); //更新UNIGBK.BIN
if(res)return 2;
LCD_ShowString(x,y,size,"Updating GBK16.BIN ",0);
res=updata_fontx(x+20*size/2,y,size,2); //更新GBK16.FON
if(res)return 3;
LCD_ShowString(x,y,size,"Updating GBK24.BIN ",0);
res=updata_fontx(x+20*size/2,y,size,3); //更新GBK24.FON
if(res)return 4;
//全部更新好了
ftinfo.fontok=0XAA;
SPI_Flash_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo)); //保存字库信息
return 0;//无错误.
}
串口软件用的是XCOM2.2
注意设置发件发送间隔时间,然后波特率尽量不要太大,速度过快会冲掉buf导致数据丢失。
上面代码是基于正点原子mini板 汉字显示实验上修改的。其主要思想是利用串口空闲中断+DMA接收字库文件,串口助手每传输一帧数据后,会有短暂延时,这时会触发串口空闲中断,在串口中断函数里面切换DMA接收的缓冲区,把接收的数据写入FLASH中,写入完成之后,清空缓存,在写入FLASH期间,DMA用另一个缓冲正在接收下一帧的数据,接收完成后触发串口中断,重复上述操作,直到文件传输完成。如果波特率过高,会导致之前的数据还没有完全写入,新的数据会覆盖原有数据。还有就是不用DMA传输完成中断的原因是在传输文件最后一帧是一个不定长的数据(不一定刚好是1024字节),用空闲中断正好来接收不定长数据,还有就是DMA缓冲区的长度要略大于1024,防止触发DMA中断(或者禁用DMA中断).
最后 ----这篇博文 写得有点乱,有问题下方留言就是了。。。