/* 用于存储接收字节的缓冲区 - 大小必须是2的幂 /
static uint8_t Receive_Buffer_Data[512];
static FIFO_BUFFER Receive_Buffer;
/ 线上的静默时间 /
static struct mstimer Silence_Timer;
/ 波特率 */
static uint32_t Baud_Rate = 38400;
/* 在接收到的帧的最后一个八位的停止位结束后,节点可以启用其EIA-485驱动器之前的最短时间:40个位时间。/
/ 在9600波特率下,40个位时间约为4.166毫秒 /
/ 在19200波特率下,40个位时间约为2.083毫秒 /
/ 在38400波特率下,40个位时间约为1.041毫秒 /
/ 在57600波特率下,40个位时间约为0.694毫秒 /
/ 在76800波特率下,40个位时间约为0.520毫秒 /
/ 在115200波特率下,40个位时间约为0.347毫秒 /
/ 40位是包括每个八位的起始位和停止位的4个八位 */
#define Tturnaround (40UL)
/*************************************************************************
Description: 重置线上的静默时间计时器。
Returns: 无
Notes: 无
**************************************************************************/
void rs485_silence_reset(void)
{
mstimer_set(&Silence_Timer, 0);
}
/*************************************************************************
Description: 从计时器确定线上的静默时间。
Returns: 如果已经过了指定的时间间隔,则返回true
Notes: 无
**************************************************************************/
bool rs485_silence_elapsed(uint32_t interval)
{
return (mstimer_elapsed(&Silence_Timer) > interval);
}
/*************************************************************************
Description: 波特率决定了回转时间。
Returns: 毫秒数
Notes: 无
*************************************************************************/
static uint16_t rs485_turnaround_time(void)
{
/ 接收后传输之前的延迟 - 根据MS/TP规范 /
/ 等待至少40个位时间自接收以来 /
/ 至少2毫秒用于错误:四舍五入、时钟滴答 */
if (Baud_Rate) {
return (2 + ((Tturnaround * 1000UL) / Baud_Rate));
} else {
return 2;
}
}
/*************************************************************************
Description: 使用静默计时器确定回转时间。
Returns: 如果回转时间已过,则返回true。
Notes: 无
**************************************************************************/
bool rs485_turnaround_elapsed(void)
{
return (mstimer_elapsed(&Silence_Timer) > rs485_turnaround_time());
}
/*************************************************************************
Description: 确定接收时是否发生错误。
Returns: 如果发生错误,则返回true。
Notes: 无
**************************************************************************/
bool rs485_receive_error(void)
{
return false;
}
/*******************************************************************/
/
@brief
*USARTx
*中断
*处理程序
*子例程
@param[in]
*无
@return
*无
**********************************************************************/
void USART2_IRQHandler(void)
{
uint8_t data_byte;
if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) {
/* 从接收数据寄存器中读取一个字节 */
data_byte = USART_ReceiveData(USART2);
(void)FIFO_Put(&Receive_Buffer, data_byte);
}
}
/*************************************************************************
DESCRIPTION: 返回是否有可用的字节。
RETURN: 如果有可用的字节,则返回true,并将字节存储在参数中。
NOTES: 无
**************************************************************************/
bool rs485_byte_available(uint8_t data_register)
{
bool data_available = false; / 返回值 */
if (!FIFO_Empty(&Receive_Buffer)) {
if (data_register) {
*data_register = FIFO_Get(&Receive_Buffer);
}
rs485_silence_reset();
data_available = true;
led_rx_on_interval(10);
}
return data_available;
}
/*************************************************************************
DESCRIPTION: 发送一个字节的数据。
RETURN: 无
NOTES: 无
**************************************************************************/
void rs485_byte_send(uint8_t tx_byte)
{
led_tx_on_interval(10);
USART_SendData(USART2, tx_byte);
rs485_silence_reset();
}
/*************************************************************************
Description: 确定USART中是否有一个字节从寄存器中移出。
Returns: 如果USART寄存器为空,则返回true。
Notes: 无
**************************************************************************/
bool rs485_byte_sent(void)
{
return USART_GetFlagStatus(USART2, USART_FLAG_TXE);
}
/*************************************************************************
Description: 确定整个帧是否从USART FIFO中发送完毕。
Returns: 如果USART FIFO为空,则返回true。
Notes: 无
**************************************************************************/
bool rs485_frame_sent(void)
{
return USART_GetFlagStatus(USART2, USART_FLAG_TC);
}
这段代码是一个用于实现RS-485通信的功能。它包含了一些函数和变量,用于接收和发送数据,并处理通信中的一些特殊情况。
首先,代码定义了一个用于存储接收到的字节的缓冲区(Receive_Buffer_Data)和一个FIFO缓冲区(Receive_Buffer)来管理接收缓冲区。
接下来,代码定义了一个用于计时静默时间的计时器(Silence_Timer)和一个用于存储波特率的变量(Baud_Rate)。
代码中的rs485_silence_reset函数用于重置静默时间计时器。
rs485_silence_elapsed函数用于判断静默时间是否已过去了指定的时间间隔。
rs485_turnaround_time函数根据波特率确定回转时间。
接下来,代码包含了一个USART2_IRQHandler函数,用于处理USART2的接收中断。当接收到数据时,它将数据存储到接收缓冲区中。
rs485_byte_available函数用于检查是否有可用的字节,并将字节存储在参数data_register中。
rs485_byte_send函数用于发送一个字节的数据。
rs485_byte_sent函数用于判断USART寄存器是否为空,即判断上一次发送的字节是否已经移出寄存器。
rs485_frame_sent函数用于判断整个帧是否已经发送完毕。
这些函数和变量的目的是实现RS-485通信的基本功能,包括接收和发送数据,并处理通信时的一些特殊情况,如静默时间和回转时间。
/*************************************************************************
DESCRIPTION: 发送一些数据并等待其发送完毕
RETURN: 如果发生冲突或超时,则返回true
NOTES: 无
**************************************************************************/
void rs485_bytes_send(uint8_t buffer, / 要发送的数据 /
uint16_t nbytes)
{ / 数据字节数 */
uint8_t tx_byte;
while (nbytes) {
/* 发送数据字节 */
tx_byte = buffer;
/ 发送一个字节 /
USART_SendData(USART2, tx_byte);
while (!rs485_byte_sent()) {
/ 什么也不做 - 等待直到Tx缓冲区为空 /
}
buffer++;
nbytes--;
}
/ 帧已发送? /
while (!rs485_frame_sent()) {
/ 什么也不做 - 等待直到整个帧在传输移位寄存器中移出 */
}
rs485_silence_reset();
return;
}
/*************************************************************************
Description: 配置USART的波特率
Returns: 无
Notes: 无
**************************************************************************/
static void rs485_baud_rate_configure(void)
{
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = Baud_Rate;
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;
/* 配置USARTx */
USART_Init(USART2, &USART_InitStructure);
}
rs485_bytes_send函数的参数buffer是一个指向要发送数据的缓冲区的指针。该缓冲区存储了要发送的数据字节序列。
nbytes参数是要发送的数据字节数。它指示了要发送的数据的长度。
在调用rs485_bytes_send函数时,需要将要发送的数据存储在buffer指向的缓冲区中,并将要发送的数据字节数作为nbytes参数传递给函数。
需要注意的是,调用rs485_bytes_send函数时,确保提供的缓冲区大小足够存储要发送的数据,并且nbytes参数的值与要发送的数据字节数相匹配,以避免访问越界和发送不完整的数据。
/*************************************************************************
Description: 将波特率设置为非易失存储,并配置USART
Returns: 如果保存了波特率值,则返回true
Notes: 无
**************************************************************************/
bool rs485_baud_rate_set(uint32_t baud)
{
bool valid = true;
switch (baud) {
case 9600:
case 19200:
case 38400:
case 57600:
case 76800:
case 115200:
Baud_Rate = baud;
rs485_baud_rate_configure();
break;
default:
valid = false;
break;
}
return valid;
}
/*************************************************************************
Description: 确定波特率(以bps为单位)
Returns: 波特率(以bps为单位)
Notes: 无
**************************************************************************/
uint32_t rs485_baud_rate(void)
{
return Baud_Rate;
}
/*************************************************************************
Description: 启用请求发送(RTS)即发送使能引脚
Returns: 无
Notes: 无
**************************************************************************/
void rs485_rts_enable(bool enable)
{
if (enable) {
GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
} else {
GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
}
}
/*************************************************************************
Description: 初始化USART
Returns: 无
Notes: 无
**************************************************************************/
void rs485_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
/* 配置USARTx的接收引脚为浮空输入 /
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/ 配置USARTx的发送引脚为复用推挽输出 /
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/ 配置请求发送(RTS)即发送使能引脚 /
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/ 使能USARTx时钟 /
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
/RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);/
/ 使能GPIO时钟 /
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/ 为这对引脚和外设功能启用USART引脚软件重映射:
USART3全重映射(TX/PD8,RX/PD9,CK/PD10,CTS/PD11,RTS/PD12)/
/GPIO_PinRemapConfig(GPIO_FullRemap_USART3, ENABLE);/
/ 配置NVIC的抢占优先级位数 /
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
/ 使能USARTx中断 /
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/ 使能USART生成中断 */
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
rs485_baud_rate_set(Baud_Rate);
USART_Cmd(USART2, ENABLE);
FIFO_Init(&Receive_Buffer, &Receive_Buffer_Data[0],
(unsigned)sizeof(Receive_Buffer_Data));
rs485_silence_reset();
}
首先,在调用rs485_init函数之前,确保已经正确配置了USART2的引脚和时钟。
调用rs485_init函数将初始化USART2和相关的GPIO引脚。
rs485_init函数中的rs485_baud_rate_set函数用于配置USART2的波特率。可以使用rs485_baud_rate_set函数来设置所需的波特率。默认情况下,波特率被设置为Baud_Rate变量的值。
USART_ITConfig函数用于使能USART2接收中断。这将允许在接收到数据时触发中断。
FIFO_Init函数用于初始化接收缓冲区。接收缓冲区是一个循环队列,用于存储接收到的数据。
rs485_silence_reset函数用于重置沉默定时器。
一旦完成了rs485_init函数的调用,USART2就已经准备好进行通信了。您可以使用USART_SendData函数发送数据,并使用USART_ReceiveData函数接收数据。
请注意,为了使USART2正常工作,您还需要根据具体的应用场景进行适当的配置,例如使能中断处理函数、处理接收到的数据等。