/* * 模块名称:主机侧单测桩 - main.h(sb_link 模块专用) * 模块功能:给 Drivers/BSP/ScreenLink/sb_link.c 提供"可脚本控制"的 HAL 面: * ① 假 USART 寄存器:SR / DR 的**读取顺序与次数**可记账,ORE 锁存可注入; * ② 假 ReceiveToIdle_IT:记录挂接收的缓冲/长度,返回值可脚本指定(OK/BUSY/TIMEOUT/ERROR)。 * ⚠ 仅供 test/host/sb_link/ 使用,不参与 MCU 构建(MCU 走真实 main.h -> stm32f4xx_hal.h)。 * * 为什么必须桩:真实链 main.h -> stm32f4xx_hal.h -> stm32f4xx.h -> core_cm4.h 里是 * __IO 寄存器绝对地址与 ARMCC 内联汇编,宿主 MSVC 既编不动也访问不了。 * * ★ 本桩唯一的技巧点:`->SR` / `->DR` 的读没法用普通结构体成员钩住(成员访问不能替换)。 * 于是把 USART_TypeDef 的两个成员做成**函数指针**,再用宏把成员名改写成「成员名()」: * 生产代码 p_huart->Instance->SR * 预处理后 p_huart->Instance->sr_cell() * 即一次真实的函数调用 —— 测试因此能记账"读了什么、什么顺序",并让 ORE 的清除 * 严格按 RM0090 §30.6.1 的序列生效(**先读 SR 再读 DR 才清得掉**,顺序反了清不掉)。 * 改成员名之前先确认生产代码只用 `->SR` / `->DR` 这一种写法(sb_link.c 里就两处)。 * * 创建日期:2026-10-29 */ #ifndef MAIN_H #define MAIN_H #include #include /* ===== 与真实头同值的枚举(stm32f4xx_hal_def.h / stm32f4xx_hal_uart.h) ===== */ typedef enum { HAL_OK = 0x00U, HAL_ERROR = 0x01U, HAL_BUSY = 0x02U, HAL_TIMEOUT = 0x03U } HAL_StatusTypeDef; /* RxState 的两个取值:错误回调里"要不要重挂"就靠它判(见 sb_link.c 的 on_error 说明) */ #define HAL_UART_STATE_READY (0x20U) #define HAL_UART_STATE_BUSY_RX (0x22U) #define HAL_UART_STATE_BUSY_TX (0x24U) /* HAL 的错误位(ErrorCode):ORE 是阻塞型,NE/FE/PE 是非阻塞型 */ #define HAL_UART_ERROR_PE (0x01U) #define HAL_UART_ERROR_NE (0x02U) #define HAL_UART_ERROR_FE (0x04U) #define HAL_UART_ERROR_ORE (0x08U) /* ===== ① 假 USART 寄存器(读法见文件头的★) ===== */ typedef struct { uint32_t (*sr_cell)(void); uint32_t (*dr_cell)(void); } USART_TypeDef; #define SR sr_cell() #define DR dr_cell() #define SB_FAKE_SR_ORE (0x0008U) /* USART_SR_ORE,bit3 */ #define SB_FAKE_SR_CLEAN (0x00C0U) /* TXE|TC 置位,无错误位 */ #define SB_FAKE_DR_VALUE (0x00A5U) #define SB_FAKE_TRACE_MAX (8U) static uint32_t sb_reg_trace[SB_FAKE_TRACE_MAX]; /* 'S' = 读了 SR,'D' = 读了 DR */ static uint32_t sb_reg_n; static uint32_t sb_ore_latched; /* 1 = ORE 处于锁存态 */ static uint32_t sb_sr_seen; /* 1 = 刚读过 SR(DR 才清得掉 ORE) */ static uint32_t sb_sr_cell(void) { if (sb_reg_n < SB_FAKE_TRACE_MAX) { sb_reg_trace[sb_reg_n] = (uint32_t)'S'; } sb_reg_n++; sb_sr_seen = 1U; return (sb_ore_latched != 0U) ? SB_FAKE_SR_ORE : SB_FAKE_SR_CLEAN; } static uint32_t sb_dr_cell(void) { if (sb_reg_n < SB_FAKE_TRACE_MAX) { sb_reg_trace[sb_reg_n] = (uint32_t)'D'; } sb_reg_n++; /* RM0090 §30.6.1:读 SR 之后再读 DR 才清 ORE;顺序反了清不掉(真机也这样) */ if (sb_sr_seen != 0U) { sb_ore_latched = 0U; } sb_sr_seen = 0U; return SB_FAKE_DR_VALUE; } static USART_TypeDef g_sb_uart = { sb_sr_cell, sb_dr_cell }; /* ===== ② 假 UART 句柄 ===== */ typedef struct { uint32_t BaudRate; uint32_t WordLength; uint32_t StopBits; uint32_t Parity; uint32_t Mode; uint32_t HwFlowCtl; uint32_t OverSampling; } UART_InitTypeDef; typedef struct { USART_TypeDef *Instance; UART_InitTypeDef Init; volatile uint32_t gState; volatile uint32_t RxState; volatile uint32_t ErrorCode; } UART_HandleTypeDef; /* 关中断三件套(2026-10-30 sb_tx_kick 的原子占位闸用)。宿主单线程、没有真中断, * 空实现即可——临界区的"不交错"性质本来就不是本套件能测的面,逻辑面(busy/ * frame_ready 闸门、扣件状态机、超时保底)全部照测。 */ #define __get_PRIMASK() (0U) #define __disable_irq() ((void)0) #define __set_PRIMASK(x) ((void)(x)) /* ===== ③ 假 ReceiveToIdle_IT ===== */ #define SB_FAKE_ARM_OK (0U) #define SB_FAKE_ARM_BUSY (1U) #define SB_FAKE_ARM_TIMEOUT (2U) #define SB_FAKE_ARM_ERROR (3U) static uint32_t sb_arm_calls; static uint32_t sb_arm_force; /* SB_FAKE_ARM_* */ static uint8_t *sb_arm_buf; static uint16_t sb_arm_size; static HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_IT(UART_HandleTypeDef *p_huart, uint8_t *p_buf, uint16_t size) { sb_arm_calls++; sb_arm_buf = p_buf; sb_arm_size = size; if (sb_arm_force == SB_FAKE_ARM_BUSY) { return HAL_BUSY; } if (sb_arm_force == SB_FAKE_ARM_TIMEOUT) { return HAL_TIMEOUT; } if (sb_arm_force == SB_FAKE_ARM_ERROR) { return HAL_ERROR; } /* ORE 锁存时真机是收不了的(RXNE 不再置位)——桩照这个语义走, * 于是"ORE 没清干净"必然表现为挂接收失败,用例能抓住 */ if (sb_ore_latched != 0U) { return HAL_ERROR; } /* 挂上了:RxState 进 BUSY_RX。错误回调据此判断"接收是不是还在跑" * (真机也是这样:非阻塞型错误不动 RxState,阻塞型错误会把它打回 READY) */ p_huart->RxState = HAL_UART_STATE_BUSY_RX; return HAL_OK; } /* ===== ④ 假 Transmit_DMA(批 2:起发在中断里,收尾在 TxCplt) ===== */ #define SB_FAKE_TX_OK (0U) #define SB_FAKE_TX_BUSY (1U) #define SB_FAKE_TX_ERROR (2U) static uint32_t sb_tx_calls; static uint32_t sb_tx_force; /* SB_FAKE_TX_* */ static const uint8_t *sb_tx_buf; static uint16_t sb_tx_len; static HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *p_huart, const uint8_t *p_buf, uint16_t size) { sb_tx_calls++; sb_tx_buf = p_buf; sb_tx_len = size; if (sb_tx_force == SB_FAKE_TX_BUSY) { return HAL_BUSY; } if (sb_tx_force == SB_FAKE_TX_ERROR) { return HAL_ERROR; } /* 真机成功起发后 gState = BUSY_TX,TxCplt 才打回 READY。 * 错误回调里"TX 是不是还在发"就靠这一位判(防残留 busy)。 */ p_huart->gState = HAL_UART_STATE_BUSY_TX; return HAL_OK; } /* ===== ⑤ 假 DWT / CoreDebug / SystemCoreClock(组帧耗时统计用) ===== */ /* 真机由 core_cm4.h 提供。宿主侧要能"推进"计数器,所以 CYCCNT 也做成函数指针 * (与 SR / DR 同一手法):每读一次前进 sb_cyccnt_step,于是 sb_link 里那两次 * 读之间的差值 = 一个 step,测试就能钉住 "周期 → µs" 的除数。 */ typedef struct { volatile uint32_t CTRL; uint32_t (*cyccnt_cell)(void); } DWT_TypeDef; typedef struct { volatile uint32_t DEMCR; } CoreDebug_TypeDef; #define CYCCNT cyccnt_cell() static uint32_t sb_cyccnt_val; static uint32_t sb_cyccnt_step; /* 每次读前进多少周期(0 = 恒 0,默认) */ static uint32_t sb_cyccnt_cell(void) { sb_cyccnt_val += sb_cyccnt_step; return sb_cyccnt_val; } static DWT_TypeDef g_sb_dwt = { 0U, sb_cyccnt_cell }; static CoreDebug_TypeDef g_sb_coredebug; static uint32_t SystemCoreClock = 168000000U; #define DWT (&g_sb_dwt) #define CoreDebug (&g_sb_coredebug) #define CoreDebug_DEMCR_TRCENA_Msk (0x01000000U) #define DWT_CTRL_CYCCNTENA_Msk (0x00000001U) /* ===== ⑥ 测试用的复位/查询把手 ===== */ static void sb_fake_reset(void) { uint32_t i; for (i = 0U; i < SB_FAKE_TRACE_MAX; i++) { sb_reg_trace[i] = 0U; } sb_reg_n = 0U; sb_ore_latched = 0U; sb_sr_seen = 0U; sb_arm_calls = 0U; sb_arm_force = SB_FAKE_ARM_OK; sb_arm_buf = NULL; sb_arm_size = 0U; sb_tx_calls = 0U; sb_tx_force = SB_FAKE_TX_OK; sb_tx_buf = NULL; sb_tx_len = 0U; g_sb_dwt.CTRL = 0U; sb_cyccnt_val = 0U; sb_cyccnt_step = 0U; } /* 寄存器读序列是否为期望的字符串(如 "SD"):长度不等或内容不符都返回 0 */ static int sb_fake_trace_is(const char *p_expect) { uint32_t i; uint32_t len = (uint32_t)0U; while (p_expect[len] != '\0') { len++; } if (sb_reg_n != len) { return 0; } for (i = 0U; i < len; i++) { if (sb_reg_trace[i] != (uint32_t)(unsigned char)p_expect[i]) { return 0; } } return 1; } #endif /* MAIN_H */