rs485收发数据:
在上一篇中完成:
重定向printf到USART2
在main.c
中添加以下代码,将printf
重定向到USART2:
/* USER CODE BEGIN Includes */
#include <stdio.h> // 添加标准输入输出库
/* USER CODE END Includes */
/* USER CODE BEGIN 0 */
// 重定向printf到USART2
int __io_putchar(int ch) {
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
// 可选:如果需要scanf,重定向输入
int __io_getchar(void) {
uint8_t ch;
HAL_UART_Receive(&huart2, &ch, 1, HAL_MAX_DELAY);
return ch;
}
/* USER CODE END 0 */
封装带方向控制的printf函数
由于485需要手动控制方向引脚,需要封装一个安全的printf
函数:
/* USER CODE BEGIN PFP */
// 封装安全的485打印函数
void RS485_Printf(const char *format, ...) {
va_list args;
va_start(args, format);
// 临时缓冲区(根据需求调整大小)
char buffer[128];
int len = vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
if (len > 0) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // 发送模式
HAL_UART_Transmit(&huart2, (uint8_t *)buffer, len, HAL_MAX_DELAY);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // 接收模式
}
}
/* USER CODE END PFP */
使用示例
int main(void) {
// 初始化代码...
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // 初始为接收模式
while (1) {
RS485_Printf("Temperature: %.1f\r\n", 25.5); // 自定义封装函数
// 或者直接使用printf(需确保方向控制已处理)
printf("Hello 485!\r\n"); // 需配合重定向
HAL_Delay(1000);
}
}
关键注意事项
-
方向控制时序
每次调用printf
或HAL_UART_Transmit
前,必须切换为发送模式(CON=低电平),发送完成后立即切回接收模式(CON=高电平)。 -
缓冲区大小
RS485_Printf
中的临时缓冲区(如char buffer[128]
)需根据实际数据长度调整,避免溢出。 -
中断安全
如果在中断中使用printf
,需确保方向控制不会被其他中断打断。 -
格式化支持
vsnprintf
支持完整的printf
格式化(如%f
、%d
),但需在工程选项中启用浮点数支持:Project > Properties > C/C++ Build > Settings > Tool Settings > MCU Settings > Enable float with printf (-u _printf_float)。
完整代码
//rs485.c
#include "string.h"
#include "rs485.h"
#include "main.h"
#include <stdio.h> // 添加标准输入输出库
#include <stdarg.h> // 用于 va_list, va_start, va_end
#include <string.h>
#define RS485_TX_BUFFER_SIZE 128 // 发送缓冲区大小
volatile uint8_t rs485_tx_buf[128];
volatile uint8_t rs485_tx_busy = 0;
uint8_t rxBuffer[50];
volatile uint8_t usart2_tx_complete = 0; // 发送完成标志
extern DMA_HandleTypeDef hdma_usart2_rx;
extern UART_HandleTypeDef huart2;
//// 重定向printf到USART2
//int __io_putchar(int ch) {
// HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
// return ch;
//}
//// 可选:如果需要scanf,重定向输入
//int __io_getchar(void) {
// uint8_t ch;
// HAL_UART_Receive(&huart2, &ch, 1, HAL_MAX_DELAY);
// return ch;
//}
void RS485_Printf(const char *format, ...) {
if (rs485_tx_busy) {
return; // 或加入队列等待
}
va_list args;
va_start(args, format);
int len = vsnprintf((char *)rs485_tx_buf, sizeof(rs485_tx_buf), format, args);
va_end(args);
if (len > 0) {
rs485_tx_busy = 1;
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // 发送模式
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485_tx_buf, len); // DMA发送
}
}
uint8_t RS485_IsBusy(void) {
return rs485_tx_busy;
}
void RS485_Send(uint8_t *data, uint16_t size) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // 发送模式
usart2_tx_complete = 0;
HAL_UART_Transmit_DMA(&huart2, data, size); // 非阻塞发送
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
if (huart == &huart2) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // 发送完成后切接收模式
usart2_tx_complete = 1; rs485_tx_busy = 0; // 标记发送完成
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart == &huart2) {
RS485_Send(rxBuffer, 3); // 回传数据
HAL_UART_Receive_DMA(&huart2, rxBuffer, 3); // 重启接收
}
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){
if (huart == &huart2) {
RS485_Send(rxBuffer, Size); // 回传数据
HAL_UARTEx_ReceiveToIdle_DMA(&huart2, rxBuffer, sizeof(rxBuffer));
__HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);
}
}
//rs485.h
#ifndef __RS485_H
#define __RS485_H
#include "stdint.h"
void RS485_Printf(const char *format, ...);
uint8_t RS485_IsBusy(void);
void RS485_Send(uint8_t *data, uint16_t size) ;
#endif
使用测试:
while (1) {
RS485_Printf("Temperature: %.1f\r\n", 25.5f);
HAL_Delay(1000); // 避免频繁发送
}
现象:
调试助手显示收到 Temperature: 25.5