surge 发表于 2020-6-5 14:48:27

STM32F407 RS485高频传输而产生乱码的问题

小弟在一项目碰到一难道,RS485每隔5ms向应上位机时,上位机接到的数据是乱码,尝试无数办法,而不得解,特来此宝地,请教各位大佬。

硬件平台:STM32F407ZGT6 + FreeRTOS
硬件接口资源:串口2(RS232)做为调试使用;串口5(RS485)做为读卡器使用;串口6(RS485)做为与上位机通讯使用,RS485全为半双工;
串口驱动:bsp_uart_fifo.c (v1.0 库函数版)
项目功能,单片机读卡,将处理后的卡号数据传送给上位机
工作流程:
STM32F407 串口通讯,波特率38400,通过RS485跟上位机通讯,我的设备做为从机,上位机做为主机,上位机每隔5ms下发一条5字节的指令,要求我在5ms响应,并且在20ms完成传输,需要我上送到上位机的字节数是37字节。
问题现象:
收到上位机指令,然后响应上位机指令,这个过程中上机位收到的数据会有乱码出现。我尝试过使用串口中断接收中断发送;中断接收DMA发送等方式,都还有乱码。使用示波器卡了一下,上位机下发5字节,到STM32F407需要3MS,SMT32F407上送37字节到上位机需要10MS。这跟对方的要求就冲突了。


所有,请教下各位大神,你们有过类似RS485高频传输的经验吗?或者对这种现象,有什么建议?感谢!!!

surge 发表于 2020-6-5 14:55:23

另外,在这个贴子 http://armbbs.cn/forum.php?mod=viewthread&tid=82149&extra=page%3D1 ,硬汉哥有帮忙看,但是小弟一直没有搞定。。。惭愧。

eric2013 发表于 2020-6-5 15:02:59

方便的话,画个框图。

雷鹏 发表于 2020-6-5 15:19:22

485通讯加GND了没?

surge 发表于 2020-6-5 15:29:20

雷鹏 发表于 2020-6-5 15:19
485通讯加GND了没?

485通讯没有接地。

surge 发表于 2020-6-5 15:51:25

附件关于处理串口的代码static void vTaskComm(void *pvParameters)
{
    uint16_t recvLen = 0;
    uint8_t buf = {0};
    uint8_t crc = 0;   

   
    ELEVATOR_BUFF_STRU *sendBuf = &gElevtorData;

    uint8_t defaultBuff ={ 0x5A,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
                                          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                                          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                                          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                                          0x00,0x00,0x00,0x00,0x5A };

    uint32_t i = 0;
   
    READER_BUFF_STRU *ptMsg= &gReaderMsg;
    BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
    const TickType_t xMaxBlockTime = pdMS_TO_TICKS(10); /* 设置最大等待时间为200ms */
   
    //获取当前设备的ID
    uint16_t readID = bsp_dipswitch_read();   

    memset(&gReaderMsg,0x00,sizeof(READER_BUFF_STRU));   

    /* 清零 */
    ptMsg->authMode = 0; //默认为刷卡
    ptMsg->dataLen = 0;
    memset(ptMsg->data,0x00,sizeof(ptMsg->data));

    memset(sendBuf->data,0x00,sizeof(sendBuf->data));   
   
    while (1)
    {

      memset(buf,0x00,sizeof(buf));      
      recvLen = RS485_Recv(COM6,buf,sizeof(buf));
          if(buf == 0x5A && buf == readID)
          {
                crc= xorCRC(buf,4);                
                if(crc != buf)
                {
                    continue;
                }

            xReturn = xQueueReceive( xTransDataQueue,    /* 消息队列的句柄 */
                                     (void *)&sendBuf,/*这里获取的是结构体的地址 */
                                     xMaxBlockTime); /* 设置阻塞时间 */
            if(pdTRUE == xReturn)
            {   
                printf("2.%02x,%02x\r\n",sendBuf->data,sendBuf->data);
            }
            else
            {
                //发送默认数据包
                memcpy(sendBuf->data,defaultBuff,MAX_RS485_LEN);
            }

            //BSP_DMAUsart6Puts(sendBuf->data,MAX_RS485_LEN);
            RS485_SendBuf(COM6,sendBuf->data,MAX_RS485_LEN);

      }
        /* 发送事件标志,表示任务正常运行 */      
        xEventGroupSetBits(xCreatedEventGroup, TASK_BIT_1);
      vTaskDelay(5);
    }
}关于串口部分的代码
/*
*********************************************************************************************************
*
*        模块名称 : 串口中断+FIFO驱动模块
*        文件名称 : bsp_uart_fifo.c
*        版    本 : V1.0
*        说    明 : 采用串口中断+FIFO模式实现多个串口的同时访问
*        修改记录 :
*                版本号日期       作者    说明
*                V1.0    2013-02-01 armfly正式发布
*                V1.1    2013-06-09 armflyFiFo结构增加TxCount成员变量,方便判断缓冲区满; 增加 清FiFo的函数
*                V1.2        2014-09-29 armfly增加RS485 MODBUS接口。接收到新字节后,直接执行回调函数。
*                V1.3        2015-07-23 armfly增加 UART_T 结构的读写指针几个成员变量必须增加 __IO 修饰,否则优化后
*                                        会导致串口发送函数死机。
*                V1.4        2015-08-04 armfly解决UART4配置bugGPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART1);
*                V1.5        2015-10-08 armfly增加修改波特率的接口函数
*
*        Copyright (C), 2015-2020
*
*********************************************************************************************************
*/

#include "bsp_uart_fifo.h"
#include "bsp_time.h"
#include "sys.h"




#if 1
#pragma import(__use_no_semihosting)            
//标准库需要的支持函数               
struct __FILE
{
        int handle;
};

FILE __stdout;      
//定义_sys_exit()以避免使用半主机模式   
void _sys_exit(int x)
{
        x = x;
}

/*
*********************************************************************************************************
*        函 数 名: fputc
*        功能说明: 重定义putc函数,这样可以使用printf函数从串口1打印输出
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
int fputc(int ch, FILE *f)
{
#if 0        /* 将需要printf的字符通过串口中断FIFO发送出去,printf函数会立即返回 */
        comSendChar(COM1, ch);

        return ch;
#else        /* 采用阻塞方式发送每个字符,等待数据发送完毕 */
        /* 写一个字节到USART1 */
        USART_SendData(USART2, (uint8_t) ch);

        /* 等待发送结束 */
        while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET)
        {}

        return ch;
#endif
}

/*
*********************************************************************************************************
*        函 数 名: fgetc
*        功能说明: 重定义getc函数,这样可以使用getchar函数从串口1输入数据
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
int fgetc(FILE *f)
{

#if 1        /* 从串口接收FIFO中取1个数据, 只有取到数据才返回 */
        uint8_t ucData;

        while(comGetChar(COM2, &ucData) == 0);

        return ucData;
#else
        /* 等待串口1输入数据 */
        while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);

        return (int)USART_ReceiveData(USART1);
#endif
}

#endif



/* 定义每个串口结构体变量 */
#if UART1_FIFO_EN == 1
        static UART_T g_tUart1;
        static uint8_t g_TxBuf1;                /* 发送缓冲区 */
        static uint8_t g_RxBuf1;                /* 接收缓冲区 */
#endif

#if UART2_FIFO_EN == 1
        static UART_T g_tUart2;
        static uint8_t g_TxBuf2;                /* 发送缓冲区 */
        static uint8_t g_RxBuf2;                /* 接收缓冲区 */
#endif

#if UART3_FIFO_EN == 1
        static UART_T g_tUart3;
        static uint8_t g_TxBuf3;                /* 发送缓冲区 */
        static uint8_t g_RxBuf3;                /* 接收缓冲区 */
#endif

#if UART4_FIFO_EN == 1
        static UART_T g_tUart4;
        static uint8_t g_TxBuf4;                /* 发送缓冲区 */
        static uint8_t g_RxBuf4;                /* 接收缓冲区 */
#endif

#if UART5_FIFO_EN == 1
        static UART_T g_tUart5;
        static uint8_t g_TxBuf5;                /* 发送缓冲区 */
        static uint8_t g_RxBuf5;                /* 接收缓冲区 */
#endif

#if UART6_FIFO_EN == 1
        static UART_T g_tUart6;
        static uint8_t g_TxBuf6;                /* 发送缓冲区 */
        static uint8_t g_RxBuf6;                /* 接收缓冲区 */
#endif

static void UartVarInit(void);
static void InitHardUart(void);
static void UartSend(UART_T *_pUart, uint8_t *_ucaBuf, uint16_t _usLen);
static uint8_t UartGetChar(UART_T *_pUart, uint8_t *_pByte);
static void UartIRQ(UART_T *_pUart);
static void ConfigUartNVIC(void);




/*
*********************************************************************************************************
*        函 数 名: bsp_InitUart
*        功能说明: 初始化串口硬件,并对全局变量赋初值.
*        形    参:无
*        返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitUart(void)
{
        UartVarInit();                /* 必须先初始化全局变量,再配置硬件 */
        InitHardUart();                /* 配置串口的硬件参数(波特率等) */
        RS485_InitTXE();        /* 配置RS485芯片的发送使能硬件,配置为推挽输出 */
        ConfigUartNVIC();        /* 配置串口中断 */
}

/*
*********************************************************************************************************
*        函 数 名: ComToUart
*        功能说明: 将COM端口号转换为UART指针
*        形    参: _ucPort: 端口号(COM1 - COM6)
*        返 回 值: uart指针
*********************************************************************************************************
*/
UART_T *ComToUart(COM_PORT_E _ucPort)
{
        if (_ucPort == COM1)
        {
                #if UART1_FIFO_EN == 1
                        return &g_tUart1;
                #else
                        return 0;
                #endif
        }
        else if (_ucPort == COM2)
        {
                #if UART2_FIFO_EN == 1
                        return &g_tUart2;
                #else
                        return 0;
                #endif
        }
        else if (_ucPort == COM3)
        {
                #if UART3_FIFO_EN == 1
                        return &g_tUart3;
                #else
                        return 0;
                #endif
        }
        else if (_ucPort == COM4)
        {
                #if UART4_FIFO_EN == 1
                        return &g_tUart4;
                #else
                        return 0;
                #endif
        }
        else if (_ucPort == COM5)
        {
                #if UART5_FIFO_EN == 1
                        return &g_tUart5;
                #else
                        return 0;
                #endif
        }
        else if (_ucPort == COM6)
        {
                #if UART6_FIFO_EN == 1
                        return &g_tUart6;
                #else
                        return 0;
                #endif
        }
        else
        {
                /* 不做任何处理 */
                return 0;
        }
}


/*
*********************************************************************************************************
*        函 数 名: ComToUart
*        功能说明: 将COM端口号转换为 USART_TypeDef* USARTx
*        形    参: _ucPort: 端口号(COM1 - COM6)
*        返 回 值: USART_TypeDef*,USART1, USART2, USART3, UART4, UART5
*********************************************************************************************************
*/
USART_TypeDef *ComToUSARTx(COM_PORT_E _ucPort)
{
        if (_ucPort == COM1)
        {
                #if UART1_FIFO_EN == 1
                        return USART1;
                #else
                        return 0;
                #endif
        }
        else if (_ucPort == COM2)
        {
                #if UART2_FIFO_EN == 1
                        return USART2;
                #else
                        return 0;
                #endif
        }
        else if (_ucPort == COM3)
        {
                #if UART3_FIFO_EN == 1
                        return USART3;
                #else
                        return 0;
                #endif
        }
        else if (_ucPort == COM4)
        {
                #if UART4_FIFO_EN == 1
                        return UART4;
                #else
                        return 0;
                #endif
        }
        else if (_ucPort == COM5)
        {
                #if UART5_FIFO_EN == 1
                        return UART5;
                #else
                        return 0;
                #endif
        }
        else if (_ucPort == COM6)
        {
                #if UART6_FIFO_EN == 1
                        return USART6;
                #else
                        return 0;
                #endif
        }
        else
        {
                /* 不做任何处理 */
                return 0;
        }
}

/*
*********************************************************************************************************
*        函 数 名: comSendBuf
*        功能说明: 向串口发送一组数据。数据放到发送缓冲区后立即返回,由中断服务程序在后台完成发送
*        形    参: _ucPort: 端口号(COM1 - COM6)
*                          _ucaBuf: 待发送的数据缓冲区
*                          _usLen : 数据长度
*        返 回 值: 无
*********************************************************************************************************
*/
void comSendBuf(COM_PORT_E _ucPort, uint8_t *_ucaBuf, uint16_t _usLen)
{
        UART_T *pUart;

        pUart = ComToUart(_ucPort);
        if (pUart == 0)
        {
                return;
        }

        if (pUart->SendBefor != 0)
        {
                pUart->SendBefor();                /* 如果是RS485通信,可以在这个函数中将RS485设置为发送模式 */
        }

        UartSend(pUart, _ucaBuf, _usLen);
}

/*
*********************************************************************************************************
*        函 数 名: comSendChar
*        功能说明: 向串口发送1个字节。数据放到发送缓冲区后立即返回,由中断服务程序在后台完成发送
*        形    参: _ucPort: 端口号(COM1 - COM6)
*                          _ucByte: 待发送的数据
*        返 回 值: 无
*********************************************************************************************************
*/
void comSendChar(COM_PORT_E _ucPort, uint8_t _ucByte)
{
        comSendBuf(_ucPort, &_ucByte, 1);
}

/*
*********************************************************************************************************
*        函 数 名: comGetChar
*        功能说明: 从接收缓冲区读取1字节,非阻塞。无论有无数据均立即返回。
*        形    参: _ucPort: 端口号(COM1 - COM5)
*                          _pByte: 接收到的数据存放在这个地址
*        返 回 值: 0 表示无数据, 1 表示读取到有效字节
*********************************************************************************************************
*/
uint8_t comGetChar(COM_PORT_E _ucPort, uint8_t *_pByte)
{
        UART_T *pUart;

        pUart = ComToUart(_ucPort);
        if (pUart == 0)
        {
                return 0;
        }

        return UartGetChar(pUart, _pByte);
}

uint8_t comRecvBuff(COM_PORT_E _ucPort,uint8_t *buf, uint16_t len)
{
    uint8_t i = 0;

    UART_T *pUart;
        pUart = ComToUart(_ucPort);
   
        if (pUart == 0)
        {
                return 0;
        }
   
    if(len > pUart->usRxCount)//指定读取长度大于实际接收到的数据长度时
    {
      len=pUart->usRxCount; //读取长度设置为实际接收到的数据长度
    }
   
    for(i=0;i<len;i++)//拷贝接收到的数据到接收指针中
    {
      UartGetChar(pUart,buf+i);//将数据复制到buf中
    }

    return len;                   //返回实际读取长度
}


/*
*********************************************************************************************************
*        函 数 名: comClearTxFifo
*        功能说明: 清零串口发送缓冲区
*        形    参: _ucPort: 端口号(COM1 - COM6)
*        返 回 值: 无
*********************************************************************************************************
*/
void comClearTxFifo(COM_PORT_E _ucPort)
{
        UART_T *pUart;

        pUart = ComToUart(_ucPort);
        if (pUart == 0)
        {
                return;
        }

        pUart->usTxWrite = 0;
        pUart->usTxRead = 0;
        pUart->usTxCount = 0;
}

/*
*********************************************************************************************************
*        函 数 名: comClearRxFifo
*        功能说明: 清零串口接收缓冲区
*        形    参: _ucPort: 端口号(COM1 - COM6)
*        返 回 值: 无
*********************************************************************************************************
*/
void comClearRxFifo(COM_PORT_E _ucPort)
{
        UART_T *pUart;

        pUart = ComToUart(_ucPort);
        if (pUart == 0)
        {
                return;
        }

        pUart->usRxWrite = 0;
        pUart->usRxRead = 0;
        pUart->usRxCount = 0;
}

/*
*********************************************************************************************************
*        函 数 名: comSetBaud
*        功能说明: 设置串口的波特率
*        形    参: _ucPort: 端口号(COM1 - COM5)
*                          _BaudRate: 波特率,0-4500000, 最大4.5Mbps
*        返 回 值: 无
*********************************************************************************************************
*/
void comSetBaud(COM_PORT_E _ucPort, uint32_t _BaudRate)
{
        USART_TypeDef* USARTx;
       
        USARTx = ComToUSARTx(_ucPort);
        if (USARTx == 0)
        {
                return;
        }
       
        USART_SetBaudRate(USARTx, _BaudRate);
}

/*
*********************************************************************************************************
*        函 数 名: USART_SetBaudRate
*        功能说明: 修改波特率寄存器,不更改其他设置。如果使用 USART_Init函数, 则会修改硬件流控参数和RX,TX配置
*                          根据固件库中 USART_Init函数,将其中配置波特率的部分单独提出来封装为一个函数
*        形    参: USARTx : USART1, USART2, USART3, UART4, UART5
*                          BaudRate : 波特率,取值 0 - 4500000
*        返 回 值: 无
*********************************************************************************************************
*/
void USART_SetBaudRate(USART_TypeDef* USARTx, uint32_t BaudRate)
{
        uint32_t tmpreg = 0x00, apbclock = 0x00;
        uint32_t integerdivider = 0x00;
        uint32_t fractionaldivider = 0x00;
        RCC_ClocksTypeDef RCC_ClocksStatus;

        /* Check the parameters */
        assert_param(IS_USART_ALL_PERIPH(USARTx));
        assert_param(IS_USART_BAUDRATE(BaudRate));

        /*---------------------------- USART BRR Configuration -----------------------*/
        /* Configure the USART Baud Rate */
        RCC_GetClocksFreq(&RCC_ClocksStatus);

        if ((USARTx == USART1) || (USARTx == USART6))
        {
                apbclock = RCC_ClocksStatus.PCLK2_Frequency;
        }
        else
        {
                apbclock = RCC_ClocksStatus.PCLK1_Frequency;
        }

        /* Determine the integer part */
        if ((USARTx->CR1 & USART_CR1_OVER8) != 0)
        {
                /* Integer part computing in case Oversampling mode is 8 Samples */
                integerdivider = ((25 * apbclock) / (2 * (BaudRate)));   
        }
        else /* if ((USARTx->CR1 & USART_CR1_OVER8) == 0) */
        {
                /* Integer part computing in case Oversampling mode is 16 Samples */
                integerdivider = ((25 * apbclock) / (4 * (BaudRate)));   
        }
        tmpreg = (integerdivider / 100) << 4;

        /* Determine the fractional part */
        fractionaldivider = integerdivider - (100 * (tmpreg >> 4));

        /* Implement the fractional part in the register */
        if ((USARTx->CR1 & USART_CR1_OVER8) != 0)
        {
                tmpreg |= ((((fractionaldivider * 8) + 50) / 100)) & ((uint8_t)0x07);
        }
        else /* if ((USARTx->CR1 & USART_CR1_OVER8) == 0) */
        {
                tmpreg |= ((((fractionaldivider * 16) + 50) / 100)) & ((uint8_t)0x0F);
        }

        /* Write to USART BRR register */
        USARTx->BRR = (uint16_t)tmpreg;
}




/* 如果是RS485通信,请按如下格式编写函数*/

/*
*********************************************************************************************************
*        函 数 名: RS485_InitTXE
*        功能说明: 配置RS485发送使能口线 TXE
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
void RS485_InitTXE(void)
{
   
        GPIO_InitTypeDef GPIO_InitStructure;

    #if UART1_RS485_EN == 1
    #endif

    #if UART2_RS485_EN == 1
    #endif

    #if UART3_RS485_EN == 1

    #endif

    #if UART4_RS485_EN == 1

    #endif   

    #if UART5_RS485_EN == 1
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);        /* 打开GPIO时钟 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;                /* 设为输出口 */
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                /* 设为推挽 */
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        /* 无上拉电阻 */
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;        /* IO口最大速度 */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
        GPIO_Init(GPIOD, &GPIO_InitStructure);   
    #endif

    #if UART6_RS485_EN == 1
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);        /* 打开GPIO时钟 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;                /* 设为输出口 */
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                /* 设为推挽 */
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        /* 无上拉电阻 */
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;        /* IO口最大速度 */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
        GPIO_Init(GPIOA, &GPIO_InitStructure);      

    #endif   
}

/*
*********************************************************************************************************
*        函 数 名: RS485_SetBaud
*        功能说明: 修改485串口的波特率。
*        形    参: _baud : 波特率.0-4500000
*        返 回 值: 无
*********************************************************************************************************
*/
void RS485_SetBaud(COM_PORT_E _ucPort,uint32_t _baud)
{
        comSetBaud(_ucPort, _baud);
}

/*
*********************************************************************************************************
*        函 数 名: RS485_SendBefor
*        功能说明: 发送数据前的准备工作。对于RS485通信,请设置RS485芯片为发送状态,
*                          并修改 UartVarInit()中的函数指针等于本函数名,比如 g_tUart2.SendBefor = RS485_SendBefor
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
void RS485_SendBefor(void)
{
       
    #if UART1_RS485_EN == 1
    #endif

    #if UART2_RS485_EN == 1
    #endif

    #if UART3_RS485_EN == 1

    #endif

    #if UART4_RS485_EN == 1

    #endif   

    #if UART5_RS485_EN == 1
    RS485_U5_TX_EN();/* 切换RS485收发芯片为发送模式 */
    #endif

    #if UART6_RS485_EN == 1
    RS485_U6_TX_EN();
    #endif      
}

/*
*********************************************************************************************************
*        函 数 名: RS485_SendOver
*        功能说明: 发送一串数据结束后的善后处理。对于RS485通信,请设置RS485芯片为接收状态,
*                          并修改 UartVarInit()中的函数指针等于本函数名,比如 g_tUart2.SendOver = RS485_SendOver
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
void RS485_SendOver(void)
{
       
    #if UART1_RS485_EN == 1
    #endif

    #if UART2_RS485_EN == 1
    #endif

    #if UART3_RS485_EN == 1

    #endif

    #if UART4_RS485_EN == 1

    #endif   

    #if UART5_RS485_EN == 1
    RS485_U5_RX_EN(); /* 切换RS485收发芯片为接收模式 */
    #endif

    #if UART6_RS485_EN == 1
    RS485_U6_RX_EN();
    #endif   
}


/*
*********************************************************************************************************
*        函 数 名: RS485_SendBuf
*        功能说明: 通过RS485芯片发送一串数据。注意,本函数不等待发送完毕。
*        形    参: _ucaBuf : 数据缓冲区
*                          _usLen : 数据长度
*        返 回 值: 无
*********************************************************************************************************
*/
void RS485_SendBuf(COM_PORT_E _ucPort,uint8_t *_ucaBuf, uint16_t _usLen)
{
        comSendBuf(_ucPort, _ucaBuf, _usLen);
}


/*
*********************************************************************************************************
*        函 数 名: RS485_SendStr
*        功能说明: 向485总线发送一个字符串,0结束。
*        形    参: _pBuf 字符串,0结束
*        返 回 值: 无
*********************************************************************************************************
*/
void RS485_SendStr(COM_PORT_E _ucPort,char *_pBuf)
{
        RS485_SendBuf(_ucPort,(uint8_t *)_pBuf, strlen(_pBuf));
}

/*
*********************************************************************************************************
*        函 数 名: RS485_ReciveNew
*        功能说明: 接收到新的数据
*        形    参: _byte 接收到的新数据
*        返 回 值: 无
*********************************************************************************************************
*/
//extern void MODBUS_ReciveNew(uint8_t _byte);
void RS485_ReciveNew(uint8_t _byte)
{
//        MODBUS_ReciveNew(_byte);
}

uint16_t RS485_Recv(COM_PORT_E _ucPort,uint8_t *buf, uint16_t len)
{
    uint16_t i = 0;

    UART_T *pUart;
        pUart = ComToUart(_ucPort);
   
        if (pUart == 0)
        {
                return 0;
        }
   
    if(len > pUart->usRxCount)//指定读取长度大于实际接收到的数据长度时
    {
      len=pUart->usRxCount; //读取长度设置为实际接收到的数据长度
    }
   
    for(i=0;i<len;i++)//拷贝接收到的数据到接收指针中
    {
      UartGetChar(pUart,buf+i);//将数据复制到buf中
    }

    return len;                   //返回实际读取长度
}


uint16_t RS485_RecvAtTime(COM_PORT_E _ucPort,uint8_t *buf, uint16_t len,uint32_t timeout)
{
    //uint8_t i = 0;   
    uint16_t recvSize = len;
    uint16_t recvLen = 0;
    //uint8_t tmp = {0};
    UART_T *pUart;
        pUart = ComToUart(_ucPort);
   
        if (pUart == 0)
        {
                return 0;
        }
   
    if(recvSize > pUart->usRxCount)//指定读取长度大于实际接收到的数据长度时
    {
      recvSize=pUart->usRxCount; //读取长度设置为实际接收到的数据长度
    }


        g500usTimerRS485 = timeout;

        while (1)
        {
                if (g500usTimerRS485 == 0) return recvLen;


      UartGetChar(pUart,buf + recvLen);
      recvLen++;

                if (recvLen >= recvSize) return recvSize;
        }

}



/*
*********************************************************************************************************
*        函 数 名: UartVarInit
*        功能说明: 初始化串口相关的变量
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
static void UartVarInit(void)
{
#if UART1_FIFO_EN == 1
        g_tUart1.uart = USART1;                                                /* STM32 串口设备 */
        g_tUart1.pTxBuf = g_TxBuf1;                                        /* 发送缓冲区指针 */
        g_tUart1.pRxBuf = g_RxBuf1;                                        /* 接收缓冲区指针 */
        g_tUart1.usTxBufSize = UART1_TX_BUF_SIZE;        /* 发送缓冲区大小 */
        g_tUart1.usRxBufSize = UART1_RX_BUF_SIZE;        /* 接收缓冲区大小 */
        g_tUart1.usTxWrite = 0;                                                /* 发送FIFO写索引 */
        g_tUart1.usTxRead = 0;                                                /* 发送FIFO读索引 */
        g_tUart1.usRxWrite = 0;                                                /* 接收FIFO写索引 */
        g_tUart1.usRxRead = 0;                                                /* 接收FIFO读索引 */
        g_tUart1.usRxCount = 0;                                                /* 接收到的新数据个数 */
        g_tUart1.usTxCount = 0;                                                /* 待发送的数据个数 */
        g_tUart1.SendBefor = 0;                                                /* 发送数据前的回调函数 */
        g_tUart1.SendOver = 0;                                                /* 发送完毕后的回调函数 */
        g_tUart1.ReciveNew = 0;                                                /* 接收到新数据后的回调函数 */
#endif

#if UART2_FIFO_EN == 1
        g_tUart2.uart = USART2;                                                /* STM32 串口设备 */
        g_tUart2.pTxBuf = g_TxBuf2;                                        /* 发送缓冲区指针 */
        g_tUart2.pRxBuf = g_RxBuf2;                                        /* 接收缓冲区指针 */
        g_tUart2.usTxBufSize = UART2_TX_BUF_SIZE;        /* 发送缓冲区大小 */
        g_tUart2.usRxBufSize = UART2_RX_BUF_SIZE;        /* 接收缓冲区大小 */
        g_tUart2.usTxWrite = 0;                                                /* 发送FIFO写索引 */
        g_tUart2.usTxRead = 0;                                                /* 发送FIFO读索引 */
        g_tUart2.usRxWrite = 0;                                                /* 接收FIFO写索引 */
        g_tUart2.usRxRead = 0;                                                /* 接收FIFO读索引 */
        g_tUart2.usRxCount = 0;                                                /* 接收到的新数据个数 */
        g_tUart2.usTxCount = 0;                                                /* 待发送的数据个数 */
        g_tUart2.SendBefor = 0;                /* 发送数据前的回调函数 */
        g_tUart2.SendOver = 0;                        /* 发送完毕后的回调函数 */
        g_tUart2.ReciveNew = 0;                /* 接收到新数据后的回调函数 */
#endif

#if UART3_FIFO_EN == 1
        g_tUart3.uart = USART3;                                                /* STM32 串口设备 */
        g_tUart3.pTxBuf = g_TxBuf3;                                        /* 发送缓冲区指针 */
        g_tUart3.pRxBuf = g_RxBuf3;                                        /* 接收缓冲区指针 */
        g_tUart3.usTxBufSize = UART3_TX_BUF_SIZE;        /* 发送缓冲区大小 */
        g_tUart3.usRxBufSize = UART3_RX_BUF_SIZE;        /* 接收缓冲区大小 */
        g_tUart3.usTxWrite = 0;                                                /* 发送FIFO写索引 */
        g_tUart3.usTxRead = 0;                                                /* 发送FIFO读索引 */
        g_tUart3.usRxWrite = 0;                                                /* 接收FIFO写索引 */
        g_tUart3.usRxRead = 0;                                                /* 接收FIFO读索引 */
        g_tUart3.usRxCount = 0;                                                /* 接收到的新数据个数 */
        g_tUart3.usTxCount = 0;                                                /* 待发送的数据个数 */
        g_tUart3.SendBefor = 0;                              /* 发送数据前的回调函数 */
        g_tUart3.SendOver = 0;                                    /* 发送完毕后的回调函数 */
        g_tUart3.ReciveNew = 0;                              /* 接收到新数据后的回调函数 */
#endif

#if UART4_FIFO_EN == 1
        g_tUart4.uart = UART4;                                                /* STM32 串口设备 */
        g_tUart4.pTxBuf = g_TxBuf4;                                        /* 发送缓冲区指针 */
        g_tUart4.pRxBuf = g_RxBuf4;                                        /* 接收缓冲区指针 */
        g_tUart4.usTxBufSize = UART4_TX_BUF_SIZE;        /* 发送缓冲区大小 */
        g_tUart4.usRxBufSize = UART4_RX_BUF_SIZE;        /* 接收缓冲区大小 */
        g_tUart4.usTxWrite = 0;                                                /* 发送FIFO写索引 */
        g_tUart4.usTxRead = 0;                                                /* 发送FIFO读索引 */
        g_tUart4.usRxWrite = 0;                                                /* 接收FIFO写索引 */
        g_tUart4.usRxRead = 0;                                                /* 接收FIFO读索引 */
        g_tUart4.usRxCount = 0;                                                /* 接收到的新数据个数 */
        g_tUart4.usTxCount = 0;                                                /* 待发送的数据个数 */
        g_tUart4.SendBefor = 0;                                                /* 发送数据前的回调函数 */
        g_tUart4.SendOver = 0;                                                /* 发送完毕后的回调函数 */
        g_tUart4.ReciveNew = 0;                                                /* 接收到新数据后的回调函数 */
#endif

#if UART5_FIFO_EN == 1
        g_tUart5.uart = UART5;                                                /* STM32 串口设备 */
        g_tUart5.pTxBuf = g_TxBuf5;                                        /* 发送缓冲区指针 */
        g_tUart5.pRxBuf = g_RxBuf5;                                        /* 接收缓冲区指针 */
        g_tUart5.usTxBufSize = UART5_TX_BUF_SIZE;        /* 发送缓冲区大小 */
        g_tUart5.usRxBufSize = UART5_RX_BUF_SIZE;        /* 接收缓冲区大小 */
        g_tUart5.usTxWrite = 0;                                                /* 发送FIFO写索引 */
        g_tUart5.usTxRead = 0;                                                /* 发送FIFO读索引 */
        g_tUart5.usRxWrite = 0;                                                /* 接收FIFO写索引 */
        g_tUart5.usRxRead = 0;                                                /* 接收FIFO读索引 */
        g_tUart5.usRxCount = 0;                                                /* 接收到的新数据个数 */
        g_tUart5.usTxCount = 0;                                                /* 待发送的数据个数 */
        g_tUart5.SendBefor = RS485_SendBefor;                                                /* 发送数据前的回调函数 */
        g_tUart5.SendOver = RS485_SendOver;                                                /* 发送完毕后的回调函数 */
        g_tUart5.ReciveNew = RS485_ReciveNew;                                                /* 接收到新数据后的回调函数 */
#endif


#if UART6_FIFO_EN == 1
        g_tUart6.uart = USART6;                                                /* STM32 串口设备 */
        g_tUart6.pTxBuf = g_TxBuf6;                                        /* 发送缓冲区指针 */
        g_tUart6.pRxBuf = g_RxBuf6;                                        /* 接收缓冲区指针 */
        g_tUart6.usTxBufSize = UART6_TX_BUF_SIZE;        /* 发送缓冲区大小 */
        g_tUart6.usRxBufSize = UART6_RX_BUF_SIZE;        /* 接收缓冲区大小 */
        g_tUart6.usTxWrite = 0;                                                /* 发送FIFO写索引 */
        g_tUart6.usTxRead = 0;                                                /* 发送FIFO读索引 */
        g_tUart6.usRxWrite = 0;                                                /* 接收FIFO写索引 */
        g_tUart6.usRxRead = 0;                                                /* 接收FIFO读索引 */
        g_tUart6.usRxCount = 0;                                                /* 接收到的新数据个数 */
        g_tUart6.usTxCount = 0;                                                /* 待发送的数据个数 */
        g_tUart6.SendBefor = RS485_SendBefor;                                                /* 发送数据前的回调函数 */
        g_tUart6.SendOver = RS485_SendOver;                                                /* 发送完毕后的回调函数 */
        g_tUart6.ReciveNew = RS485_ReciveNew;                                                /* 接收到新数据后的回调函数 */
#endif
}

/*
*********************************************************************************************************
*        函 数 名: InitHardUart
*        功能说明: 配置串口的硬件参数(波特率,数据位,停止位,起始位,校验位,中断使能)适合于STM32-F4开发板
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
static void InitHardUart(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;

#if UART1_FIFO_EN == 1                /* 串口1 TX = PA9   RX = PA10 或 TX = PB6   RX = PB7*/

        /* 第1步: 配置GPIO */
        #if 1        /* TX = PA9   RX = PA10 */
                /* 打开 GPIO 时钟 */
                RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

                /* 打开 UART 时钟 */
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

                /* 将 PA9 映射为 USART1_TX */
                GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);

                /* 将 PA10 映射为 USART1_RX */
                GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);

                /* 配置 USART Tx 为复用功能 */
                GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;        /* 输出类型为推挽 */
                GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        /* 内部上拉电阻使能 */
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        /* 复用模式 */

                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOA, &GPIO_InitStructure);

                /* 配置 USART Rx 为复用功能 */
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
                GPIO_Init(GPIOA, &GPIO_InitStructure);
        #else        /* TX = PB6   RX = PB7*/
                /* 打开 GPIO 时钟 */
                RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

                /* 打开 UART 时钟 */
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

                /* 将 PB6 映射为 USART1_TX */
                GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1);

                /* 将 PB7 映射为 USART1_RX */
                GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1);

                /* 配置 USART Tx 为复用功能 */
                GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;        /* 输出类型为推挽 */
                GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        /* 内部上拉电阻使能 */
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        /* 复用模式 */

                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOB, &GPIO_InitStructure);

                /* 配置 USART Rx 为复用功能 */
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
                GPIO_Init(GPIOB, &GPIO_InitStructure);
        #endif

        /* 第2步: 配置串口硬件参数 */
        USART_InitStructure.USART_BaudRate = UART1_BAUD;        /* 波特率 */
        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(USART1, &USART_InitStructure);

        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);        /* 使能接收中断 */
        /*
                USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
                注意: 不要在此处打开发送中断
                发送中断使能在SendUart()函数打开
        */
        USART_Cmd(USART1, ENABLE);                /* 使能串口 */

        /* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
                如下语句解决第1个字节无法正确发送出去的问题 */
        USART_ClearFlag(USART1, USART_FLAG_TC);   /* 清发送完成标志,Transmission Complete flag */
#endif

#if UART2_FIFO_EN == 1                /* 串口2 TX = PD5   RX = PD6 或TX = PA2, RX = PA3*/
        /* 第1步: 配置GPIO */
        #if 1        /* 串口2 TX = PD5   RX = PD6 */
                /* 打开 GPIO 时钟 */
                RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

                /* 打开 UART 时钟 */
                RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

                /* 将 PD5 映射为 USART2_TX */
                GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_USART2);

                /* 将 PD6 映射为 USART2_RX */
                GPIO_PinAFConfig(GPIOD, GPIO_PinSource6, GPIO_AF_USART2);

                /* 配置 USART Tx 为复用功能 */
                GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;        /* 输出类型为推挽 */
                GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        /* 内部上拉电阻使能 */
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        /* 复用模式 */

                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOD, &GPIO_InitStructure);

                /* 配置 USART Rx 为复用功能 */
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
                GPIO_Init(GPIOD, &GPIO_InitStructure);

        #else        /* 串口2   TX = PA2, RX = PA3 */
                /* 打开 GPIO 时钟 */
                RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

                /* 打开 UART 时钟 */
                RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

                /* 将 PA2 映射为 USART2_TX. 在STM32-V5板中,PA2 管脚用于以太网 */
                //GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);

                /* 将 PA3 映射为 USART2_RX */
                GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);

                /* 配置 USART Tx 为复用功能 */
                GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;        /* 输出类型为推挽 */
                GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        /* 内部上拉电阻使能 */
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        /* 复用模式 */

                //GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
                //GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
                //GPIO_Init(GPIOA, &GPIO_InitStructure);

                /* 配置 USART Rx 为复用功能 */
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_Init(GPIOA, &GPIO_InitStructure);
        #endif

        /* 第2步: 配置串口硬件参数 */
        USART_InitStructure.USART_BaudRate = UART2_BAUD;        /* 波特率 */
        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(USART2, &USART_InitStructure);

        USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);        /* 使能接收中断 */
        /*
                USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
                注意: 不要在此处打开发送中断
                发送中断使能在SendUart()函数打开
        */
        USART_Cmd(USART2, ENABLE);                /* 使能串口 */

        /* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
                如下语句解决第1个字节无法正确发送出去的问题 */
        USART_ClearFlag(USART2, USART_FLAG_TC);   /* 清发送完成标志,Transmission Complete flag */
#endif

#if UART3_FIFO_EN == 1                       
    #if 0   /* 串口3 TX = PB10   RX = PB11 */
        /* 打开 GPIO 时钟 */
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

        /* 打开 UART 时钟 */
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);

        /* 将 PB10 映射为 USART3_TX */
        GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);

        /* 将 PB11 映射为 USART3_RX */
        GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);

        /* 配置 USART Tx 为复用功能 */
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;        /* 输出类型为推挽 */
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        /* 内部上拉电阻使能 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        /* 复用模式 */

        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOB, &GPIO_InitStructure);

        /* 配置 USART Rx 为复用功能 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
        GPIO_Init(GPIOB, &GPIO_InitStructure);

    #else
        /* 打开 GPIO 时钟 */
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

        /* 打开 UART 时钟 */
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);

        /* 将 PD8 映射为 USART3_TX */
        GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_USART3);

        /* 将 PD9 映射为 USART3_RX */
        GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_USART3);

        /* 配置 USART Tx 为复用功能 */
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;        /* 输出类型为推挽 */
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        /* 内部上拉电阻使能 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        /* 复用模式 */

        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOD, &GPIO_InitStructure);

        /* 配置 USART Rx 为复用功能 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
        GPIO_Init(GPIOD, &GPIO_InitStructure);

    #endif

        /* 第2步: 配置串口硬件参数 */
        USART_InitStructure.USART_BaudRate = UART3_BAUD;        /* 波特率 */
        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(USART3, &USART_InitStructure);

        USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);        /* 使能接收中断 */
        /*
                USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
                注意: 不要在此处打开发送中断
                发送中断使能在SendUart()函数打开
        */
        USART_Cmd(USART3, ENABLE);                /* 使能串口 */

        /* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
                如下语句解决第1个字节无法正确发送出去的问题 */
        USART_ClearFlag(USART3, USART_FLAG_TC);   /* 清发送完成标志,Transmission Complete flag */   
#endif

#if UART4_FIFO_EN == 1                        /* 串口4 TX = PC10   RX = PC11 */
        /* 第1步: 配置GPIO */

        /* 打开 GPIO 时钟 */
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

        /* 打开 UART 时钟 */
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);

        /* 将 PC10 映射为 UART4_TX */
        GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_UART4);

        /* 将 PC11 映射为 UART4_RX */
        GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_UART4);

        /* 配置 USART Tx 为复用功能 */
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;        /* 输出类型为推挽 */
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        /* 内部上拉电阻使能 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        /* 复用模式 */

        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOC, &GPIO_InitStructure);

        /* 配置 USART Rx 为复用功能 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
        GPIO_Init(GPIOC, &GPIO_InitStructure);

        /* 第2步: 配置串口硬件参数 */
        USART_InitStructure.USART_BaudRate = UART4_BAUD;        /* 波特率 */
        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(UART4, &USART_InitStructure);

        USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);        /* 使能接收中断 */
        /*
                USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
                注意: 不要在此处打开发送中断
                发送中断使能在SendUart()函数打开
        */
        USART_Cmd(UART4, ENABLE);                /* 使能串口 */

        /* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
                如下语句解决第1个字节无法正确发送出去的问题 */
        USART_ClearFlag(UART4, USART_FLAG_TC);   /* 清发送完成标志,Transmission Complete flag */
#endif

#if UART5_FIFO_EN == 1                        /* 串口5 TX = PC12   RX = PD2 */
        /* 第1步: 配置GPIO */

        /* 打开 GPIO 时钟 */
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC |RCC_AHB1Periph_GPIOD, ENABLE);

        /* 打开 UART 时钟 */
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);

        /* 将 PC12 映射为 UART5_TX */
        GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5);

        /* 将 PD2 映射为 UART5_RX */
        GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5);

        /* 配置 UART Tx 为复用功能 */
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;        /* 输出类型为推挽 */
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        /* 内部上拉电阻使能 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        /* 复用模式 */

        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOC, &GPIO_InitStructure);

        /* 配置 UART Rx 为复用功能 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
        GPIO_Init(GPIOD, &GPIO_InitStructure);

        /* 第2步: 配置串口硬件参数 */
        USART_InitStructure.USART_BaudRate = UART5_BAUD;        /* 波特率 */
        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(UART5, &USART_InitStructure);

        USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);        /* 使能接收中断 */
        /*
                USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
                注意: 不要在此处打开发送中断
                发送中断使能在SendUart()函数打开
        */
        USART_Cmd(UART5, ENABLE);                /* 使能串口 */

        /* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
                如下语句解决第1个字节无法正确发送出去的问题 */
        USART_ClearFlag(UART5, USART_FLAG_TC);   /* 清发送完成标志,Transmission Complete flag */
#endif

#if UART6_FIFO_EN == 1                        /* PG14/USART6_TX , PC7/USART6_RX,PG8/USART6_RTS, PG15/USART6_CTS */
        /* 第1步: 配置GPIO */

        /* 打开 GPIO 时钟 */
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

        /* 打开 UART 时钟 */
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE);

        /* 将 PC6 映射为 USART6_TX */
        GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_USART6);

        /* 将 PC7 映射为 USART6_RX */
        GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_USART6);


        /* 配置 PG14/USART6_TX 为复用功能 */
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;        /* 输出类型为推挽 */
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        /* 内部上拉电阻使能 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        /* 复用模式 */

        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOC, &GPIO_InitStructure);

        /* 配置 PC7/USART6_RX 为复用功能 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
        GPIO_Init(GPIOC, &GPIO_InitStructure);


        /* 第2步: 配置串口硬件参数 */
        USART_InitStructure.USART_BaudRate = UART6_BAUD;        /* 波特率 */
        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_RTS_CTS;        /* 选择硬件流控 */
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;        /* 不要硬件流控 */
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
        USART_Init(USART6, &USART_InitStructure);

        USART_ITConfig(USART6, USART_IT_RXNE, ENABLE);        /* 使能接收中断 */
        /*
                USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
                注意: 不要在此处打开发送中断
                发送中断使能在SendUart()函数打开
        */
        USART_Cmd(USART6, ENABLE);                /* 使能串口 */

        /* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
                如下语句解决第1个字节无法正确发送出去的问题 */
        USART_ClearFlag(USART6, USART_FLAG_TC);   /* 清发送完成标志,Transmission Complete flag */
#endif
}

/*
*********************************************************************************************************
*        函 数 名: ConfigUartNVIC
*        功能说明: 配置串口硬件中断.
*        形    参:无
*        返 回 值: 无
*********************************************************************************************************
*/
static void ConfigUartNVIC(void)
{
        NVIC_InitTypeDef NVIC_InitStructure;

        /* Configure the NVIC Preemption Priority Bits */
        /*        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);--- 在 bsp.c 中 bsp_Init() 中配置中断优先级组 */

#if UART1_FIFO_EN == 1
        /* 使能串口1中断 */
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
#endif

#if UART2_FIFO_EN == 1
        /* 使能串口2中断 */
        NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
#endif

#if UART3_FIFO_EN == 1
        /* 使能串口3中断t */
        NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
#endif

#if UART4_FIFO_EN == 1
        /* 使能串口4中断t */
        NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
#endif

#if UART5_FIFO_EN == 1
        /* 使能串口5中断t */
        NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
#endif

#if UART6_FIFO_EN == 1
        /* 使能串口6中断t */
        NVIC_InitStructure.NVIC_IRQChannel = USART6_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
#endif
}

/*
*********************************************************************************************************
*        函 数 名: UartSend
*        功能说明: 填写数据到UART发送缓冲区,并启动发送中断。中断处理函数发送完毕后,自动关闭发送中断
*        形    参:无
*        返 回 值: 无
*********************************************************************************************************
*/
static void UartSend(UART_T *_pUart, uint8_t *_ucaBuf, uint16_t _usLen)
{
        uint16_t i;
   

        for (i = 0; i < _usLen; i++)
        {
                /* 如果发送缓冲区已经满了,则等待缓冲区空 */

                /* 当 _pUart->usTxBufSize == 1 时, 下面的函数会死掉(待完善) */
                while (1)
                {
                        __IO uint16_t usCount;
//                        DISABLE_INT();
                        usCount = _pUart->usTxCount;
//                        ENABLE_INT();

                        if (usCount < _pUart->usTxBufSize)
                        {
                                break;
                        }
            else if(usCount == _pUart->usTxBufSize)/* 数据已填满缓冲区 */
                        {
                                if((_pUart->uart->CR1 & USART_CR1_TXEIE) == 0)
                                {
                                        SET_BIT(_pUart->uart->CR1, USART_CR1_TXEIE);
                                }
                        }
                }
       

                /* 将新数据填入发送缓冲区 */
                _pUart->pTxBuf = _ucaBuf;

//                DISABLE_INT();
                if (++_pUart->usTxWrite >= _pUart->usTxBufSize)
                {
                        _pUart->usTxWrite = 0;
                }
                _pUart->usTxCount++;
//                ENABLE_INT();
        }

        USART_ITConfig(_pUart->uart, USART_IT_TXE, ENABLE);
}

/*
*********************************************************************************************************
*        函 数 名: UartGetChar
*        功能说明: 从串口接收缓冲区读取1字节数据 (用于主程序调用)
*        形    参: _pUart : 串口设备
*                          _pByte : 存放读取数据的指针
*        返 回 值: 0 表示无数据1表示读取到数据
*********************************************************************************************************
*/
static uint8_t UartGetChar(UART_T *_pUart, uint8_t *_pByte)
{
        uint16_t usCount;

        /* usRxWrite 变量在中断函数中被改写,主程序读取该变量时,必须进行临界区保护 */
//        DISABLE_INT();
        usCount = _pUart->usRxCount;
//        ENABLE_INT();

        /* 如果读和写索引相同,则返回0 */
        //if (_pUart->usRxRead == usRxWrite)
        if (usCount == 0)        /* 已经没有数据 */
        {
                return 0;
        }
        else
        {
                *_pByte = _pUart->pRxBuf;                /* 从串口接收FIFO取1个数据 */

                /* 改写FIFO读索引 */
//                DISABLE_INT();
                if (++_pUart->usRxRead >= _pUart->usRxBufSize)
                {
                        _pUart->usRxRead = 0;
                }
                _pUart->usRxCount--;
//                ENABLE_INT();
                return 1;
        }
}

/*
*********************************************************************************************************
*        函 数 名: comGetBuff
*        功能说明: 读取指字长度的字符
*        形    参: _ucPort: 端口号(COM1 - COM5)
*                          Buff: 接收到的数据存放在这个地址
             len:要读取的字符长度
*        返 回 值: 0 读取失败; 非零为读取到的数据长度
*********************************************************************************************************
*/
uint16_t comGetBuff(COM_PORT_E _ucPort,uint8_t *Buff, uint16_t RecvSize,uint16_t timeout_MilliSeconds)
{

//        uint16_t RecvLen = 0;
//        uint8_t ch = {0};

//        if (len == 0 || Buff == NULL) return 0;

//        while (len--)
//        {
//                if (comGetChar (_ucPort,ch) == 1)
//                {
//                        Buff = ch;
//            printf("push : %d\r\n",RecvLen);
//                }

//                if (RecvLen >= len) return RecvLen;
//        }

//        return RecvLen;


        uint16_t RecvLen = 0;
        uint8_t tmp = {0};

        if (RecvSize == 0) return 0;

        g500usTimerUART = timeout_MilliSeconds;

        while (1)
        {
                if (g500usTimerUART == 0) return RecvLen;

                if (comGetChar (_ucPort,tmp) == 1)
                {
                        Buff = tmp;
                }

                if (RecvLen >= RecvSize) return RecvLen;
        }

}



/*
*********************************************************************************************************
*        函 数 名: UartIRQ
*        功能说明: 供中断服务程序调用,通用串口中断处理函数
*        形    参: _pUart : 串口设备
*        返 回 值: 无
*********************************************************************************************************
*/
static void UartIRQ(UART_T *_pUart)
{
    uint32_t ulReturn;
    /* 进入临界段,临界段可以嵌套 */
    ulReturn = taskENTER_CRITICAL_FROM_ISR();
   

        /* 处理接收中断*/
        if (USART_GetITStatus(_pUart->uart, USART_IT_RXNE) != RESET)
        {
                /* 从串口接收数据寄存器读取数据存放到接收FIFO */
                uint8_t ch;

                ch = USART_ReceiveData(_pUart->uart);
                _pUart->pRxBuf = ch;
                if (++_pUart->usRxWrite >= _pUart->usRxBufSize)
                {
                        _pUart->usRxWrite = 0;
                }
      
                if (_pUart->usRxCount < _pUart->usRxBufSize)
                {
                        _pUart->usRxCount++;
                }

                /* 回调函数,通知应用程序收到新数据,一般是发送1个消息或者设置一个标记 */
                //if (_pUart->usRxWrite == _pUart->usRxRead)
                //if (_pUart->usRxCount == 1)
                {
                        if (_pUart->ReciveNew)
                        {
                                _pUart->ReciveNew(ch);
                        }
                }
        }

        /* 处理发送缓冲区空中断 */
        if (USART_GetITStatus(_pUart->uart, USART_IT_TXE) != RESET)
        {
                //if (_pUart->usTxRead == _pUart->usTxWrite)
                if (_pUart->usTxCount == 0)
                {
                        /* 发送缓冲区的数据已取完时, 禁止发送缓冲区空中断 (注意:此时最后1个数据还未真正发送完毕)*/
                        USART_ITConfig(_pUart->uart, USART_IT_TXE, DISABLE);

                        /* 使能数据发送完毕中断 */
                        USART_ITConfig(_pUart->uart, USART_IT_TC, ENABLE);
                }
                else
                {
                        /* 从发送FIFO取1个字节写入串口发送数据寄存器 */
                        USART_SendData(_pUart->uart, _pUart->pTxBuf);
                        if (++_pUart->usTxRead >= _pUart->usTxBufSize)
                        {
                                _pUart->usTxRead = 0;
                        }
                        _pUart->usTxCount--;
                }

        }
        /* 数据bit位全部发送完毕的中断 */
        else if (USART_GetITStatus(_pUart->uart, USART_IT_TC) != RESET)
        {
                //if (_pUart->usTxRead == _pUart->usTxWrite)
                if (_pUart->usTxCount == 0)
                {
                        /* 如果发送FIFO的数据全部发送完毕,禁止数据发送完毕中断 */
                        USART_ITConfig(_pUart->uart, USART_IT_TC, DISABLE);

                        /* 回调函数, 一般用来处理RS485通信,将RS485芯片设置为接收模式,避免抢占总线 */
                        if (_pUart->SendOver)
                        {
                                _pUart->SendOver();
                        }
                }
                else
                {
                        /* 正常情况下,不会进入此分支 */

                        /* 如果发送FIFO的数据还未完毕,则从发送FIFO取1个数据写入发送数据寄存器 */
                        USART_SendData(_pUart->uart, _pUart->pTxBuf);
                        if (++_pUart->usTxRead >= _pUart->usTxBufSize)
                        {
                                _pUart->usTxRead = 0;
                        }
                        _pUart->usTxCount--;
                }
        }

/* 退出临界段 */
taskEXIT_CRITICAL_FROM_ISR( ulReturn );       
}

/*
*********************************************************************************************************
*        函 数 名: USART1_IRQHandlerUSART2_IRQHandler USART3_IRQHandler UART4_IRQHandler UART5_IRQHandler
*        功能说明: USART中断服务程序
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
#if UART1_FIFO_EN == 1
void USART1_IRQHandler(void)
{
        UartIRQ(&g_tUart1);
}
#endif

#if UART2_FIFO_EN == 1
void USART2_IRQHandler(void)
{
        UartIRQ(&g_tUart2);
}
#endif

#if UART3_FIFO_EN == 1
void USART3_IRQHandler(void)
{
        UartIRQ(&g_tUart3);
}
#endif

#if UART4_FIFO_EN == 1
void UART4_IRQHandler(void)
{
        UartIRQ(&g_tUart4);
}
#endif

#if UART5_FIFO_EN == 1
void UART5_IRQHandler(void)
{
        UartIRQ(&g_tUart5);
}
#endif

#if UART6_FIFO_EN == 1
void USART6_IRQHandler(void)
{
        UartIRQ(&g_tUart6);
}
#endif






void RS485_U6_RX_EN(void)
{
    int i=0;

    for(i=0;i<500;i++)
    {
      ;
    }
//   GPIOA->BSRRH = GPIO_Pin_8;
   GPIO_ResetBits(GPIOA,GPIO_Pin_8);
    for(i=0;i<500;i++)
    {
      ;
    }
}

void RS485_U6_TX_EN(void)
{
    int i=0;

    for(i=0;i<500;i++)
    {
      ;
    }
//   GPIOA->BSRRL = GPIO_Pin_8;
    GPIO_SetBits(GPIOA,GPIO_Pin_8);

    for(i=0;i<500;i++)
    {
      ;
    }

}










caicaptain2 发表于 2020-6-5 15:59:35

简单计算一下,38400波特率,一般单字节需要9个位,那么1ms可以发送4.2个字节。 那么指令5个字节,耗时约1.2ms;37个回答字节耗时约8.7ms。
所以客户要求有点紧张,但是可以达到。 所以,你的F407查询间隔必须小于5ms,采用串口DMA发送。 接收也使用DMA+串口空闲中断即可。

雷鹏 发表于 2020-6-5 16:03:45

surge 发表于 2020-6-5 15:29
485通讯没有接地。

加根地线试试。

dghwjh 发表于 2020-6-5 16:08:43

波特率38400上位机下发5字节,到STM32F407需要3MS,我怎么觉得你这句话有问题5个字节无校验位50个bit   1/38400x50(bit)大约1.3ms   有点矛盾啊!

surge 发表于 2020-6-5 16:08:47

caicaptain2 发表于 2020-6-5 15:59
简单计算一下,38400波特率,一般单字节需要9个位,那么1ms可以发送4.2个字节。 那么指令5个字节,耗时约1. ...

我也按您说的这个方法尝试过,串口空闲中断+DMA发送,结果依然不好。附件RS485 空闲中断+DMA发送的代码

#include "bsp_usart6.h"
#include "FreeRTOS.h"
#include "task.h"


#define COM6_RXBUFFER_SIZE   255 //串口接收缓存器长度
#define UART6_RX_LEN         COM6_RXBUFFER_SIZE //USART1 DMA接收缓存器长度
#define UART6_TX_LEN         COM6_RXBUFFER_SIZE //USART1 DMA发送缓存器长度

typedef struct COMx_RXBUFFER
{
    u8 buffer;//接收缓存器
    u8 RxFlag;//接收数据标志 1:接收到新数据 0:无新数据被接收
    u16 RxLen;//接收到数据长度
}COMx_RXBUFFER;

//串口1接收DMA缓存
COMx_RXBUFFER Uart6_RxBuffer;


uint8_t Uart6_Tx = {0};               //串口1发送DMA缓存   
uint8_t Uart6_Rx = {0};               //串口1接收DMA缓存

static void BSP_DMAUsar6Rx_Init(void);
static void BSP_DMAUsar6Tx_Init(void);


void bsp_Usart6_Init (uint32_t BaudRate)
{
    //GPIO端口设置
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;

        RCC_AHB1PeriphClockCmd(USART1_GPIO_CLK,ENABLE); //使能GPIOA时钟
        RCC_APB2PeriphClockCmd(USART1_CLK,ENABLE);//使能USART1时钟

        //串口1对应引脚复用映射
        GPIO_PinAFConfig(USART1_GPIO_PORT,USART1_TX_SOURCE,USART1_TX_AF); //GPIOA9复用为USART1
        GPIO_PinAFConfig(USART1_GPIO_PORT,USART1_RX_SOURCE,USART1_RX_AF); //GPIOA10复用为USART1
       
        //USART1端口配置
        GPIO_InitStructure.GPIO_Pin = USART1_TX_PIN | USART1_RX_PIN; //GPIOA9与GPIOA10
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //速度50MHz
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
        GPIO_Init(USART1_GPIO_PORT,&GPIO_InitStructure); //初始化PA9,PA10

    //DE1
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);        /* 打开GPIO时钟 */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;                /* 设为输出口 */
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                /* 设为推挽 */
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        /* 无上拉电阻 */
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;        /* IO口最大速度 */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
        GPIO_Init(GPIOA, &GPIO_InitStructure);                  

   //USART1 初始化设置
        USART_InitStructure.USART_BaudRate = BaudRate;//波特率设置
        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(USART6, &USART_InitStructure); //初始化串口1   


    //开启USART6
    USART_Cmd(USART6, ENABLE);
   

        USART_ClearFlag(USART6, USART_FLAG_TC); //清除发送完成标志       
        while(USART_GetFlagStatus(USART6, USART_FLAG_TC) == RESET);        //等待空闲帧发送完成后再清零发送完成标志
        USART_ClearFlag(USART6, USART_FLAG_TC);        //清除发送完成标志
   
    //开启USART6总线空闲中断
        USART_ITConfig(USART6,USART_IT_TC,DISABLE);
        USART_ITConfig(USART6,USART_IT_RXNE,DISABLE);
        USART_ITConfig(USART6,USART_IT_TXE,DISABLE);
        USART_ITConfig(USART6,USART_IT_IDLE,ENABLE);



        //Usart1 NVIC 配置
        NVIC_InitStructure.NVIC_IRQChannel = USART6_IRQn;//串口1中断通道
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级1
        NVIC_InitStructure.NVIC_IRQChannelSubPriority =4;                //子优先级1
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
        NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器


       
    //串口接收DMA配置
    BSP_DMAUsar6Rx_Init();
    //串口发送DMA配置
    BSP_DMAUsar6Tx_Init();

    USART_DMACmd(USART6,USART_DMAReq_Tx,ENABLE);//采用DMA方式发送   
    USART_DMACmd(USART6,USART_DMAReq_Rx,ENABLE); //采用DMA方式接收

}

void USART6_IRQHandler(void)
{   
    int i = 0;
    uint32_t ulReturn;
    /* 进入临界段,临界段可以嵌套 */
    ulReturn = taskENTER_CRITICAL_FROM_ISR();
   
    if(USART_GetITStatus(USART6, USART_IT_IDLE) != RESET)
    {
      //1.清除USART6接收完成中断
      USART6->SR;
      USART6->DR;   //清USART_IT_IDLE标志
      //2.存储收到的数据内容、长度、标志位
      DMA_Cmd(DMA2_Stream1,DISABLE); //使能数据流1 通道5
      
      DMA_ClearFlag(DMA2_Stream1, DMA_FLAG_TCIF1 | DMA_FLAG_FEIF1 | DMA_FLAG_DMEIF1 | DMA_FLAG_TEIF1 | DMA_FLAG_HTIF1);//清零标志位
   
      Uart6_RxBuffer.RxLen = UART6_RX_LEN - DMA_GetCurrDataCounter(DMA2_Stream1);      //计算接收数据包长度      
      
      for (i = 0;i <Uart6_RxBuffer.RxLen;i++)    //将接收到的数据包缓存
      {
            Uart6_RxBuffer.buffer = Uart6_Rx;               
      }       

      if(Uart6_RxBuffer.RxLen <= UART6_RX_LEN)
      {
            //printf(">>>>>>>>>>>>>>>>>>>>>>>>>>\r\n");
      }

      DMA_SetCurrDataCounter(DMA2_Stream1,UART6_RX_LEN);                      //设置传输数据长度
      DMA_Cmd(DMA2_Stream1,ENABLE);                                           //打开DMA
    }

    if(USART_GetITStatus(USART6, USART_IT_TC) != RESET)
    {
      USART_ClearFlag(USART6,USART_IT_TC);
      
      //关闭发送完成中断
      USART_ITConfig(USART6,USART_IT_TC,DISABLE);
      
      RS485_U6_RX_EN();
    }

/* 退出临界段 */
taskEXIT_CRITICAL_FROM_ISR( ulReturn );          

}

//USART1接收DMA配置
static void BSP_DMAUsar6Rx_Init(void)
{
    DMA_InitTypeDef   DMA_InitStructure;
    u16 mid_u16RetryCnt = 0;
   
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);                        //启动DMA时钟
   
    DMA_DeInit(DMA2_Stream1);
    while ((DMA_GetCmdStatus(DMA2_Stream1) != DISABLE) && (mid_u16RetryCnt++ < 500));
   
    DMA_InitStructure.DMA_Channel = DMA_Channel_5;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART6->DR);         //外设地址      
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)Uart6_Rx;               //内存地址      
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;                     //dma传输方向单向      
    DMA_InitStructure.DMA_BufferSize = UART6_RX_LEN;                            //设置DMA在传输时缓冲区的长度   
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;            //设置DMA的外设递增模式,一个外设      
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                     //设置DMA的内存递增模式      
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;   //外设数据字长         
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;             //内存数据字长      
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                               //设置DMA的传输模式      
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                     //设置DMA的优先级别   
   
        DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
        DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
        DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
        DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
   
    DMA_Init(DMA2_Stream1,&DMA_InitStructure);          
    DMA_Cmd(DMA2_Stream1,ENABLE);                                             //使能数据流1 通道5
}

//USART6发送DMA配置
static void BSP_DMAUsar6Tx_Init(void)
{
    DMA_InitTypeDef   DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    u16 mid_u16RetryCnt = 0;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);                        //启动DMA时钟
   
    DMA_DeInit(DMA2_Stream6);
    while ((DMA_GetCmdStatus(DMA2_Stream6) != DISABLE) && (mid_u16RetryCnt++ < 500));
   
    DMA_InitStructure.DMA_Channel = DMA_Channel_5;
        DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART6->DR);         //DMA外设基地址
        DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)Uart6_Tx;               //DMA内存基地址
        DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                     //数据传输方向,从内存读取发送到外设
        DMA_InitStructure.DMA_BufferSize = UART6_TX_LEN;                            //DMA通道的DMA缓存的大小
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;            //外设地址寄存器不变
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                     //内存地址寄存器递增
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;   //数据宽度为8位
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;             //数据宽度为8位
        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                               //工作在正常模式
        DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                     //DMA通道 x拥有中优先级
   
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
        DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
        DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
        DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;   
       


    //DMA发送中断设置
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream6_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    DMA_ITConfig(DMA2_Stream6,DMA_IT_TC,ENABLE);   
   
        DMA_Init(DMA2_Stream6, &DMA_InitStructure);//根据DMA_InitStruct中指定的参数初始化DMA的通道USART6_Tx_DMA_Channel所标识的寄存器   
    DMA_Cmd(DMA2_Stream6,DISABLE);
}
//USART2 DMA发送指定长度的数据
//str:要发送的数据首地址
//cndtr:数据传输量
void BSP_DMAUsart6Puts(unsigned char *str,u8 cndtr)
{
    u16 l_u16RetryCnt = 0;   
    RS485_U6_TX_EN();
    memcpy(Uart6_Tx, str, cndtr);
    DMA_Cmd(DMA2_Stream6, DISABLE);                      //关闭DMA传输         
    while ((DMA_GetCmdStatus(DMA2_Stream6) != DISABLE) && (l_u16RetryCnt++ < 500));        //等待DMA可配置       
    DMA_SetCurrDataCounter(DMA2_Stream6, cndtr);//数据传输量        
    DMA_Cmd(DMA2_Stream6, ENABLE);                              //开启DMA传输           
}

//DMA2中断函数。
void DMA2_Stream6_IRQHandler(void)
{

    if(DMA_GetFlagStatus(DMA2_Stream6, DMA_FLAG_TCIF6) != RESET)        //DMA发送完成
    {   
                DMA_ClearFlag(DMA2_Stream6, DMA_FLAG_TCIF6 | DMA_FLAG_FEIF6 |
                                          DMA_FLAG_DMEIF6 | DMA_FLAG_TEIF6 | DMA_FLAG_HTIF6);         //清除标志位                       
               
                while(!USART_GetFlagStatus(USART6, USART_FLAG_TC));        //等待USART1发送完成标志TC置1
                USART_ClearFlag(USART6, USART_FLAG_TC);         //清除发送完成标志

                DMA_Cmd(DMA2_Stream6,DISABLE);
      USART_ITConfig(USART6,USART_IT_TC,ENABLE);               
                RS485_U6_RX_EN();                //切换为RS485接收模式               
    }

}





dghwjh 发表于 2020-6-5 16:24:36

从机回复37个字节    1/38400x370(bit) = 9.6ms   主机5ms 下发5个字节,这个方案是不行的
提高波特率   或者更改主机下发命令间隔时间   再或者 改为 RS422 全双工   

surge 发表于 2020-6-5 16:32:23

dghwjh 发表于 2020-6-5 16:24
从机回复37个字节    1/38400x370(bit) = 9.6ms   主机5ms 下发5个字节,这个方案是不行的
提高波特率 ...

上位机是电梯,电梯公司不会理我的,波特率,时间都定死的,顶天我这边改为全双工的RS485.

wdliming 发表于 2020-6-5 17:29:02

surge 发表于 2020-6-5 16:32
上位机是电梯,电梯公司不会理我的,波特率,时间都定死的,顶天我这边改为全双工的RS485.

就这个霸王公司有点恶心

surge 发表于 2020-6-6 08:22:56

雷鹏 发表于 2020-6-5 16:03
加根地线试试。

我本地加地线没用吧?上位机那边只预留了两条线。

h_007 发表于 2020-6-6 20:13:26

建议先用纯中断处理,如成功,再用DMA

落叶凋零 发表于 2020-6-7 13:45:37

上位机5ms间隔下发,你必须在5ms内处理完数据并且发送完毕,不然5ms一到上位机又开始下发了,你还没有上传完成,那么肯定会出问题的。38400传输37个字节,8N1的情况下,37*9/38400=需要9ms,所以我觉得就是设计有问题,现在的情况下你下位机怎么改都有问题。

yjwpm 发表于 2020-6-8 10:15:07

落叶凋零 发表于 2020-6-7 13:45
上位机5ms间隔下发,你必须在5ms内处理完数据并且发送完毕,不然5ms一到上位机又开始下发了,你还没有上传 ...

485的硬件接口部分没有看到,不好说具体是哪里的问题,我这边测试过1M传输都没有问题

caicaptain2 发表于 2020-6-8 11:32:51

本帖最后由 caicaptain2 于 2020-6-8 11:34 编辑

yjwpm 发表于 2020-6-8 10:15
485的硬件接口部分没有看到,不好说具体是哪里的问题,我这边测试过1M传输都没有问题
赞同。。。几百米的距离, 485传输1Mb都是没有问题的。 记得用双绞线。

另外,你这个时间要求也是应该没有问题的。 记得设定modbus任务的优先级高一点。1ms的响应速度,对于F4来说,很容易的。
建议,首先用示波器观察波形,确定硬件方便的问题。 传输时间,应该和理论一样才对。

surge 发表于 2020-6-14 08:05:43

感谢各位大佬的帮忙,我更改了指令解析那块的内容,把延时时间改为绝对延时,每2ms去读一下接收缓冲区,然后解析指令,电梯每5ms一次查询指令,在5ms内接收到第一个字节后,20ms后传完,就OK了。附上指令解析部分的代码
static uint8_t deal_Serial_Parse(void)
{
    uint8_t ch = 0;
   
    while(RS485_Recv(COM6,&ch,1))
    {
       switch (rxFromHost.rxStatus)
      {               
            case STEP1:
                if(0x5A == ch) /*接收包头*/
                {
                  rxFromHost.rxBuff = ch;
                  rxFromHost.rxCRC = ch;
                  rxFromHost.rxCnt = 1;
                  rxFromHost.rxStatus = STEP2;
                }

                break;
         case STEP2:
                if(0x01 == ch) //判定第二个字节是否是需要的字节,若多梯联动时需读取拨码开关的值
                {
                  rxFromHost.rxBuff = ch;
                  rxFromHost.rxCRC ^= ch;
                  rxFromHost.rxCnt = 2;
                  rxFromHost.rxStatus = STEP3;               
                }
                else
                {
               
                   memset(&rxFromHost,0x00,sizeof(rxFromHost));                  
                }
                break;         
            default:      /* 接收整个数据包 */
            
                rxFromHost.rxBuff = ch;
                rxFromHost.rxCRC ^= ch;
               
                if(rxFromHost.rxCnt >= 5)
                {
               
                  if(rxFromHost.rxCRC == 0)
                  {
                        memset(&rxFromHost,0x00,sizeof(rxFromHost));
                        return FINISHED;                        
                  }
                  memset(&rxFromHost,0x00,sizeof(rxFromHost));
                }
            
                break;
         }
         
    }   

    return UNFINISHED;
}

滴滴滴 发表于 2023-9-6 16:09:31

:victory:
页: [1]
查看完整版本: STM32F407 RS485高频传输而产生乱码的问题