stm32 usart 单线半双工串口 DMA发送 最后一个字节发不出来问题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/zhanglifu3601881/article/details/100984055

最近遇到一个小问题,感觉很有意思便记下来分享一下 ,顺便也为日后类似的问题提供点思路:

使用stm32串口发送数据 ,串口是单线半双工模式,要求数据发送前串口变成发送模式,发送完后立即变回接收模式,发送操作使用DMA来发送,程序开了串口中断和DMA中断,串口中断主要是用于接收与解析数据,DMA中断是想用于操作串口收发模式切换,发现DMA发送时最后一个数据老是发不出来。

经过分析定位发现是因为DMA将传输完成后,串口其实还没有将所有的问题发送出去(至少还有一个字节没发出去),然后修改操串口收发模式切换时间点将该问题解决,具体为:串口在DMA传输前由接收模式变成发送模式,在DMA传输完成中断中开启串口发送完成中断,在串口发送完成中断中将串口工作模式从发送模式变回接收模式,问题完解决,相关配置代码如下:

串口配置:

void Usart_Init( void )
{
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    GPIO_InitTypeDef  GPIO_InitStructure;

    //GPIO clock enable
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);     // USART1时钟使能

    //USART1_TX
    GPIO_InitStructure.GPIO_Pin = USART1_TX_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	           // 复用推挽输出
    GPIO_Init(USART1_TXRX_PORT, &GPIO_InitStructure);

    //USART1_RX
    GPIO_InitStructure.GPIO_Pin = USART1_RX_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;      // 浮空输入
    GPIO_Init(USART1_TXRX_PORT, &GPIO_InitStructure);


    USART_InitStructure.USART_BaudRate = 115200;               // 串口波特率
    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_HalfDuplexCmd(USART1, ENABLE);                            // 使能变双工
 
    DMA_Usart_Init();

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;         // USART1中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2 ;// 抢占优先级2
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		    // 子优先级1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			      // IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);	                          // 根据指定的参数初始化NVIC寄存器

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);            // 开启串口1接收中断
    USART_Cmd(USART1, ENABLE);                                // 使能串口1
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);            // 开启串口USART1的DMA发送

}

DMA配置:

void DMA_Usart_Init(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //开启DMA1时钟

    DMA_Cmd(DMA1_Channel4, DISABLE);//关闭DMA1的通道4  Usart1_TX对应通道4
    DMA_DeInit(DMA1_Channel4);//恢复缺省值

    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);//设置串口发送数据寄存器
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DMATXBUFF1;//设置发送缓冲区首地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//设置外设位目标,内存缓冲区->外设寄存器
    DMA_InitStructure.DMA_BufferSize = 10;//需要发送的字节数,这里可以设置为0,因为在实际要发送时还会重新设置
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不做增加调整,调整不调整是DMA自动实现的
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存缓冲区地址增加调整
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据宽度8位,1个字节
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//内存数据宽度8位,1个字节
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//单次传输模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_Low;//优先级配置
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//关闭内存到内存的DMA模式
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);//写入配置

    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;//DMA1中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1 ;//抢占优先级2
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//子优先级1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化NVIC寄存器

    DMA_ClearFlag(DMA1_FLAG_GL4);//清除DMA所有标志 GL4:通道4全局标志 TC4:传输完成 HT4:传输过半 TE4:传输错误
    DMA_Cmd(DMA1_Channel4, DISABLE);//关闭DMA1的通道4
    DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);//开启发送DMA通道中断

}

串口中断:

void USART1_IRQHandler(void)
{
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  // 接收数据寄存器非空标志
    {
        if(RxNum1 > (RXBUFFSIZE1 - 1))
        {
            RxNum1 = 0;
        }
        RXBUFF1[RxNum1++] = USART_ReceiveData(USART1);
    }
    else if(USART_GetITStatus(USART1, USART_IT_TC) != RESET)// 发送完成标志
    {
        USART_ITConfig(USART1, USART_IT_TC, DISABLE);
        USART_ClearFlag(USART1,USART_FLAG_TC);
        USART1_RX_EN_TX_DIS();
    }
    else
    {
			
    }

}

DMA中断:

void DMA1_Channel4_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_FLAG_TC4) != RESET)     // DMA1通道4发送完成标志
    {
        Usart1TxBusy = 0;
        DMA_Cmd(DMA1_Channel4, DISABLE);            // 关闭DMA1的通道4
        DMA_ClearFlag(DMA1_FLAG_GL4);               // 清除DMA1的通道4的所有中断标志,因为只有发送完成标志,所以直接全部清除
        USART_ClearFlag(USART1,USART_FLAG_TC);	    // 在关中断前先清中断标志,防止是串口上一个数据发送时的中断标志
        USART_ITConfig(USART1, USART_IT_TC, ENABLE);// DMA1传输完成后使能Usart1发送完成中断
    }
}

猜你喜欢

转载自blog.csdn.net/zhanglifu3601881/article/details/100984055