V1.5: 485接收改DMA+IDLE架构(DMA1_Channel6+空闲中断定帧),根治丢帧;实测秒回慢回全通
This commit is contained in:
parent
c5b0922004
commit
eeb18b66cf
|
|
@ -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))));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_RX,Receive_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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 秤帧定界:线路静默产生 IDLE,DMA 缓冲区里即一帧完整数据 */
|
||||
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 */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -197,3 +197,6 @@
|
|||
现 400ms 无好帧或收到坏帧自动补发一次查询,仍不行才报无应答
|
||||
- 485 取帧改原子操作:USART2_PollFrame 判静默+拷出+清缓冲一步完成,替代 Tick 置标志/GetFrame 取帧
|
||||
的两步交接(交接窗口会被查询补发的清缓冲打断丢帧)
|
||||
- 485 接收架构重写为 DMA+IDLE(DMA1_Channel6 + 空闲中断定帧,与 4G/WiFi 同架构):
|
||||
根治字节中断+软件超时方案的丢帧/停摆问题;实测秒回/慢回全部一次成功。
|
||||
过程:DWT 硬件观察点抓到合法写点无误,定位为原中断接收路径在特定时序下字节丢失
|
||||
|
|
|
|||
Loading…
Reference in New Issue