STM32通过串口更新中文字库到外置的SPI FLASH中

用正点原子的mini板子做EMWIN显示汉字需要把字库放在外置的FLASH中,给的例程是先将做好的字库放到SD卡中,然后使用FATFS把SD卡的字库拷贝到FLASH中,手上没有SD卡,考虑用串口+DMA传输将字库直接拷到FLASH中。

字库的制作

STM32通过串口更新中文字库到外置的SPI FLASH中_第1张图片
主要注意取模方式的选择,EMWIN取模方式跟正点原子基础例程汉字汉字显示的取模不同,根据需要自行选择。
将做好的字库放在一个文件夹中,共三种大小的字体。
STM32通过串口更新中文字库到外置的SPI FLASH中_第2张图片
同时右键->属性 可查看字库的大小,这个大小在程序里面会用上。
STM32通过串口更新中文字库到外置的SPI FLASH中_第3张图片
注意是大小,不是占用空间

代码

//串口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
STM32通过串口更新中文字库到外置的SPI FLASH中_第4张图片
注意设置发件发送间隔时间,然后波特率尽量不要太大,速度过快会冲掉buf导致数据丢失。

上面代码是基于正点原子mini板 汉字显示实验上修改的。其主要思想是利用串口空闲中断+DMA接收字库文件,串口助手每传输一帧数据后,会有短暂延时,这时会触发串口空闲中断,在串口中断函数里面切换DMA接收的缓冲区,把接收的数据写入FLASH中,写入完成之后,清空缓存,在写入FLASH期间,DMA用另一个缓冲正在接收下一帧的数据,接收完成后触发串口中断,重复上述操作,直到文件传输完成。如果波特率过高,会导致之前的数据还没有完全写入,新的数据会覆盖原有数据。还有就是不用DMA传输完成中断的原因是在传输文件最后一帧是一个不定长的数据(不一定刚好是1024字节),用空闲中断正好来接收不定长数据,还有就是DMA缓冲区的长度要略大于1024,防止触发DMA中断(或者禁用DMA中断).

最后 ----这篇博文 写得有点乱,有问题下方留言就是了。。。

你可能感兴趣的:(单片机)