STM32F1X RS485使用DMA发送丢失数据的处理方法。

串口通过DMA发送一帧数据时总是缺少2个字节,且最后一个字节数据为0xff的原因及解决方法

本次记录为采用485串口发送数据,发送模式是循环检测串口数据寄存器为空(TXE)和发送完成标志位(TC)。DMA发送串口方式

按照以上程序编译运行时,发现装置每次发送一帧报文都不完整,总是缺少2个字节且最后一个字节为0xff。
 根据本人的猜想,应该是数据还没来及发送出去就使能了485接收,导致数据发送不完整,随后又上网查看了类型问题,终于查到说进入DMA发送完成中断以后,还要延时2-4ms等待串口数据完全发送出去才使能485接收。按照以上的结论修改了DMA发送完成中断函数,如下

                        
原文链接:https://blog.csdn.net/more_thinking/article/details/108575952

DMA发送完成中断处理  

在其中加入延时函数

/********************************************************************
* name         : DMA1_Channel7_IRQHandler
* description  : 
* Input        : 
* Output       : 无
* Return       : 
********************************************************************/
void DMA1_Channel7_IRQHandler(void)
{
    if(DMA_GetFlagStatus(DMA1_FLAG_TC7))
    {
        delaydma1_flag_tc7_ms(2);
        DMA_ClearFlag(DMA1_FLAG_TC7); //清除全部中断标志
        DMA_ClearFlag(DMA1_FLAG_GL7);// 清除DMA1的通道4的所有中断标志,因为只有发送完成标志,所以直接全部清除
        USART_ClearFlag(USART1,USART_FLAG_TC);// 在关中断前先清中断标志,防止是串口上一个数据发送时的中断标志
        USART_ITConfig(USART1, USART_IT_TC, ENABLE);// DMA1传输完成后使能Usart1发送完成中断
        D_RS485DE_RE = DISABLE;//重新使能串口接收
    }
}

 DMA发送部分程序 

打开485发送使能

D_RS485DE_RE = ENABLE;    

void Write_Lcd_Mem(uint16_t addr, uint16_t *pBuffer, uint8_t len)
{
#ifdef DMA_SEND
    uint8_t i;
    rt_enter_critical();
    Usartx_SendData(USART2, D_DGUS_FRAME_HEAD1);
    Usartx_SendData(USART2, D_DGUS_FRAME_HEAD2);
    Usartx_SendData(USART2, 2 * len + 3);
    Usartx_SendData(USART2, 0x82);
    Usartx_SendData(USART2, addr >> 8);
    Usartx_SendData(USART2, addr & 0xff);
    for(i = 0; i < len; i++)
    {
        Usartx_SendData(USART2, *(pBuffer + i) >> 8);
        Usartx_SendData(USART2, *(pBuffer + i) & 0xff);
    }
#ifdef D_PC_SEND
    Usartx_SendData(USART2, D_DGUS_FRAME_END1);
    Usartx_SendData(USART2, D_DGUS_FRAME_END2);
#endif
    rt_thread_delay(50);//
    rt_exit_critical();
#endif
    
    uint8_t i;
    D_RS485DE_RE = ENABLE;    
    g_aUSART2_TX_BUF[0] = D_DGUS_FRAME_HEAD1;
    g_aUSART2_TX_BUF[1] = D_DGUS_FRAME_HEAD2;
    g_aUSART2_TX_BUF[2] = 2 * len + 3;
    g_aUSART2_TX_BUF[3] =  0x82;
    g_aUSART2_TX_BUF[4] =  addr >> 8;
    g_aUSART2_TX_BUF[5] =  addr & 0xff;
    for(i = 0; i < len; i++)
    {
        g_aUSART2_TX_BUF[2 * len + 3 + i] =  *(pBuffer + i) >> 8;
        g_aUSART2_TX_BUF[2 * len + 3 + i] =  *(pBuffer + i) & 0xff;
    }
#ifdef D_PC_SEND
    g_aUSART2_TX_BUF[2 * len + 5] = D_DGUS_FRAME_END1;
    g_aUSART2_TX_BUF[2 * len + 6] = D_DGUS_FRAME_END2;
#endif
    Usart2_TX_DMA_Enable( 2 * len + 7);
    rt_thread_delay(50);
}

经测试 协议发送完整

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Keil5中使用STM32F103的RS485发送接收数据可以采用以下步骤: 1. 配置USART的硬件参数,包括波特率、数据位、停止位、奇偶校验等等。 2. 配置GPIO口的输出控制,将控制485芯片的使能引脚配置为输出,将控制方向的引脚配置为输出。 3. 在发送数据前,将485芯片的使能引脚置为高电平,将方向引脚置为发送模式。 4. 发送数据前,检查USART的状态寄存器,确保USART可以发送数据,否则等待USART空闲。 5. 调用USART发送函数,将要发送数据发送出去。 6. 发送完成后,将方向引脚置为接收模式,等待接收数据。 7. 检查USART的状态寄存器,确保USART可以接收数据,否则等待USART空闲。 8. 调用USART接收函数,读取接收到的数据。 9. 接收完成后,将485芯片的使能引脚置为低电平,表示数据传输结束。 下面是一个简单的示例代码,可以参考一下: ```c #include "stm32f10x.h" #define USARTx USART1 #define USARTx_CLK RCC_APB2Periph_USART1 #define USARTx_IRQn USART1_IRQn #define USARTx_IRQHandler USART1_IRQHandler #define GPIO_PORT GPIOA #define GPIO_PIN_TX GPIO_Pin_9 #define GPIO_PIN_RX GPIO_Pin_10 #define GPIO_PIN_EN GPIO_Pin_11 #define GPIO_PIN_DIR GPIO_Pin_12 void USART_Configuration(void) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; /* Enable GPIO clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); /* Configure USART Tx and Rx as alternate function push-pull */ GPIO_InitStructure.GPIO_Pin = GPIO_PIN_TX | GPIO_PIN_RX; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIO_PORT, &GPIO_InitStructure); /* Configure RS485 control pins as output */ GPIO_InitStructure.GPIO_Pin = GPIO_PIN_EN | GPIO_PIN_DIR; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIO_PORT, &GPIO_InitStructure); /* Enable USART clock */ RCC_APB2PeriphClockCmd(USARTx_CLK, ENABLE); /* USART configuration */ USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; 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(USARTx, &USART_InitStructure); /* Enable USART interrupts */ NVIC_EnableIRQ(USARTx_IRQn); USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE); /* Enable USART */ USART_Cmd(USARTx, ENABLE); } void USART_SendData_RS485(uint8_t *pData, uint16_t len) { /* Enable RS485 transmit mode */ GPIO_SetBits(GPIO_PORT, GPIO_PIN_EN); GPIO_SetBits(GPIO_PORT, GPIO_PIN_DIR); /* Wait for USART idle */ while((USARTx->SR & USART_SR_TC) == 0); for(int i = 0; i < len; i++) { /* Wait for USART idle */ while((USARTx->SR & USART_SR_TC) == 0); /* Send data */ USART_SendData(USARTx, pData[i]); } /* Wait for USART idle */ while((USARTx->SR & USART_SR_TC) == 0); /* Enable RS485 receive mode */ GPIO_ResetBits(GPIO_PORT, GPIO_PIN_EN); GPIO_ResetBits(GPIO_PORT, GPIO_PIN_DIR); } void USARTx_IRQHandler(void) { if(USART_GetITStatus(USARTx, USART_IT_RXNE) != RESET) { uint8_t data = USART_ReceiveData(USARTx); /* Process received data */ } } ``` 在上面的代码中,我们通过GPIO控制485芯片的使能引脚和方向引脚。在发送数据时,我们将使能引脚置为高电平,方向引脚置为发送模式;在接收数据时,我们将使能引脚置为低电平,方向引脚置为接收模式。 注意,在发送数据时,我们需要等待USART的状态寄存器中的TC位为1,表示USART发送数据完毕,才能继续发送下一个数据;在接收数据时,我们需要在USART接收到数据后立即调用USART_ReceiveData函数,将数据从接收缓冲区中读取出来。 希望这个简单的示例代码能够帮助您完成RS485发送和接收。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值