历史上的今天
返回首页

历史上的今天

今天是:2024年09月17日(星期二)

2018年09月17日 | STM32串口双缓冲乒乓数据传输方式

2018-09-17 来源:eefocus

对于没玩过DMA 的朋友,这里简单说一下DMA,用自己的语言说吧,那就是,从某个位置
传输数据到某个位置,如果不用DMA,那要CPU参与操作,一个字节一个字节地搬,效率高
点的,就一个字一个字地搬.但当你用了DMA 后,那就是只需要设置:A.从哪里开始搬; B,
搬到哪里去;C以字节方式搬还是半字还是字;D:一共搬多少个.之后,启动DMA.CPU内部
就会开始搬数据了,整个搬数据的过程都不需要指令的参与,唯一要做的,就是检测什么时
候搬完.你可以扫描寄存器,也可以用中断.这里,我使用了中断.
具体设置功能看注释就可以明白了.注意一点就是,有一个设置:
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
这个是外设的地址不递增.也就是说,每次搬动,都是从源头,也就是USART1的DR寄存器
搬,但内存地址却是递增的:
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
这个历程实现了 接受 串口的数据 写到FLASH 之中工作,而DMA的作用在于将 串口收寄存器 USART1->DR 的 数据写到内存之中 比如某个数组之中 u8 USART1_DMA_Buf1[512]; 写满512个字节之后将进入DMA中断(通道5)在这里修改DMA 的内存写入入口
u8 USART1_DMA_Buf2[512]; ,同时标记 下次的入口Free_Buf_No=BUF_NO1; 与 Buf_Ok=TRUE; 证明已有数据准备完成。这时CUP将USART1_DMA_Buf1中的数据写入FLASH .
又抄了一点
这次使用的是双缓冲,也有人
叫乒乓缓冲.因为一般情况下,串口的数据DMA 传输进BUF1 的过程中,是不建议对
BUF1 进行操作的.但由于串口数据是不会等待的直传,所以你总不能等BUF1 满了,
才往FLASH 上写,因为这时候串口数据依旧是源源不断.于是,使用双缓冲就变的理
所当然了.当BUF1 满了的时候,就马上设置DMA的目标为BUF2,并且BUF1的数据
往25F080上灌.当串口DMA写满了BUF2的时候,再设置DMA的目标为BUF1,此时
再操作BUF2写进25F080.如此一直循环,就好像打乒乓球那样吧,所以就叫乒乓缓冲.
用这个方法的速度极限就是,你必须确保两点a.DMA 灌满了BUF1 的时候,会发生中
断,此时切换DMA 的目标缓冲为BUF2,而且切换的过程必须在新的串口数据溢出之
前完成.b.在DMA的BUF1满之前,另外一个有数据的BUF2必须能全部写进25F080,
其中包括了遇到新的扇区边界而要刷除扇区的操作时间!!
可以看出,BUF的增大,并不能够很大程度的提升速度极限.
STM32中USART的DMA 实现 - java - stm32学习日志
假设 USART 与 FLASH 的 底层驱动已经写好了。   点击查看。
/************DMA方式传输***************************/
#define SRC_USART1_DR    (&(USART1->DR))        //串口接收寄存器作为源头
//DMA目标缓冲,这里使用双缓冲
u8 USART1_DMA_Buf1[512];
u8 USART1_DMA_Buf2[512];
bool Buf_Ok;    //BUF是否已经可用
BUF_NO Free_Buf_No;        //空闲的BUF号 typedef enum {BUF_NO1=0,BUF_NO2=1}BUF_NO;
DMA_InitTypeDef DMA_InitStructure;
void USART_DMAToBuf1(void)
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                             //开DMA时钟
    DMA_DeInit(DMA1_Channel5);                                                                               //将DMA的通道1寄存器重设为缺省值
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)SRC_USART1_DR;                //源头BUF 既是 (&(USART1->DR)) 
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1;                //目标BUF 既是要写在哪个个数组之中
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                      //外设作源头//外设是作为数据传输的目的地还是来源
    DMA_InitStructure.DMA_BufferSize = 512;                                        //DMA缓存的大小 单位在下边设定
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;               //外设地址寄存器不递增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                        //内存地址递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;        //外设字节为单位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;            //内存字节为单位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                               //工作在循环缓存模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;                            //4优先级之一的(高优先) VeryHigh/High/Medium/Low
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                //非内存到内存
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);                                    //根据DMA_InitStruct中指定的参数初始化DMA的通道1寄存器
    
    DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);                                //DMA5传输完成中断
STM32中USART的DMA 实现 - java - stm32学习日志
     USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);                               //使能USART1的接收DMA请求
    /*****************************************************************************************************************************************************/
    //初始化BUF标志
    Free_Buf_No=BUF_NO2; //因为 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1; 
    Buf_Ok=FALSE; //此时没有数据准备完成 当然FALSE
    DMA_Cmd(DMA1_Channel5, ENABLE);                                                //正式允许DMA
    
}
再来看看DMA中断:
//u16 DataCounter;
extern DMA_InitTypeDef DMA_InitStructure;
void DMA1_Channel5_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_IT_TC5)) //通道5传输完成中断TC 还有传输 过半中断HT 错误中断TE 全局中断GL
     {
       //DataCounter = DMA_GetCurrDataCounter(DMA1_Channel5);//获取剩余长度,一般都为0,调试用
        DMA_ClearITPendingBit(DMA1_IT_GL5);    //清除全部中断标志
        
       //转换可操作BUF
        if(Free_Buf_No==BUF_NO1)
        {    
            DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1;
            DMA_Init(DMA1_Channel5, &DMA_InitStructure);
            Free_Buf_No=BUF_NO2;
        }
        else
        {
            DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf2;
            DMA_Init(DMA1_Channel5, &DMA_InitStructure);
            Free_Buf_No=BUF_NO1;
        }
        Buf_Ok=TRUE; //有准备好的数据了
    
     }
}
写FLASH的操作
while(1)
    {
        if(Buf_Ok==TRUE)
        {
            LED1_ON;   //一个标记
            Buf_Ok=FALSE; //操作了准备好的数据
            if((addr%4096)==0)        //跨越一个扇区,则需要先刷除
            {
                SST25SectorErase(addr);    
                sector_count++;
            }
            if(Free_Buf_No==BUF_NO1)
                SST25Write(addr,USART1_DMA_Buf1,512);
            else
                SST25Write(addr,USART1_DMA_Buf2,512);
            addr+=512;
            Timer1=5000; //时间重置
            LED1_OFF;
        }
        
       //检测超时 开了 定时器
        if(Timer1==0) //五秒内没准备好的数据
        {
           //获取长度
            len=512-DMA_GetCurrDataCounter(DMA1_Channel5);
           //写入最后数据
            if(Free_Buf_No==BUF_NO1)
                SST25Write(addr,USART1_DMA_Buf2,len);
            else
                SST25Write(addr,USART1_DMA_Buf1,len);
            addr+=len;
            
            break;
        }
    }
STM32中USART的DMA 实现 - java - stm32学习日志
还是很简单的。
有一点比较困扰 就是 FlagStatus标志位 与 ITStatus中断标志位 的区别。 其实就 DMA 来说 DMA_IT值 与 DMA_FLAG值

 是一样的
甚至2者值的获取 都是读 DMA ISR register 的值 清除也是设置 DMA_IFCR 寄存器来清除的所以貌似没有区别.........STM32中USART的DMA 实现 - java - stm32学习日志同理这

个问题在别的中断也存在但我还不可保证 IT 与FLAG 的值总是相同的这个存在也许是为了兼容但一定有其意义务必不可混用即

使有时用错也正确.....


推荐阅读

史海拾趣

世纪金光(CENGOL)公司的发展小趣事

在碳化硅材料技术取得突破后,世纪金光迅速将这一技术应用于功率器件的研发与生产。公司成功开发出额定电压650-1700V、额定电流5-100A的碳化硅肖特基二极管(SBD)以及额定电压650-1200V、额定电流20-100A的金属-氧化物半导体场效应晶体管(MOSFET)。这些高性能的功率器件在电源PFC、充电桩充电模组、光伏逆变器、特种电源等领域得到了广泛应用,为客户提供了高效、可靠的解决方案。

长运通(CYT)公司的发展小趣事

深圳市长运通光电技术有限公司(简称长运通或CYT)成立于2003年11月,总部设在深圳这座充满创新活力的城市。公司从最初只有几名员工的初创团队,逐步发展成为在电源管理IC和LED光源领域具有影响力的企业。长运通在初创阶段就明确了自身的市场定位,致力于为客户提供高品质的电源解决方案和LED照明产品。通过不断努力,公司逐渐在市场上建立了良好的口碑。

Amkor公司的发展小趣事

深圳市长运通光电技术有限公司(简称长运通或CYT)成立于2003年11月,总部设在深圳这座充满创新活力的城市。公司从最初只有几名员工的初创团队,逐步发展成为在电源管理IC和LED光源领域具有影响力的企业。长运通在初创阶段就明确了自身的市场定位,致力于为客户提供高品质的电源解决方案和LED照明产品。通过不断努力,公司逐渐在市场上建立了良好的口碑。

Electro-Mech Components Inc公司的发展小趣事

自1976年起,EMC在蒂华纳不仅为自身运营制造工厂,还开始为其他美国公司,如GM/Hughes Aircraft和Daneher Joslyn Sunbank Electronics提供制造服务。这种深度合作模式不仅增强了EMC的生产能力,也使其能够接触到更多的先进技术和管理经验。通过与这些美国公司的合作,EMC的产品质量和生产效率都得到了显著提升,同时也为公司的全球化战略打下了坚实基础。

EICHHOFF公司的发展小趣事

在快速发展的同时,EICHHOFF公司也积极关注环保和可持续发展问题。公司认识到作为电子行业的一员,必须承担起保护环境、节约资源的责任。为此,EICHHOFF公司采取了一系列措施来降低生产过程中的能耗和排放。例如:引入节能设备和技术、优化生产流程、推广绿色包装等。

此外,EICHHOFF公司还积极参与环保公益活动和社会责任项目,为社会做出积极贡献。这些努力不仅提升了公司的社会形象,也为公司的可持续发展奠定了基础。

Gauthier Connectique公司的发展小趣事
长期使用或不当操作可能导致元器件损坏。解决方法是检查并更换损坏的元器件。

问答坊 | AI 解惑

新手:可控硅SG25AA60门极电压电流问题

可控硅门极电压电流问题:我用的是SG25AA60型号的的可控硅(25A/600V),但PDF资料很简单,自己也是第一次接触可控硅,所以在这问一下, 1.可控硅的门极电压电流一般有什么要求啊? 2.门极触发电压要多大的值才能触发? 3.门极能接电压的正负范 ...…

查看全部问答>

变频空调的PFC驱动模块指的是什么?这里的PFC是什么意思?

变频空调的PFC驱动模块指的是什么?这里的PFC是什么意思?…

查看全部问答>

谁有三星2440的BSP安装包和全套文档?

谁有三星2440的BSP CSP安装包和全套文档? 可以给我一份吗? 想了解一下,开阔一下知识. 我的邮箱:huabinsir@126.com. 谢谢.…

查看全部问答>

请hzdysymbol和yashi进来,送分!

上一个帖子分数不够,没办法给全,请二位进来留个言,好加分。 谢谢!…

查看全部问答>

并口的信号线和3个寄存器24位是一一对应的吗

并口的信号线25根,和数据寄存器8位,控制寄存器8位,状态寄存器8位,是不是一一对应呢 还有EPP模式中,控制寄存器某一位设置0的时候,信号线上是不是低电平,设置1的时候,信号线上是不是高电平呢。…

查看全部问答>

在NDIS层实现数据转发的问题

想在NDIS层做一个数据过滤,并通过对目的地址的修改发往本地主机的另一个进程。 实际上是做了一个NAT转换。 这样做有可行性吗? 谢谢~~ …

查看全部问答>

WinCE中如何向image中添加文件夹

我Wince下一应用程序设为开机自动运行,运行时要读取一文件夹下的图片。问题是:如何向image中添加此文件夹,与系统捆绑在一起,开机后即在Windows目录下,以便读取。 期待高人指点!!! …

查看全部问答>

聊聊全国电设战况

本帖最后由 paulhyde 于 2014-9-15 08:56 编辑 全国电子设计大赛越来越近了.......有经验的朋友们可以交流一下接下来的备战情况吗????? 本人感觉到有点迷茫了.........................  …

查看全部问答>

51当中,怎么写自己的头文件

初学51,感觉很多东西都是相同的设定,想把一些相同的写进一个头文件里面,但是不知道怎么写,各位高手指教下!…

查看全部问答>