使用RS485芯片进行串口通讯
创始人
2024-04-01 16:25:20
0

使用RS485芯片进行串口通讯

  • RS485 芯片说明
  • 实操
    • 发送程序
    • 接收程序
    • 总结

RS485 芯片说明

开发板使用的 RS485 芯片是 SP3485E 芯片,其引脚功能如下:

在这里插入图片描述
值得说明的点:

  1. $ \overline{RE} $ 和 DIDIDI 正好逻辑互补,这样的好处在于我们可以用一根引脚的高低电平就可以控制该芯片的收发使能;
  2. AAA 和 BBB 为极性互补的输出引脚;
  3. RORORO 为数据输入引脚连接到主控芯片,DIDIDI 为数据输出引脚连接到主控芯片;

实操

(安富莱)V7开发板 的RS485与芯片连接的原理图如下:在这里插入图片描述
如上图,我们可以知道,PB11 和 PB2 对应为 USART3 的接收和发送引脚,连接到了芯片的 RORORO 和 DIDIDI,对应数据的输出与接收。

控制芯片收发的引脚为 PB2 引脚,在程序中:

/* PB2 控制RS485芯片的发送使能 */
#define RS485_TXEN_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
#define RS485_TXEN_GPIO_PORT              GPIOB
#define RS485_TXEN_PIN                    GPIO_PIN_2#define RS485_RX_EN()	RS485_TXEN_GPIO_PORT->BSRRH = RS485_TXEN_PIN	// 设置GPIO输出为1
#define RS485_TX_EN()	RS485_TXEN_GPIO_PORT->BSRRL = RS485_TXEN_PIN	// 设置GPIO输出为0

我们通过置位 BSRR 寄存器,直接对 ODR 寄存器(output data register)进行操作。

发送程序

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

SendBefor() 是回调函数,该函数用于在发送前调用本质是调用 RS485_TX_EN() 开启 RS485 芯片的输出使能:

void RS485_SendBefor(void)
{RS485_TX_EN();	/* 切换RS485收发芯片为发送模式 */
}

UartSend(pUart, _ucaBuf, _usLen) 实际的发送:

static void UartSend(UART_T *_pUart, uint8_t *_ucaBuf, uint16_t _usLen)
{uint16_t i;for (i = 0; i < _usLen; i++){/* 如果发送缓冲区已经满了,则等待缓冲区空 */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[_pUart->usTxWrite] = _ucaBuf[i];DISABLE_INT();if (++_pUart->usTxWrite >= _pUart->usTxBufSize){_pUart->usTxWrite = 0;}_pUart->usTxCount++;ENABLE_INT();}SET_BIT(_pUart->uart->CR1, USART_CR1_TXEIE);	/* 使能发送中断(缓冲区空) */
}

该函数主要为第 5 行的 for 循环,会将要发送的数据,从用户传入的 _ucaBuf 缓存器拷贝到软件层定义的 USART 的缓冲器 _pUart->pTxBuf 中,如果缓存区已经满则需要等到缓存区先发送一部分数据,这时调用者会被阻塞。

最后一行,开启 TXE 中断,每当发送一个到 USART 的 USART_TDR 寄存器的数据被移动到发送移位寄存器中,就会触发一个 TXE 中断(表面 TDR 寄存器已经为空)。

详细 USART 操作逻辑参考:USART发送与接收_Bin Watson的博客-CSDN博客

于是就会触发 USART3_IRQHandler 中断处理函数被调用,该函数最终调用 UartIRQ() 函数:

static void UartIRQ(UART_T *_pUart)
{uint32_t isrflags   = READ_REG(_pUart->uart->ISR);uint32_t cr1its     = READ_REG(_pUart->uart->CR1);uint32_t cr3its     = READ_REG(_pUart->uart->CR3);.../* 处理发送缓冲区空中断 */if ( ((isrflags & USART_ISR_TXE) != RESET) && (cr1its & USART_CR1_TXEIE) != RESET){//if (_pUart->usTxRead == _pUart->usTxWrite)if (_pUart->usTxCount == 0){/* 发送缓冲区的数据已取完时, 禁止发送缓冲区空中断 (注意:此时最后1个数据还未真正发送完毕)*///USART_ITConfig(_pUart->uart, USART_IT_TXE, DISABLE);CLEAR_BIT(_pUart->uart->CR1, USART_CR1_TXEIE);/* 使能数据发送完毕中断 *///USART_ITConfig(_pUart->uart, USART_IT_TC, ENABLE);SET_BIT(_pUart->uart->CR1, USART_CR1_TCIE);}else{_pUart->Sending = 1;/* 从发送FIFO取1个字节写入串口发送数据寄存器 *///USART_SendData(_pUart->uart, _pUart->pTxBuf[_pUart->usTxRead]);_pUart->uart->TDR = _pUart->pTxBuf[_pUart->usTxRead];if (++_pUart->usTxRead >= _pUart->usTxBufSize){_pUart->usTxRead = 0;}_pUart->usTxCount--;}}...
}

第 12 行的 if 条件,当 usTxCount 为 0 时,表明发送已经完成,这时我们保持 TXE 为置 1 状态,然后开启 TC 中断。当发送移位寄存器发送出最后一个数据后,在判断 TXE 为1的情况下就会触发 TC 中断,于是乎又会进入 UartIRQ()

static void UartIRQ(UART_T *_pUart)
{uint32_t isrflags   = READ_REG(_pUart->uart->ISR);uint32_t cr1its     = READ_REG(_pUart->uart->CR1);uint32_t cr3its     = READ_REG(_pUart->uart->CR3);.../* 数据bit位全部发送完毕的中断 */if (((isrflags & USART_ISR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET)){//if (_pUart->usTxRead == _pUart->usTxWrite)if (_pUart->usTxCount == 0){/* 如果发送FIFO的数据全部发送完毕,禁止数据发送完毕中断 *///USART_ITConfig(_pUart->uart, USART_IT_TC, DISABLE);CLEAR_BIT(_pUart->uart->CR1, USART_CR1_TCIE);/* 回调函数, 一般用来处理RS485通信,将RS485芯片设置为接收模式,避免抢占总线 */if (_pUart->SendOver){_pUart->SendOver();}_pUart->Sending = 0;}else{/* 正常情况下,不会进入此分支 *//* 如果发送FIFO的数据还未完毕,则从发送FIFO取1个数据写入发送数据寄存器 *///USART_SendData(_pUart->uart, _pUart->pTxBuf[_pUart->usTxRead]);_pUart->uart->TDR = _pUart->pTxBuf[_pUart->usTxRead];if (++_pUart->usTxRead >= _pUart->usTxBufSize){_pUart->usTxRead = 0;}_pUart->usTxCount--;}}...
}

这时候我们执行的逻辑就是上面的代码。主要是第 21 行,会调用 SendOver 回调函数,该函数本质是调用 RS485_RX_EN()

void RS485_SendOver(void)
{RS485_RX_EN();	/* 切换RS485收发芯片为接收模式 */
}

于是乎我们重新将 RS485 置于接收状态。

接收程序

接收用中断来实现

static void UartIRQ(UART_T *_pUart)
{uint32_t isrflags   = READ_REG(_pUart->uart->ISR);uint32_t cr1its     = READ_REG(_pUart->uart->CR1);uint32_t cr3its     = READ_REG(_pUart->uart->CR3);/* 处理接收中断  */if ((isrflags & USART_ISR_RXNE) != RESET){/* 从串口接收数据寄存器读取数据存放到接收FIFO */uint8_t ch;ch = READ_REG(_pUart->uart->RDR);_pUart->pRxBuf[_pUart->usRxWrite] = 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); /* 比如,交给MODBUS解码程序处理字节流 */}}}...
}

接收程序比较简单,当 USART_RDR 寄存器接收到一个新数据时就会触发 RX 中断,最终还是调用 UartIRQ() 这个中断处理函数。

如第 13 行的操作,将 RDR 寄存器中的置读取到 ch 中然后存入到软件层面的 USART 接收缓存区 _pUart->pRxBuf 中。

第 30 行,ReciveNew() 这个回调函数用于配合一些协议的使用,如 ModBus 协议,我们需要在接收一个数据后进行一些处理操作就可以通过该回调函数进行实现。

总结

该程序源码来源于 安富莱V7开发板的第13个程序。

相关内容

热门资讯

银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...