Files

133 lines
4.7 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "rs485.h"
#include "gd32f4xx_usart.h"
#include "gd32f4xx_rcu.h"
#include "gd32f4xx_gpio.h"
#include "board_config.h"
/* 独立接收环形缓冲区(与 debug 串口的 uart_ring_buffer 隔离),
采用"预留一格"区分空/满,最大可用容量为 RS485_RX_BUFFER_SIZE-1 */
static __IO uint8_t rs485_rx_buffer[RS485_RX_BUFFER_SIZE];
static __IO uint8_t rs485_write_index = 0;
static __IO uint8_t rs485_read_index = 0;
static inline uint32_t rs485_irq_save(void) {
uint32_t primask = __get_PRIMASK();
__disable_irq();
return primask;
}
static inline void rs485_irq_restore(uint32_t primask) {
if ((primask & 0x1) == 0) {
__enable_irq();
}
}
/**
* @brief 配置 RS485 收发引脚(PA9 TX / PA10 RX 复用,DE PA8 推挽输出)。
*/
static void rs485_gpio_config(void) {
/* 使能 GPIOA 和 USART0 时钟 */
rcu_periph_clock_enable(RS485_GPIO_RCU);
rcu_periph_clock_enable(RS485_RCU);
/* PA9 为 USART0_TX,PA10 为 USART0_RX(AF7) */
gpio_af_set(RS485_GPIO_PORT, GPIO_AF_7, RS485_TX_PIN | RS485_RX_PIN);
gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, RS485_TX_PIN | RS485_RX_PIN);
gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_TX_PIN | RS485_RX_PIN);
/* DE 引脚为普通推挽输出,默认接收态 */
rcu_periph_clock_enable(RS485_DE_RCU);
gpio_mode_set(RS485_DE_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_DE_PIN);
gpio_output_options_set(RS485_DE_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_DE_PIN);
gpio_bit_reset(RS485_DE_PORT, RS485_DE_PIN);
}
void rs485_init(void) {
rs485_gpio_config();
/* 配置波特率、数据位、停止位等 */
usart_deinit(RS485_PHY);
usart_word_length_set(RS485_PHY, USART_WL_8BIT);
usart_stop_bit_set(RS485_PHY, USART_STB_1BIT);
usart_parity_config(RS485_PHY, USART_PM_NONE);
usart_baudrate_set(RS485_PHY, RS485_BAUDRATE);
usart_receive_config(RS485_PHY, USART_RECEIVE_ENABLE);
usart_transmit_config(RS485_PHY, USART_TRANSMIT_ENABLE);
usart_enable(RS485_PHY);
/* 使能接收非空中断 */
nvic_irq_enable(RS485_IRQ, 0, 0);
usart_interrupt_enable(RS485_PHY, USART_INT_RBNE);
}
void rs485_send(const uint8_t *data, uint16_t len) {
/* 半双工防冲突:发送期间暂停 RX 中断(若收发器 RE 独立使能,
自己发出的字节会回环进 USART,既污染接收缓冲也可能与本方解析交错) */
usart_interrupt_disable(RS485_PHY, USART_INT_RBNE);
/* 进入发送态 */
gpio_bit_set(RS485_DE_PORT, RS485_DE_PIN);
for (uint16_t i = 0; i < len; i++) {
/* 等待发送缓冲区空 */
while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TBE)) {}
usart_data_transmit(RS485_PHY, data[i]);
}
/* 等待最后一字节移位完成,再切回接收态(避免截断尾字节) */
while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TC)) {}
/* 回到接收态 */
gpio_bit_reset(RS485_DE_PORT, RS485_DE_PIN);
/* 读一次 DATA 丢弃发送窗口内挤入接收通道的残留字节(清 RBNE/ORE)。
注意:不能清接收环形缓冲——缓冲里可能有"已收到待处理"的用户帧字节。
发送期间的回环/杂散字节由协议状态机按非包头丢弃。 */
(void)usart_data_receive(RS485_PHY);
/* 恢复接收 */
usart_interrupt_enable(RS485_PHY, USART_INT_RBNE);
}
uint8_t rs485_rx_available(void) {
uint8_t w = rs485_write_index;
uint8_t r = rs485_read_index;
return (uint8_t)((w + RS485_RX_BUFFER_SIZE - r) % RS485_RX_BUFFER_SIZE);
}
int rs485_rx_get(void) {
if (rs485_read_index == rs485_write_index) return -1; /* 空 */
uint8_t data = rs485_rx_buffer[rs485_read_index];
rs485_read_index = (rs485_read_index + 1) % RS485_RX_BUFFER_SIZE;
return data;
}
void rs485_rx_clear(void) {
uint32_t primask = rs485_irq_save();
rs485_write_index = 0;
rs485_read_index = 0;
rs485_irq_restore(primask);
}
void rs485_irq_handler(void) {
/* 处理USART0的接收中断 */
if (usart_interrupt_flag_get(RS485_PHY, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(RS485_PHY);
uint32_t primask = rs485_irq_save();
uint8_t next = (rs485_write_index + 1) % RS485_RX_BUFFER_SIZE;
if (next != rs485_read_index) {
/* 缓冲区未满 */
rs485_rx_buffer[rs485_write_index] = data;
rs485_write_index = next;
}
/* 缓冲满时丢弃 */
rs485_irq_restore(primask);
}
/* 处理USART0的空闲中断(帧间隔提示,后续协议解析用) */
if (usart_interrupt_flag_get(RS485_PHY, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(RS485_PHY, USART_INT_FLAG_IDLE);
}
}