STM32F103 DMA快速写片外flash 解决没有双缓存的两种方案

本文探讨了两种串口DMA接收中断处理方案。方案一使用DMA_Mode_Normal模式,每次中断后切换接收buffer并重置传输值。方案二采用DMA_Mode_Circular模式,通过DMA_IT_HT中断处理数据,避免了关闭和重置DMA导致的数据丢失问题,适用于高速数据接收。测试表明,方案二在921600波特率下接收大量数据并写入Flash时更稳定。
摘要由CSDN通过智能技术生成

方案一:设置DMA为DMA_Mode_Normal模式,开启DMA DMA_IT_TC接收完成中断,初始化两个接收buffer,接收中断触发以后,关闭DMA接收,切换接收buffe1r到buffer2,重置传输值,开启DMA接收。处理接收buffer1.

/***********************************************
** Function name:
** Descriptions:        串口DMA中断
** input parameters:    无
** output parameters:   无
** Returned value:      无
*************************************************/
void Usart_DMA_IRQHandler(USART_TypeDef* com)
{
    u8 com_id = 0;

    for(u8 i = 0; i < COMn; i++)
    {
        if(EVAL_USART[i] == com)
        {
            com_id = i;
        }
    }

    if(com_id >= COMn)
    {
        return;
    }
    
      if((DMA_GetFlagStatus(EVAL_COM_DMA_IT_TC[com_id]) != RESET)  ||
            (strlen((char *)DownBuf[(DmaUseNum) % 2])))
    {
        /*清除全部中断标志*/
        DMA_ClearFlag(EVAL_COM_DMA_IT_TC[com_id]);
        DmaUseNum = (DmaUseNum + 1) % 2;
        DMA_Cmd(EVAL_COM_DMA_CHANNEL[com_id], DISABLE);
        memset((u8 *)DownBuf[DmaUseNum], 0, DOWNLOAD_SIZE);
        /*设置DMA的传输值*/
        EVAL_COM_DMA_CHANNEL[com_id] -> CNDTR = (DOWNLOAD_SIZE - 32);
        /*设置传输地址*/
        EVAL_COM_DMA_CHANNEL[com_id] -> CMAR  = (u32)DownBuf[DmaUseNum];
        /*打开DMA*/
        DMA_Cmd(EVAL_COM_DMA_CHANNEL[com_id], ENABLE);
        DownloadWriteFlash((char *)DownBuf[(DmaUseNum + 1) % 2], (u32 *)&DownOffset);
        Dmastartdown = 1;
        DownBuf_Time_Update();
    }

}


void usart_dma_config(USART_TypeDef* com, u8 DMAFlag)
{
    u8 com_id = 0;
    /* DMA1通道5配置 */
    DMA_InitTypeDef DMA_InitStructure;
    /*DMA 内存申请和释放*/
    Request_DownBuf(DMAFlag);

    for(u8 i = 0; i < COMn; i++)
    {
        if(EVAL_USART[i] == com)
        {
            com_id = i;
        }
    }

    if(com_id >= COMn)
    {
        return;
    }

    RCC_AHBPeriphClockCmd(COM_DMA_CLK[com_id], ENABLE);
    /* 关DMA通道 */
    DMA_Cmd(EVAL_COM_DMA_CHANNEL[com_id], DISABLE);
    /* 恢复缺省值 */
    DMA_DeInit(EVAL_COM_DMA_CHANNEL[com_id]);
    /* DMA外设地址 */
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DR);
    /* DMA 存储器0地址 */
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DownBuf[DmaUseNum];
    /* 外设到存储器模式 */
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    /* 数据传输量 */
    DMA_InitStructure.DMA_BufferSize = (DOWNLOAD_SIZE - 32);
    /* 外设非增量模式 */
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    /* 存储器增量模式 */
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    /* 外设数据长度:8位 */
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    /* 存储器数据长度:8位 */
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    /* 使用普通模式 */
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    /* 中等优先级 */
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    /* 设置DMA的2个memory中的变量互相访问*/
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    /* 初始化DMA Stream */
    DMA_Init(EVAL_COM_DMA_CHANNEL[com_id], &DMA_InitStructure);
    /* 配置DMA发送完成后产生中断 */
    DMA_ITConfig(EVAL_COM_DMA_CHANNEL[com_id], DMA_IT_TC, ENABLE);
    /* 使能通道 */
    DMA_Cmd(EVAL_COM_DMA_CHANNEL[com_id], ENABLE);
     /*采用DMA方式接收*/
    if(DMAFlag == 1)
    {
        /*开启串口的 DMA 接受功能*/
        USART_DMACmd(com, USART_DMAReq_Rx, ENABLE);
        /*关闭串口接收中断*/
        USART_ITConfig(com, USART_IT_RXNE, DISABLE);
        /*关闭串口发送中断*/
        //USART_ITConfig(WIFI_USARTx, USART_IT_TC, DISABLE);
        /*使能串口空闲中断*/
        //USART_ITConfig(WIFI_USARTx, USART_IT_IDLE, ENABLE);
        memory_down_mode = 1;
    }
    else
    {
        USART_DMACmd(com, USART_DMAReq_Rx, DISABLE);
        USART_ITConfig(com, USART_IT_RXNE, ENABLE);
        /*开启串口发送中断*/
        //USART_ITConfig(WIFI_USARTx, USART_IT_TC, ENABLE);
        //USART_ITConfig(WIFI_USARTx, USART_IT_IDLE, DISABLE);
        memory_down_mode = 0;
    }


    Dmastartdown = 0;
}

方案二:设置DMA为DMA_Mode_Circular模式,开启DMA DMA_IT_TC接收完成中断和DMA_IT_HT接收过半中断,初始化1个接收buffer,接收到过半中断后处理上一半接收buffer,接收到接收完成中断后处理下一半接收buffer。

/***********************************************
** Function name:
** Descriptions:        串口DMA中断
** input parameters:    无
** output parameters:   无
** Returned value:      无
*************************************************/
void Usart_DMA_IRQHandler(USART_TypeDef* com)
{
    u8 com_id = 0;
    u32  test_ms;
    u32 len;
    u32 addr;
    for(u8 i = 0; i < COMn; i++)
    {
        if(EVAL_USART[i] == com)
        {
            com_id = i;
        }
    }

    if(com_id >= COMn)
    {
        return;
    }
    
      if((DMA_GetFlagStatus(EVAL_COM_DMA_IT_TC[com_id]) != RESET)  ||
            (strlen((char *)DownBuf[(DmaUseNum) % 2])))
    {
        /*清除全部中断标志*/
        DMA_ClearFlag(EVAL_COM_DMA_IT_TC[com_id]);
        DmaUseNum = (DmaUseNum + 1) % 2;
        DMA_Cmd(EVAL_COM_DMA_CHANNEL[com_id], DISABLE);
//        memset((u8 *)DownBuf[DmaUseNum], 0, DOWNLOAD_SIZE);
        /*设置DMA的传输值*/
        EVAL_COM_DMA_CHANNEL[com_id] -> CNDTR = (DOWNLOAD_SIZE - 32);
        /*设置传输地址*/
        EVAL_COM_DMA_CHANNEL[com_id] -> CMAR  = (u32)DownBuf[DmaUseNum];
        /*打开DMA*/
        DMA_Cmd(EVAL_COM_DMA_CHANNEL[com_id], ENABLE);
        dma_full = 1;
        down_buff_nub = (DmaUseNum + 1) % 2;
//        printf("%s", (char *)DownBuf[(DmaUseNum + 1) % 2]);
//        test_ms = (RTC_GetCounter()%1000) * 1000 + ((32767 - RTC_GetDivider()) * 1000 / 32767);
//        printf("d_s:%d\r\n", test_ms);
//        DownloadWriteFlash((char *)DownBuf[(DmaUseNum + 1) % 2], (u32 *)&DownOffset);
//        test_ms = (RTC_GetCounter()%1000) * 1000 + ((32767 - RTC_GetDivider()) * 1000 / 32767);
//        len = strlen((char *)DownBuf[(DmaUseNum + 1) % 2]);
//        addr =  HEX_START_ADDR +  DownOffset;
//        SPI_FLASH_BufferWrite(0,(u8*)DownBuf[(DmaUseNum + 1) % 2], addr, len);
//        DownOffset = DownOffset + len;
//        printf("dma:%d\r\n",down_buff_nub);
    }

}


void usart_dma_config(USART_TypeDef* com, u8 DMAFlag)
{
    u8 com_id = 0;
    /* DMA1通道5配置 */
    DMA_InitTypeDef DMA_InitStructure;
    /*DMA 内存申请和释放*/
    Request_DownBuf(DMAFlag);

    for(u8 i = 0; i < COMn; i++)
    {
        if(EVAL_USART[i] == com)
        {
            com_id = i;
        }
    }

    if(com_id >= COMn)
    {
        return;
    }

    RCC_AHBPeriphClockCmd(COM_DMA_CLK[com_id], ENABLE);
    /* 关DMA通道 */
    DMA_Cmd(EVAL_COM_DMA_CHANNEL[com_id], DISABLE);
    /* 恢复缺省值 */
    DMA_DeInit(EVAL_COM_DMA_CHANNEL[com_id]);
    /* DMA外设地址 */
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DR);
    /* DMA 存储器0地址 */
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DownBuf[DmaUseNum];
    /* 外设到存储器模式 */
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    /* 数据传输量 */
    DMA_InitStructure.DMA_BufferSize = (DOWNLOAD_SIZE - 32);
    /* 外设非增量模式 */
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    /* 存储器增量模式 */
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    /* 外设数据长度:8位 */
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    /* 存储器数据长度:8位 */
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    /* 使用普通模式 */
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    /* 中等优先级 */
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    /* 设置DMA的2个memory中的变量互相访问*/
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    /* 初始化DMA Stream */
    DMA_Init(EVAL_COM_DMA_CHANNEL[com_id], &DMA_InitStructure);
    /* 配置DMA发送完成后产生中断 */
    DMA_ITConfig(EVAL_COM_DMA_CHANNEL[com_id], DMA_IT_TC, ENABLE);
    /* 使能通道 */
    DMA_Cmd(EVAL_COM_DMA_CHANNEL[com_id], ENABLE);
     /*采用DMA方式接收*/
    if(DMAFlag == 1)
    {
        /*开启串口的 DMA 接受功能*/
        USART_DMACmd(com, USART_DMAReq_Rx, ENABLE);
        /*关闭串口接收中断*/
        USART_ITConfig(com, USART_IT_RXNE, DISABLE);
        /*关闭串口发送中断*/
        //USART_ITConfig(WIFI_USARTx, USART_IT_TC, DISABLE);
        /*使能串口空闲中断*/
        //USART_ITConfig(WIFI_USARTx, USART_IT_IDLE, ENABLE);
        memory_down_mode = 1;
    }
    else
    {
        USART_DMACmd(com, USART_DMAReq_Rx, DISABLE);
        USART_ITConfig(com, USART_IT_RXNE, ENABLE);
        /*开启串口发送中断*/
        //USART_ITConfig(WIFI_USARTx, USART_IT_TC, ENABLE);
        //USART_ITConfig(WIFI_USARTx, USART_IT_IDLE, DISABLE);
        memory_down_mode = 0;
    }


    Dmastartdown = 0;
}

说明:

方案二的处理方式比方案一效果更好,特别是当接收的数据速度比较快的时候,方案一关闭再重置DMA的过程有可能导致数据丢失。方案二是循环接收,没有关闭DMA就不存在这个问题,但是两种方案都要注意尽量不在接收中断中处理接收数据。经过测试方案二通过串口接收大量数据的波特率最大调整到921600 然后通过spi写片外flash都不会丢失数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值