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高频传输的经验吗?或者对这种现象,有什么建议?感谢!!!
另外,在这个贴子 http://armbbs.cn/forum.php?mod=viewthread&tid=82149&extra=page%3D1 ,硬汉哥有帮忙看,但是小弟一直没有搞定。。。惭愧。 方便的话,画个框图。 485通讯加GND了没? 雷鹏 发表于 2020-6-5 15:19
485通讯加GND了没?
485通讯没有接地。 附件关于处理串口的代码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++)
{
;
}
}
简单计算一下,38400波特率,一般单字节需要9个位,那么1ms可以发送4.2个字节。 那么指令5个字节,耗时约1.2ms;37个回答字节耗时约8.7ms。
所以客户要求有点紧张,但是可以达到。 所以,你的F407查询间隔必须小于5ms,采用串口DMA发送。 接收也使用DMA+串口空闲中断即可。
surge 发表于 2020-6-5 15:29
485通讯没有接地。
加根地线试试。 波特率38400上位机下发5字节,到STM32F407需要3MS,我怎么觉得你这句话有问题5个字节无校验位50个bit 1/38400x50(bit)大约1.3ms 有点矛盾啊! 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接收模式
}
}
从机回复37个字节 1/38400x370(bit) = 9.6ms 主机5ms 下发5个字节,这个方案是不行的
提高波特率 或者更改主机下发命令间隔时间 再或者 改为 RS422 全双工 dghwjh 发表于 2020-6-5 16:24
从机回复37个字节 1/38400x370(bit) = 9.6ms 主机5ms 下发5个字节,这个方案是不行的
提高波特率 ...
上位机是电梯,电梯公司不会理我的,波特率,时间都定死的,顶天我这边改为全双工的RS485. surge 发表于 2020-6-5 16:32
上位机是电梯,电梯公司不会理我的,波特率,时间都定死的,顶天我这边改为全双工的RS485.
就这个霸王公司有点恶心 雷鹏 发表于 2020-6-5 16:03
加根地线试试。
我本地加地线没用吧?上位机那边只预留了两条线。 建议先用纯中断处理,如成功,再用DMA 上位机5ms间隔下发,你必须在5ms内处理完数据并且发送完毕,不然5ms一到上位机又开始下发了,你还没有上传完成,那么肯定会出问题的。38400传输37个字节,8N1的情况下,37*9/38400=需要9ms,所以我觉得就是设计有问题,现在的情况下你下位机怎么改都有问题。 落叶凋零 发表于 2020-6-7 13:45
上位机5ms间隔下发,你必须在5ms内处理完数据并且发送完毕,不然5ms一到上位机又开始下发了,你还没有上传 ...
485的硬件接口部分没有看到,不好说具体是哪里的问题,我这边测试过1M传输都没有问题 本帖最后由 caicaptain2 于 2020-6-8 11:34 编辑
yjwpm 发表于 2020-6-8 10:15
485的硬件接口部分没有看到,不好说具体是哪里的问题,我这边测试过1M传输都没有问题
赞同。。。几百米的距离, 485传输1Mb都是没有问题的。 记得用双绞线。
另外,你这个时间要求也是应该没有问题的。 记得设定modbus任务的优先级高一点。1ms的响应速度,对于F4来说,很容易的。
建议,首先用示波器观察波形,确定硬件方便的问题。 传输时间,应该和理论一样才对。
感谢各位大佬的帮忙,我更改了指令解析那块的内容,把延时时间改为绝对延时,每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;
}
:victory:
页:
[1]