打印
[应用相关]

stm32采用队列方式接收和发送RS485串口数据

[复制链接]
2650|51
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
漫天星yl|  楼主 | 2024-3-31 22:48 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
stm32采用队列方式接收和发送RS485串口数据
/* 用于存储接收字节的缓冲区 - 大小必须是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);
}


使用特权

评论回复
沙发
漫天星yl|  楼主 | 2024-3-31 22:48 | 只看该作者
这段代码是一个用于实现RS-485通信的功能。它包含了一些函数和变量,用于接收和发送数据,并处理通信中的一些特殊情况。

首先,代码定义了一个用于存储接收到的字节的缓冲区(Receive_Buffer_Data)和一个FIFO缓冲区(Receive_Buffer)来管理接收缓冲区。

接下来,代码定义了一个用于计时静默时间的计时器(Silence_Timer)和一个用于存储波特率的变量(Baud_Rate)。

代码中的rs485_silence_reset函数用于重置静默时间计时器。

rs485_silence_elapsed函数用于判断静默时间是否已过去了指定的时间间隔。

rs485_turnaround_time函数根据波特率确定回转时间。

接下来,代码包含了一个USART2_IRQHandler函数,用于处理USART2的接收中断。当接收到数据时,它将数据存储到接收缓冲区中。

使用特权

评论回复
板凳
漫天星yl|  楼主 | 2024-3-31 22:49 | 只看该作者
rs485_byte_available函数用于检查是否有可用的字节,并将字节存储在参数data_register中。

rs485_byte_send函数用于发送一个字节的数据。

rs485_byte_sent函数用于判断USART寄存器是否为空,即判断上一次发送的字节是否已经移出寄存器。

rs485_frame_sent函数用于判断整个帧是否已经发送完毕。

这些函数和变量的目的是实现RS-485通信的基本功能,包括接收和发送数据,并处理通信时的一些特殊情况,如静默时间和回转时间。

使用特权

评论回复
地板
漫天星yl|  楼主 | 2024-3-31 22:49 | 只看该作者
/*************************************************************************

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);
}

使用特权

评论回复
5
漫天星yl|  楼主 | 2024-3-31 22:49 | 只看该作者
rs485_bytes_send函数的参数buffer是一个指向要发送数据的缓冲区的指针。该缓冲区存储了要发送的数据字节序列。

nbytes参数是要发送的数据字节数。它指示了要发送的数据的长度。

在调用rs485_bytes_send函数时,需要将要发送的数据存储在buffer指向的缓冲区中,并将要发送的数据字节数作为nbytes参数传递给函数。

需要注意的是,调用rs485_bytes_send函数时,确保提供的缓冲区大小足够存储要发送的数据,并且nbytes参数的值与要发送的数据字节数相匹配,以避免访问越界和发送不完整的数据。

使用特权

评论回复
6
漫天星yl|  楼主 | 2024-3-31 22:49 | 只看该作者
/*************************************************************************

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();
}

使用特权

评论回复
7
漫天星yl|  楼主 | 2024-3-31 22:49 | 只看该作者
首先,在调用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正常工作,您还需要根据具体的应用场景进行适当的配置,例如使能中断处理函数、处理接收到的数据等。

使用特权

评论回复
8
jkl21| | 2024-4-5 21:41 | 只看该作者
在处理接收和发送队列中的数据时,需要根据具体应用需求进行相应的数据处理。例如,可以对接收到的数据进行解析和校验,对发送的数据进行封装和加密等。

使用特权

评论回复
9
bestwell| | 2024-4-6 16:42 | 只看该作者
为队列分配适当的内存空间,并设置正确的队列参数,如最大容量、头部和尾部指针等。

使用特权

评论回复
10
sesefadou| | 2024-4-7 12:31 | 只看该作者
如果同时使用多个中断,需要合理设置中断优先级,以确保RS485接收中断能够及时得到处理,不会因为其他低优先级中断而被延迟。

使用特权

评论回复
11
sdlls| | 2024-4-7 15:21 | 只看该作者
在队列方式下,需要设置适当的中断处理函数。

使用特权

评论回复
12
LEDyyds| | 2024-4-7 16:58 | 只看该作者
用队列有啥好处吗

使用特权

评论回复
13
macpherson| | 2024-4-7 18:25 | 只看该作者
RS485通信通常在中断模式下进行,因此需要妥善管理串口中断,确保在中断服务程序中正确处理接收到的数据,并将数据推入队列。

使用特权

评论回复
14
robincotton| | 2024-4-7 20:44 | 只看该作者
在软件设计方面,需要实现一个队列系统来管理接收和发送的数据。这涉及到队列的数据结构设计,确保数据的先进先出(FIFO)顺序,以及处理可能的队列溢出或下溢情况。

使用特权

评论回复
15
eefas| | 2024-4-7 22:57 | 只看该作者
RS485通信缓冲区分配足够大的内存,以容纳最大可能的传输数据量。队列的大小应该根据预期的数据流量和传输速率来确定。

使用特权

评论回复
16
EmmaTT| | 2024-4-8 15:25 | 只看该作者
队列有什么用啊

使用特权

评论回复
17
sdlls| | 2024-4-9 10:02 | 只看该作者
可以设置串口数据接收完成的中断处理函数,以便于将接收到的数据放入队列中。

使用特权

评论回复
18
mollylawrence| | 2024-4-9 14:52 | 只看该作者
在向队列中添加数据前,需要检查队列是否已满。同样,在从队列中取出数据时,也需要检查队列是否为空。

使用特权

评论回复
19
louliana| | 2024-4-9 17:03 | 只看该作者
设计合理的通信协议,包括帧头、地址码、命令码、数据段和校验码等

使用特权

评论回复
20
robincotton| | 2024-4-9 20:09 | 只看该作者
在RS485串口通信中,需要设计合适的数据包格式,以便于区分有效数据和无效数据。数据包格式通常包括起始位、数据位、校验位和停止位。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

30

主题

346

帖子

0

粉丝