Compare commits

...

2 Commits

7 changed files with 99 additions and 57 deletions

View File

@ -33,8 +33,6 @@ static uint32_t last_query_tick = 0;
static uint32_t feed_start_tick = 0; /* 投喂开始时刻,用于超时保护 */
static uint8_t first_query_sent = 0; /* 首次上电查询已发送 */
static uint32_t await_reply_tick = 0; /* 查询发出时刻0=无等待中的查询 */
static uint8_t query_retried = 0; /* 本次查询已补发过 */
static uint8_t await_bad_frame = 0; /* 等待期间收到过坏帧(帧头被切/校验错) */
static uint8_t first_query_reported = 0; /* 首次上电有效重量已上报 */
/* 重量异常增加计数 */
@ -79,8 +77,11 @@ static void feed_scale_dump_frame(const uint8_t *buf, uint16_t len)
}
/* 从 485 应答中解析重量,应答 9 字节: 01 03 04 41 97 30 33 0B F6 */
volatile uint32_t g_parse_cnt = 0; /* 解析被调次数(排障) */
static int feed_scale_parse_weight(const uint8_t *buf, uint16_t len, uint16_t *weight_100g)
{
g_parse_cnt++;
/* 485 回声(自己发出去的查询被 RX 听到):静默丢弃,不打日志不报错 */
if (len == sizeof(RS485_WEIGHT_CMD) &&
memcmp(buf, RS485_WEIGHT_CMD, sizeof(RS485_WEIGHT_CMD)) == 0) {
@ -150,12 +151,6 @@ static int feed_scale_parse_weight(const uint8_t *buf, uint16_t len, uint16_t *w
/* 通过 USART2 发送 485 查询命令 */
static void feed_scale_send_query(void)
{
/* 排障打印接收通道硬件状态CR1 含 RXNEIE 位、SR 含 ORE/FE/RXNE 标志、
* HAL */
log_info("> FEED: UART2 CR1=%04X SR=%04X st=%u cnt=%lu idx=%u",
(unsigned)USART2->CR1, (unsigned)USART2->SR,
(unsigned)huart2.RxState,
(unsigned long)g_uart2_rx_byte_cnt, (unsigned)USART2_GetRxIndex());
USART2_FlushRxBuf(); /* 只清软件缓冲(残留回声/垃圾),硬件接收常开不动 */
HAL_UART_Transmit(&huart2, (uint8_t *)RS485_WEIGHT_CMD, sizeof(RS485_WEIGHT_CMD), 100);
}
@ -397,32 +392,18 @@ void feedScaleProcess(void)
}
}
/* 应答监视400ms 无好帧(或收到坏帧)→ 自动补发一次;
* 600ms parse */
if (await_reply_tick) {
uint32_t el = HAL_GetTick() - await_reply_tick;
if (!query_retried && (el > 400u || (await_bad_frame && el > 100u))) {
query_retried = 1;
await_bad_frame = 0;
await_reply_tick = HAL_GetTick();
log_info("> FEED: 应答异常,补发查询");
feed_scale_send_query();
} else if (query_retried && el > 600u) {
/* 查询发出后 1s 仍无有效帧:打日志(不自动补发) */
if (await_reply_tick && (HAL_GetTick() - await_reply_tick) > 1000u) {
await_reply_tick = 0;
if (!await_bad_frame) {
log_warn("> FEED: 查询无应答(秤未接/线序/波特率/站号不对)");
s_last_err = 1;
}
await_bad_frame = 0;
}
}
/* 读取 485 返回 */
len = feed_scale_read_frame(rx_buf, sizeof(rx_buf));
if (len > 0) {
int prc = feed_scale_parse_weight(rx_buf, len, &cur_weight);
if (prc == 0) { await_reply_tick = 0; query_retried = 0; await_bad_frame = 0; }
else if (prc != -7) { await_bad_frame = 1; } /* 坏帧:触发补发 */
if (prc == 0) { await_reply_tick = 0; }
if (prc != -7) { /* -7=回声帧:不影响状态显示 */
s_last_err = (prc == 0) ? 0 : (prc == -1 ? 2 : (prc == -2 ? 3 : (prc == -4 ? 4 : (prc == -6 ? 6 : 5))));
}

View File

@ -100,6 +100,7 @@ void app_init(void)
/* HLW8032 电能计量UART5 PD2只累计电量 */
hlw8032_init();
/* OLED 屏幕128x64 + GB2312 字库 IC */
oled_init();
if (g_feed_scale_on) { oled_locate(1, 1); oled_print("智能水产投料机"); }
@ -222,6 +223,7 @@ static void app_task(void *arg)
/* 喂料秤轮询与投喂保护 */
feedScaleProcess();
vTaskDelay(10);
}
}

View File

@ -10,6 +10,7 @@ UART_HandleTypeDef huart2;
UART_HandleTypeDef huart3;
UART_HandleTypeDef huart4;
UART_HandleTypeDef huart5;
DMA_HandleTypeDef hdma_usart2_rx;
uint16_t Rx_Len = 0;
uint8_t Rx_Buf[Rx_Max] = {0};
@ -29,16 +30,23 @@ static uint8_t uart1_cmd_buf[UART1_RX_BUF_SIZE] = {0};
static uint8_t uart1_cmd_index = 0;
static uint32_t uart1_last_rx_tick = 0;
static uint8_t uart2_rx_byte = 0;
volatile uint32_t g_uart2_rx_byte_cnt = 0; /* USART2 累计收到的字节数485 排障用) */
static uint8_t uart5_rx_byte = 0;
static uint8_t uart2_cmd_buf[UART2_RX_BUF_SIZE] = {0};
static uint8_t uart2_cmd_index = 0;
static uint32_t uart2_last_rx_tick = 0;
/* USART2 DMA 接收485 秤DMA 填 dma_buf线路静默产生 IDLE 中断即为一帧 */
uint8_t uart2_dma_buf[64];
uint8_t uart2_frame_buf[64];
volatile uint16_t uart2_frame_len = 0;
volatile uint8_t uart2_frame_ready = 0;
#define UART1_CMD_TIMEOUT_MS 100u /* 无换行时,字符间隔超过该时间视为命令结束 */
#define UART2_CMD_TIMEOUT_MS 20u /* 485 返回帧较短20ms 视为帧结束 */
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
@ -202,8 +210,9 @@ void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
__HAL_UART_CLEAR_NEFLAG(huart);
__HAL_UART_CLEAR_IDLEFLAG(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart2.RxState = HAL_UART_STATE_READY; /* 关键HAL 错误处理后 RxState 常卡 BUSY_RXReceive_IT 会静默失败,接收永久停摆 */
HAL_UART_Receive_IT(&huart2, &uart2_rx_byte, 1); /* RS485 秤口同样自愈 */
huart2.RxState = HAL_UART_STATE_READY;
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf)); /* RS485 秤口 DMA 自愈 */
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
}
else if (huart->Instance == UART5) {
__HAL_UART_CLEAR_OREFLAG(huart);
@ -264,7 +273,10 @@ void USART2_StartRx(void)
uart2_last_rx_tick = 0;
memset(uart2_cmd_buf, 0, sizeof(uart2_cmd_buf));
memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf));
HAL_UART_Receive_IT(&huart2, &uart2_rx_byte, 1);
uart2_frame_ready = 0;
uart2_frame_len = 0;
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf));
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
}
/* 清 USART2 软件接收缓冲(不动硬件)。
@ -288,7 +300,8 @@ void USART2_FlushRxBuf(void)
__HAL_UART_CLEAR_OREFLAG(&huart2);
huart2.ErrorCode = HAL_UART_ERROR_NONE;
huart2.RxState = HAL_UART_STATE_READY;
HAL_UART_Receive_IT(&huart2, &uart2_rx_byte, 1);
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf));
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
}
}
@ -326,13 +339,15 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
HAL_UART_Receive_IT(&huart1, &uart1_rx_byte, 1);
}
else if (huart->Instance == USART2) {
uint8_t ch = uart2_rx_byte;
g_uart2_rx_byte_cnt++;
if (uart2_cmd_index < UART2_RX_BUF_SIZE - 1) {
uart2_cmd_buf[uart2_cmd_index++] = ch;
uart2_last_rx_tick = HAL_GetTick();
/* DMA(NORMAL) 填满 64B垃圾洪泛场景把内容当一帧提交并重启接收 */
HAL_UART_DMAStop(&huart2);
if (!uart2_frame_ready) {
memcpy(uart2_frame_buf, uart2_dma_buf, sizeof(uart2_dma_buf));
uart2_frame_len = sizeof(uart2_dma_buf);
uart2_frame_ready = 1;
}
HAL_UART_Receive_IT(&huart2, &uart2_rx_byte, 1);
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf));
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
}
else if (huart->Instance == UART5) {
hlw8032_rx_byte(uart5_rx_byte);
@ -373,18 +388,16 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
* 485 */
uint16_t USART2_PollFrame(uint8_t *out, uint16_t out_size)
{
if (uart2_cmd_index > 0 &&
(HAL_GetTick() - uart2_last_rx_tick) > UART2_CMD_TIMEOUT_MS) {
/* 排障帧提交时打印内容485 丢帧定位) */
if (uart2_frame_ready) {
/* 排障:帧提交时打印内容 */
char hex[3 * 24 + 1] = {0};
uint16_t n0 = uart2_cmd_index < 24 ? uart2_cmd_index : 24;
for (uint16_t i = 0; i < n0; i++) snprintf(hex + i * 3, 4, "%02X ", uart2_cmd_buf[i]);
log_info("> UART2: 帧提交 %d 字节: %s", (int)uart2_cmd_index, hex);
uint16_t len = uart2_cmd_index < out_size ? uart2_cmd_index : out_size - 1;
memcpy(out, uart2_cmd_buf, len);
uint16_t n0 = uart2_frame_len < 24 ? uart2_frame_len : 24;
for (uint16_t k = 0; k < n0; k++) snprintf(hex + k * 3, 4, "%02X ", uart2_frame_buf[k]);
log_info("> UART2: 帧提交 %d 字节: %s", (int)uart2_frame_len, hex);
uint16_t len = uart2_frame_len < out_size ? uart2_frame_len : out_size - 1;
memcpy(out, uart2_frame_buf, len);
out[len] = 0;
uart2_cmd_index = 0;
uart2_rx_ready = 0;
uart2_frame_ready = 0;
return len;
}
return 0;

View File

@ -43,4 +43,10 @@ uint16_t USART2_GetRxIndex(void); /* USART2
int USART1_CheckCommand(const char *cmd); /* 检查是否收到指定命令(不区分大小写) */
const char *USART1_GetCommand(void); /* 获取当前接收到的命令字符串 */
extern volatile uint32_t g_wp_pc;
extern uint32_t g_wp_ring[16];
extern uint8_t g_wp_ring_idx;
void dbg_watchpoint_uart2_idx(void);
void dbg_wp_rearm(void);
#endif

View File

@ -26,6 +26,7 @@
/* USER CODE END Includes */
extern DMA_HandleTypeDef hdma_usart3_rx;
extern DMA_HandleTypeDef hdma_uart4_rx;
extern DMA_HandleTypeDef hdma_usart2_rx;
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
@ -130,6 +131,7 @@ void HAL_UART_MspInit(UART_HandleTypeDef* huart)
/* USER CODE END USART2_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_USART2_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART2 GPIO Configuration
@ -146,6 +148,24 @@ void HAL_UART_MspInit(UART_HandleTypeDef* huart)
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART2_RX DMA Init (DMA1_Channel6) */
hdma_usart2_rx.Instance = DMA1_Channel6;
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_rx.Init.Mode = DMA_NORMAL;
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(huart, hdmarx, hdma_usart2_rx);
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
/* USART2 interrupt Init */
HAL_NVIC_SetPriority(USART2_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);

View File

@ -64,6 +64,11 @@ extern UART_HandleTypeDef huart4;
extern UART_HandleTypeDef huart2;
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart5;
extern DMA_HandleTypeDef hdma_usart2_rx;
extern uint8_t uart2_dma_buf[64];
extern uint8_t uart2_frame_buf[64];
extern volatile uint16_t uart2_frame_len;
extern volatile uint8_t uart2_frame_ready;
/* USER CODE BEGIN EV */
#include "network.h"
/* USER CODE END EV */
@ -155,12 +160,6 @@ void UsageFault_Handler(void)
*/
void DebugMon_Handler(void)
{
/* USER CODE BEGIN DebugMonitor_IRQn 0 */
/* USER CODE END DebugMonitor_IRQn 0 */
/* USER CODE BEGIN DebugMonitor_IRQn 1 */
/* USER CODE END DebugMonitor_IRQn 1 */
}
/* SVC_Handler / PendSV_Handler / SysTick_Handler ÒÑÓÉ FreeRTOS port.c Ìṩ
@ -207,6 +206,11 @@ void USART1_IRQHandler(void)
/**
* @brief This function handles DMA1 channel3 global interrupt.
*/
void DMA1_Channel6_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_usart2_rx);
}
void DMA1_Channel3_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel3_IRQn 0 */
@ -278,7 +282,20 @@ void UART4_IRQHandler(void)
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
/* 485 秤帧定界:线路静默产生 IDLEDMA 缓冲区里即一帧完整数据 */
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET)
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
HAL_UART_DMAStop(&huart2);
uint16_t len = (uint16_t)(sizeof(uart2_dma_buf) - huart2.hdmarx->Instance->CNDTR);
if (len > 0 && !uart2_frame_ready) {
memcpy(uart2_frame_buf, uart2_dma_buf, len);
uart2_frame_len = len;
uart2_frame_ready = 1;
}
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf));
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
}
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */

View File

@ -197,3 +197,6 @@
现 400ms 无好帧或收到坏帧自动补发一次查询,仍不行才报无应答
- 485 取帧改原子操作USART2_PollFrame 判静默+拷出+清缓冲一步完成,替代 Tick 置标志/GetFrame 取帧
的两步交接(交接窗口会被查询补发的清缓冲打断丢帧)
- 485 接收架构重写为 DMA+IDLEDMA1_Channel6 + 空闲中断定帧,与 4G/WiFi 同架构):
根治字节中断+软件超时方案的丢帧/停摆问题;实测秒回/慢回全部一次成功。
过程DWT 硬件观察点抓到合法写点无误,定位为原中断接收路径在特定时序下字节丢失