串口DMA更新字库STM32F429

2019-07-20 02:30发布

本帖最后由 潜龙探渊 于 2016-11-3 16:45 编辑

最近工程上用到了LCD显示及W25Q256存储字库,硬件上就是没有SD卡,然后这就很困扰我怎么才能把字库放到外部FLASH里?
想来想去唯一的办法就是通过串口接收数据再把它烧到W25q256里面,然后我就先上论坛搜索有没有相关资料,然后搜了好久才找到一个“采用串口DMA双缓冲方法,快速更新外部FLASH中文字库。更新原子哥例程的4个字库仅需一分钟左右!”这个帖子!(在这我要小小吐槽一下我们论坛的关键字搜索,感觉没有以前隔壁论坛的好用。需要你搜索的关键字要准确不支持模糊搜索,这就是我的个人使用感受不知道各位有没有同感的希望原子看到不要怪我八婆
     找到资料后当然就是开始吸收咯~这位群主的硬件是STM32F407的开发板而我手上又没有这个开发板,我现在手上就一个STM32F429的阿波罗开发板,我本来想仿照那位群主写的程序用HAL库仿写的,但是最后发现HAL跟407的标准库差太多了有些设置根本找不到,最后实在不行就改成用寄存器来仿写,反正寄存器操作无非就是多看芯片寄存器手册就可以了。
     仿写了二天终于写完了,但是调试还是遇到了各种问题,然后我就慢慢看他的程序思路然后再继续修改BUG,后面我就成功更新了一次!
成功了一次就继续测试发现,这个程序的下载成功率好像不高!!!又是用了一天的时间继续调程序BUG,后面终于把程序下载成功率提到了90%以上!
    然后我又把本来群主没有添加的32*32字体也给添加进去了,后面就只要把dma.c和dma.h、TXT文件烤到你工程目录下你也可以直接使用串口来更新字库了,可以造福没有SD卡的朋友们
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u8 chx,u32 par,u32 mar0,u32 mar1,u16 ndtr)
{
        DMA_TypeDef *DMAx;
        u8 streamx;
        
        USART1->CR3 |= 1<<6;                        //使能串口1的DMA接收
        if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
        {
                DMAx=DMA2;
                RCC->AHB1ENR|=RCC_AHB1ENR_DMA2EN;//DMA2时钟使能
        }else
        {
                DMAx=DMA1;
                 RCC->AHB1ENR|=RCC_AHB1ENR_DMA1EN;//DMA1时钟使能
        }
        while(DMA_Streamx->CR&0X01);        //等待DMA可配置
        
        streamx=(((u32)DMA_Streamx-(u32)DMAx)-0X10)/0X18;                 //得到stream通道号
         if(streamx>=6)DMAx->HIFCR|=0X3D<<(6*(streamx-6)+16);         //清空之前该stream上的所有中断标志
        else if(streamx>=4)DMAx->HIFCR|=0X3D<<6*(streamx-4);     //清空之前该stream上的所有中断标志
        else if(streamx>=2)DMAx->LIFCR|=0X3D<<(6*(streamx-2)+16);//清空之前该stream上的所有中断标志
        else DMAx->LIFCR|=0X3D<<6*streamx;                                                 //清空之前该stream上的所有中断标志

        DMA_Streamx->CR=0;                            //先全部复位CR寄存器值
        DMA_Streamx->AR=par;                         //DMA外设地址
        DMA_Streamx->M0AR=mar0;                 //DMA 存储器0地址
        DMA_Streamx->NDTR=ndtr;                         //数据传输量
        
        DMA_Streamx->CR|=0<<6;                        //外设到存储器模式
        DMA_Streamx->CR|=1<<8;                        //注意:这里设置为循环模式,不然不能启动第二次传输
        DMA_Streamx->CR|=0<<9;                        //外设非增量模式
        DMA_Streamx->CR|=1<<10;                        //存储器增量模式
        DMA_Streamx->CR|=0<<11;                        //外设数据长度:8位
        DMA_Streamx->CR|=0<<13;                        //存储器数据长度:8位
        DMA_Streamx->CR|=1<<16;                        //中等优先级
        DMA_Streamx->CR|=0<<21;                        //外设突发单次传输
        DMA_Streamx->CR|=0<<23;                        //存储器突发单次传输
        DMA_Streamx->CR|=(u32)chx<<25;  //通道选择
        DMA_Streamx->FCR=0<<2;                        //FIFO 模式禁止
        DMA_Streamx->FCR=0X03;                        //FIFO 阈值
        //使能DMA双缓冲模式
        DMA_Streamx->CR|=0<<19;                 //CT=0当前目标存储器为存储器0
        DMA_Streamx->M1AR=mar1;                        //DMA 存储器1地址
        DMA_Streamx->CR|=1<<18;                    //DBM=1设置双缓存模式
        
        //DMA_Streamx->CR|=1<<0;                //EN=1开启DMA传输
        DMA_Streamx->CR|=1<<4;                //使能DMA传输完成中断TCIE使能
        DMAx->HIFCR|=1<<11;                 //清除TCIF5中断标志
        //DMA NVIC 配置
        MY_NVIC_Init(3,2,DMA2_Stream5_IRQn,2);//组2,抢占2,优先级2 DMA2_Stream5_IRQn中断通道
        DMA_Streamx->CR|=1<<0;                //EN=1开启DMA传输
}
//重新开启一次DMA传输
//DMA_StreamxMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
//ndtr:数据传输量  
void MYDMA_REST_Enable(DMA_Stream_TypeDef *DMA_Streamx,u32 par,u32 mar,u16 ndtr)
{
        DMA_Streamx->CR&=~(1<<0);         //关闭DMA传输
        while(DMA_Streamx->CR&0X1);        //确保DMA可以被设置  
        DMA_Streamx->AR=par;                //DMA外设地址
        DMA_Streamx->M0AR=mar;                 //DMA 存储器0地址
        DMA_Streamx->NDTR=ndtr;                //数据传输量
        DMA_Streamx->CR|=1<<0;                //开启DMA传输
}         

/********************************************************************
*函数名称:void DMA2_Stream5_IRQHandler(void)
*功    能MA2_Stream5_IRQHandler中断服务程序
*说    明MA2数据流5中断服务函数
*输入参数:无
*输出参数:无
*********************************************************************/  
void DMA2_Stream5_IRQHandler(void)                        
{
        if(DMA2->HISR&(1<<11))//判断TCIF5传输完成
        {
           DMA2->HIFCR|=1<<11; //清除TCIF5中断标志
           LED2=~LED2;
           //***************数据接收完处理******************//
           if(DMA2_Stream5->CR&(1<<19)) //得到CT当前目标存储器
           {
                        GBK_BUF_Flag=1; //存储器1
           }
           else
           {
                       GBK_BUF_Flag=0; //存储器0
           }
           //**************************************************//
        }
}
u8 updata_fontx(u16 x,u16 y,u8 size,u8 fx)
{
        u32 flashaddr=0;                                                                    
        u32 offx=0;      //接收到的文件大小
        u32 fsize=0;         //总文件大小
        u16 over_len =0; //最后数据长度
        
        switch(fx)
        {
                case 0:                                                                                                //更新UNIGBK.BIN
                        ftinfo.ugbkaddr=FONTINFOADDR+sizeof(ftinfo);        //信息头之后,紧跟UNIGBK转换码表
                        fsize=ftinfo.ugbksize=UNIGBK;                                        //UNIGBK大小
                        flashaddr=ftinfo.ugbkaddr;                                                //UNIGBK的起始地址
                        printf("lease send UNIGBK.bin ");
                        break;
                case 1:
                        ftinfo.f12addr=ftinfo.ugbkaddr+ftinfo.ugbksize;        //UNIGBK之后,紧跟GBK12字库
                        fsize=ftinfo.gbk12size=GBK12_FONSIZE;                        //GBK12字库大小
                        flashaddr=ftinfo.f12addr;                                                //GBK12的起始地址
                        printf("lease send GBK12.FON ");
                        break;
                case 2:
                        ftinfo.f16addr=ftinfo.f12addr+ftinfo.gbk12size;        //GBK12之后,紧跟GBK16字库
                        fsize=ftinfo.gbk16size=GBK16_FONSIZE;                        //GBK16字库大小
                        flashaddr=ftinfo.f16addr;                                                //GBK16的起始地址
                        printf("lease send GBK16.FON ");
                        break;
                case 3:
                        ftinfo.f24addr=ftinfo.f16addr+ftinfo.gbk16size;        //GBK16之后,紧跟GBK24字库
                        fsize=ftinfo.gbk24size=GBK24_FONSIZE;                        //GBK24字库大小
                        flashaddr=ftinfo.f24addr;                                                //GBK24的起始地址
                        printf("lease send GBK24.FON ");
                        break;
                case 4:
                        ftinfo.f32addr=ftinfo.f24addr+ftinfo.gbk24size;        //GBK24之后,紧跟GBK32字库
                        fsize=ftinfo.gbk32size=GBK32_FONSIZE;                        //GBK32字库大小
                        flashaddr=ftinfo.f32addr;                                                //GBK32的起始地址
                        printf("lease send GBK32.FON ");
                        break;
        }
        fupd_prog(x,y,size,fsize,offx);                                 //进度显示
        GBK_BUF_Flag        = 2; //清除标志等待下一次DMA中断
        GBK_OVER_Flag        = 0; //停止计时
        printf("请发送文件... ");
        do
        {
                if(GBK_OVER_Flag)GBK_OVER_Flag++;        //不为0开始计时
                if(GBK_BUF_Flag!=2)//判断是否进入DMA中断
                {               
                        LED0 = ~LED0;         //指示灯
                        GBK_OVER_Flag=1; //开始计时
                        if(GBK_BUF_Flag==0) //当前目标在存储器0
                        {
                                W25QXX_Write(Usart1_Rece_Buf1,offx+flashaddr,Usart1_DMA_Len);                //开始写入Usart6_DMA_Len个数据  
                        }
                        else
                        if(GBK_BUF_Flag==1) //当前目标在存储器0
                        {
                                W25QXX_Write(Usart1_Rece_Buf0,offx+flashaddr,Usart1_DMA_Len);                //开始写入Usart6_DMA_Len个数据  
                        }
                        offx+=Usart1_DMA_Len;         //加上已写完的数据量               
                        fupd_prog(x,y,size,fsize,offx);                                 //进度显示
                        GBK_BUF_Flag=2;                 //清除标志等待下一次DMA中断
                }               
                delay_us(100); //延时100us        
        }
        while(GBK_OVER_Flag<=WATE_TIME); //判断超时?
               
        over_len = Usart1_DMA_Len-DMA2_Stream5->NDTR; //最后数据长度        
        if(DMA2_Stream5->CR&(1<<19)) //得到CT当前目标存储器
        {
           W25QXX_Write(Usart1_Rece_Buf1,offx+flashaddr,over_len);//将DMA最后的一帧数据写入FLASH
        }
        else
        {
           W25QXX_Write(Usart1_Rece_Buf0,offx+flashaddr,over_len);//将DMA最后的一帧数据写入FLASH
        }
        offx+=over_len; //加上已写完的数据量
        fupd_prog(x,y,size,fsize,offx);                                 //进度显示
        //重新开启一次DMA传输
        MYDMA_REST_Enable(DMA2_Stream5,(u32)&USART1->DR,(u32)Usart1_Rece_Buf0,Usart1_DMA_Len);  //重置DMA
        printf("已写完数据,本次接收到%d字节!!! ",offx);
        printf(" ");
        delay_ms(100); //延时100ms
        if((offx > (fsize-1024))&(offx < fsize)) //误差在正负1K之内都认为是更新成功
                return 0; //更新成功
        else
                printf("更新失败,重新开始! ");
                return 1; //更新失败
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------还有我本想把最新的F429的芯片手册上传上去的,可是文件太大上传不了就算了~~~
还有各位如果你觉得我分享的程序有用,就来给我盖盖楼吧!让这个论坛活跃起来~~~~~~~~~~


友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
9条回答
taizonglai
1楼-- · 2019-07-20 05:36
我就觉得这个MDK设置的好靓吖。
潜龙探渊
2楼-- · 2019-07-20 05:48
 精彩回答 2  元偷偷看……
战舰水手
3楼-- · 2019-07-20 07:04
  版主请问:DMA_Streamx->CR|=(u32)chx<<25;  //通道选择

这里的通道选择有没有问题,我觉得是DMA_Streamx->CR|=(u32)chx<<27;

在f429中文手册里usart1的接收dma   stream2和5都是在通道4上的,为何选择通道1???、
潜龙探渊
4楼-- · 2019-07-20 09:24
战舰水手 发表于 2016-11-22 15:35
版主请问:DMA_Streamx->CR|=(u32)chx

其实这两种都可以正常使用啦~其实这两个就是二选一,你想用数据流2通道4也能正常使用,用数据流5通道4也行,反正只要是USART1_RX都行,看你怎么选择咯?
战舰水手
5楼-- · 2019-07-20 14:06
 精彩回答 2  元偷偷看……
58lu
6楼-- · 2019-07-20 19:33
不错不错,谢谢楼主

一周热门 更多>