/*
 * 模块名称：RS-485 半双工通信驱动
 * 模块功能：RS-485 半双工通信驱动，封装任意 UART 外设实现 485 方向控制，
 *          提供阻塞发送、中断接收（IDLE 空闲帧检测）、DMA 发送等功能。
 *          参考 ST AN3070 应用笔记《IO-Link master: USART communication》
 *          及 controllerstech.com RS485 教程
 * 适用平台：STM32F4 系列
 * 作者：王建锋
 * 创建日期：2026-07-18
 * 修改记录：
 *     2026-07-18  王建锋  创建初始版本
 */

#ifndef __RS485_H
#define __RS485_H

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"

/*
 * 常量定义
 */
#define RS485_TIMEOUT_DEFAULT  1000U   /* 默认发送超时（ms） */

/*
 * RS-485 句柄结构体
 * 将任意 UART + 任意 GPIO 组合绑定为一个 RS-485 半双工通信实例
 */
typedef struct {
    UART_HandleTypeDef *huart;      /* UART 外设句柄 */
    GPIO_TypeDef      *dir_port;   /* 方向控制 GPIO 端口 */
    uint16_t           dir_pin;    /* 方向控制 GPIO 引脚号 */
    volatile uint16_t  rx_size;    /* 最近一次接收的字节数 */
} rs485_handle_t;

/*
 * 公共函数声明
 */

/*
 * 函数功能：初始化 RS-485 句柄，绑定 UART 和方向控制引脚
 * 入口参数：handle  - RS-485 句柄指针
 *           huart   - UART 外设句柄指针（已由 CubeMX 初始化）
 *           dir_port - 方向控制 GPIO 端口（如 GPIOD）
 *           dir_pin  - 方向控制 GPIO 引脚号（如 GPIO_PIN_0）
 * 返回值：无
 * 限定条件：UART 和 GPIO 须先由 CubeMX 完成初始化
 * 函数说明：初始化后默认处于接收状态（DE 引脚拉低）
 */
void rs485_init(rs485_handle_t *p_handle,
                UART_HandleTypeDef *p_huart,
                GPIO_TypeDef *p_dir_port,
                uint16_t dir_pin);

/*
 * 函数功能：阻塞方式发送数据
 * 入口参数：handle  - RS-485 句柄指针
 *           data    - 待发送数据缓冲区
 *           len     - 待发送字节数
 *           timeout - 发送超时（ms），传 0 使用 RS485_TIMEOUT_DEFAULT
 * 返回值：HAL_OK / HAL_TIMEOUT / HAL_ERROR
 * 限定条件：在主循环或任务中调用，不可在 UART 中断中调用
 * 函数说明：发送前自动拉高 DE，发送完成后等待 TC 标志再拉低 DE，
 *          确保最后一字节完全移出后再切换到接收状态
 */
HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
                                  const uint8_t *p_data,
                                  uint16_t len,
                                  uint32_t timeout);

/*
 * 函数功能：启动中断方式接收（IDLE 空闲帧检测）
 * 入口参数：handle    - RS-485 句柄指针
 *           buf       - 接收缓冲区
 *           buf_size  - 缓冲区大小
 * 返回值：HAL_OK / HAL_ERROR
 * 限定条件：UART 须已开启全局中断，NVIC 中须使能对应 UART 中断
 * 函数说明：使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收，
 *          收到完整帧后在 RxEventCallback 中通知用户。
 *          用户须在回调中重新调用本函数重新开启接收
 */
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle,
                                       uint8_t *p_buf,
                                       uint16_t buf_size);

/*
 * 函数功能：将 RS-485 句柄与 HAL 回调关联
 * 入口参数：handle - RS-485 句柄指针
 * 返回值：无
 * 限定条件：须在 HAL_UART_RxCpltCallback / HAL_UARTEx_RxEventCallback 中调用
 * 函数说明：将 HAL 回调中接收到的数据大小回写到句柄的 rx_size 字段
 */
void rs485_rx_set_size(rs485_handle_t *p_handle, uint16_t size);

/*
 * 函数功能：获取最近一次接收的字节数
 * 入口参数：handle - RS-485 句柄指针
 * 返回值：最近一次接收的字节数
 * 限定条件：须在 HAL_UARTEx_RxEventCallback 触发后调用
 */
uint16_t rs485_rx_get_size(const rs485_handle_t *p_handle);

#ifdef __cplusplus
}
#endif

#endif /* __RS485_H */
