@@ -0,0 +1,510 @@
/*
* 模块名称:通道标定层(L1.5,adc_cal)主机侧单元测试
* 被测对象:真实源文件 App/algo/adc_cal.c + App/cfg/cfg_file.c(均 #include
* 原样纳入——卡 4 的落盘契约必须用真引擎测,不造轮子替身)。
* 平台依赖桩:dbg_log.h( stub/ 计数宏)、cfg_store_*(本文件内存
* 虚拟盘,同 cfg_file 套件先例)、syslog_post(计数)、
* adc_input_desc()(按 ADC通道映射说明.md 权威表给 V0.1 来源映射)。
*
* 为什么必须主机侧(数据可信 §2.6):板端无标准源,「读数 = 系数 × 码值」
* 这一根本判据在台架上不可验;Q16 舍入、系数翻倍口径、负零点落盘、合法带
* 哨兵漏放,只有离线端能用独立推导的期望值 + 坏文件注入一次性钉死。
*
* 覆盖点(TC-ADCCAL-xxx;属主登记处 = 本目录,族 TC-HOST-ADCCAL):
* TC-ADCCAL-001 init 注册两段:名称/键数/data_size/无回调/默认表指向
* TC-ADCCAL-002 UA( TPAFE)折算:0 / 8192 / 32767 / -32768 对 10V/65536 独立推导
* TC-ADCCAL-003 UB( STM32)折算:0 / 2048 / 4095 对 3.3V/4096
* TC-ADCCAL-004 其余 6 路各 1 点(合计 8 通道 × ≥3 码值点,卡 2 DoD)
* TC-ADCCAL-005 TEMP 透传 + flags/k_uv 恒 0; sem 越界守卫
* TC-ADCCAL-006 默认 flags 三态:VALID 全 0; CLIPPED = UB/UC/IB/IC/IN( 0xE6)
* TC-ADCCAL-007 k_uv 精确值:152.587890625 / 805.6640625( Q16 无舍入验证)
* TC-ADCCAL-008 set():合法带边界含端点 / 越界拒绝 RAM 不动 / TEMP 拒绝
* TC-ADCCAL-009 合法带哨兵回退:坏 k/坏 b → ROM 值 + 每通道一次 [ERR] +
* flags 不报 VALID(即便位图置位)+ init 重置观测窗
* TC-ADCCAL-014 零点 b 生效(set 路径):(code+b)× k 逐项可算
* TC-ADCCAL-015 两点标定公式:合成数据还原 k/b + 写回往返 + 参数/斜率/
* 零点越界(-2)守卫(卡 12 的算术核,台架注入待跑)
* TC-ADCCAL-016 rail_ths 门限:TPAFE ±32112 / STM32 4013·81 / TEMP 不监视
* TC-ADCCAL-017 init 幂等:重复调用 RAM 复位默认 + 重新注册
* —— 以下走真实 cfg_file 引擎(卡 4 DoD)——
* TC-ADCCAL-010 文件值生效:k_ua/b_ua 经 load 改变折算结果,VALID 置位
* TC-ADCCAL-011 越界文件值:k=999 / k=-999 被引擎收下后由哨兵兜住:
* 折算 = ROM、VALID 不报(位图置位也不报)、[ERR] 留痕
* TC-ADCCAL-012 段损坏(超长文件)→ 回退 ROM 且 flags 无 VALID(卡 4 DoD)
* TC-ADCCAL-013 段缺失(文件无本段)→ ROM 默认、VALID 全 0
*
* 通过:全部 TC PASS( main 返回 0)。
* 创建日期:2026-10-25(道A 卡 2/卡 4)
*/
# include <stdint.h>
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
/* 先纳入桩头(DBG_* → 计数),再纳入生产源码 */
# include "dbg_log.h"
/* ===================== 内存虚拟文件系统(cfg_store_* 强符号) ===================== */
/* 与 cfg_file 套件同款的最小实现;生产代码经 cfg_file_store.h 做全部文件/堆操作 */
# include "cfg_file_store.h"
# define VFS_CAP 8192u
static uint8_t s_vfs [ VFS_CAP ] ;
static size_t s_vfs_len = 0u ;
static uint32_t s_vfs_mtime = 100u ;
static int s_vfs_exists = 0 ;
void * cfg_store_alloc ( size_t size ) { return malloc ( size ) ; }
void cfg_store_free ( void * p_buf ) { free ( p_buf ) ; }
uint32_t cfg_store_heap_free ( void ) { return 0u ; }
int cfg_store_read ( const char * p_path , uint8_t * p_buf , size_t cap , size_t * p_nread ) {
size_t n ;
( void ) p_path ;
if ( ! s_vfs_exists ) {
return - 1 ;
}
n = s_vfs_len ;
if ( n > cap ) {
n = cap ;
}
( void ) memcpy ( p_buf , s_vfs , n ) ;
* p_nread = n ;
return 0 ;
}
int cfg_store_write_atomic ( const char * p_path , const uint8_t * p_buf , size_t len ) {
( void ) p_path ;
if ( len > VFS_CAP ) {
return - 1 ;
}
( void ) memcpy ( s_vfs , p_buf , len ) ;
s_vfs_len = len ;
s_vfs_exists = 1 ;
s_vfs_mtime + + ;
return 0 ;
}
int cfg_store_stat ( const char * p_path , cfg_file_stat_t * p_st ) {
( void ) p_path ;
if ( ! s_vfs_exists ) {
return - 1 ;
}
p_st - > size = ( uint32_t ) s_vfs_len ;
p_st - > mtime = s_vfs_mtime ;
return 0 ;
}
int cfg_store_remove ( const char * p_path ) {
( void ) p_path ;
s_vfs_exists = 0 ;
s_vfs_len = 0u ;
return 0 ;
}
const char * cfg_store_config_path ( void ) { return " /config.ini " ; }
const char * cfg_store_tmp_path ( void ) { return " /config.tmp " ; }
/* ---- sys_log 桩:只计数自检留痕 ---- */
# include "sys_log.h"
static int s_syslog_count = 0 ;
void syslog_post ( syslog_id_t id , const char * desc ) {
( void ) id ;
( void ) desc ;
s_syslog_count + + ;
}
/* ---- adc_input_desc 桩:V0.1 权威映射(docs/硬件平台/ADC通道映射说明.md §3/§5) ---- */
# include "adc_input.h"
static const adc_channel_desc_t s_desc_stub [ ADC_SEM_COUNT ] = {
{ ADC_SRC_TPAFE , 6u } , /* UA = buf[6] */
{ ADC_SRC_STM32 , 14u } , /* UB = CH14(PC4) */
{ ADC_SRC_STM32 , 3u } , /* UC = CH3 */
{ ADC_SRC_TPAFE , 5u } , /* UN = buf[5] */
{ ADC_SRC_TPAFE , 2u } , /* IA = buf[2] */
{ ADC_SRC_STM32 , 6u } , /* IB = CH6 */
{ ADC_SRC_STM32 , 1u } , /* IC = CH1 */
{ ADC_SRC_STM32 , 2u } , /* IN = CH2 */
{ ADC_SRC_STM32 , 5u } , /* TEMP = CH5 */
} ;
const adc_channel_desc_t * adc_input_desc ( adc_semantic_t ch ) {
if ( ( int ) ch < 0 | | ( unsigned ) ch > = ( unsigned ) ADC_SEM_COUNT ) {
return NULL ;
}
return & s_desc_stub [ ( unsigned ) ch ] ;
}
/* ---- dbg_log 桩观测计数定义(stub/dbg_log.h 声明为 extern) ---- */
int g_caltest_err_count = 0 ;
int g_caltest_info_count = 0 ;
int g_caltest_debug_count = 0 ;
/* ===================== 测试框架(极简,同 cfg_file 套件) ===================== */
static int s_total = 0 , s_passed = 0 , s_failed = 0 ;
# define CHECK(cond, name) do { \
s_total++; \
if (cond) { s_passed++; printf(" [PASS] %s\n", name); } \
else { s_failed++; printf(" [FAIL] %s (line %d)\n", name, __LINE__); } \
} while (0)
/* ===================== 被测代码 ===================== */
/* 真实引擎 + 真实标定层(顺序:cfg_file.c 先,其 DBG_TAG 宏被 adc_cal.c 重定义
* 仅产生主机侧 C4005 告警——MCU 端两者本就在不同 TU,无此现象) */
# include "cfg_file.c"
# include "adc_cal.c"
/* ===================== 独立推导的期望值 ===================== */
/* 期望一律用 **double 直接除**( 10/65536、3.3/4096),不引用被测的 Q16 常量,
* 满足卡 2 DoD「期望值由 LSB 常数独立算得(不是抄实现)」;容差只需盖住
* float32 中间积舍入(相对 ~1e-7)与打印粒度。 */
static double exp_tpafe ( int32_t code ) { return ( double ) code * ( 10.0 / 65536.0 ) ; }
static double exp_stm32 ( int32_t code ) { return ( double ) code * ( 3.3 / 4096.0 ) ; }
static double exp_q16 ( int32_t code , int32_t b , int32_t k_q16 ) {
return ( ( double ) code + ( double ) b ) * ( ( double ) k_q16 / 65536.0 ) / 1.0e6 ;
}
static int near_d ( double got , double want ) {
double d = got - want ;
if ( d < 0.0 ) { d = - d ; }
return ( d < = 1.0e-4 + ( want < 0.0 ? - want : want ) * 1.0e-5 ) ? 1 : 0 ;
}
static long my_lround ( double x ) { return ( long ) ( ( x > = 0.0 ) ? ( x + 0.5 ) : ( x - 0.5 ) ) ; }
/* ===================== 辅助 ===================== */
/* 复位「引擎注册表 + 虚拟盘 + 全部观测计数」= 刚上电(白盒代价同 cfg_file 套件) */
static void reset_all ( void ) {
memset ( s_vfs , 0 , sizeof ( s_vfs ) ) ;
s_vfs_len = 0u ;
s_vfs_mtime = 100u ;
s_vfs_exists = 0 ;
memset ( ( void * ) s_sections , 0 , sizeof ( s_sections ) ) ;
memset ( & s_base , 0 , sizeof ( s_base ) ) ;
memset ( & s_last , 0 , sizeof ( s_last ) ) ;
s_stable = 0u ;
s_base_valid = 0u ;
g_caltest_err_count = 0 ;
g_caltest_info_count = 0 ;
g_caltest_debug_count = 0 ;
s_syslog_count = 0 ;
}
static void vfs_put ( const char * text ) {
size_t n = strlen ( text ) ;
if ( n > VFS_CAP ) { n = VFS_CAP ; }
( void ) memcpy ( s_vfs , text , n ) ;
s_vfs_len = n ;
s_vfs_exists = 1 ;
s_vfs_mtime + + ;
}
static const char * vfs_get ( void ) { return ( const char * ) s_vfs ; }
/* ===================== 用例(白盒组) ===================== */
/* TC-ADCCAL-001: init 注册两段(名称/键数/data_size/无回调) */
static void tc_001_init_registers ( void ) {
printf ( " [TC-ADCCAL-001] init registers [adc_cal]/[adc_cal_ofs] \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
CHECK ( s_sections [ 0 ] . used = = 1u , " section slot0 used " ) ;
CHECK ( strcmp ( s_sections [ 0 ] . name , " adc_cal " ) = = 0 , " sec0 name = adc_cal " ) ;
CHECK ( s_sections [ 0 ] . key_count = = 9u , " sec0 keys = 8k + valid " ) ;
CHECK ( s_sections [ 0 ] . data_size = = 33u , " sec0 data_size = 33 (covers valid byte) " ) ;
CHECK ( s_sections [ 0 ] . p_cb = = NULL , " sec0 no callback (RAM copy = live values) " ) ;
CHECK ( s_sections [ 1 ] . used = = 1u & & strcmp ( s_sections [ 1 ] . name , " adc_cal_ofs " ) = = 0 ,
" sec1 name = adc_cal_ofs " ) ;
CHECK ( s_sections [ 1 ] . key_count = = 8u , " sec1 keys = 8b " ) ;
CHECK ( s_sections [ 1 ] . data_size = = 32u , " sec1 data_size = 32 " ) ;
CHECK ( s_sections [ 2 ] . used = = 0u , " only two sections registered " ) ;
}
/* TC-ADCCAL-002/003/004: 8 通道 × ≥3 码值点折算,期望独立推导(卡 2 DoD) */
static void tc_002_004_apply ( void ) {
printf ( " [TC-ADCCAL-002] UA (TPAFE) apply \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UA , 0 ) , exp_tpafe ( 0 ) ) , " UA code 0 = 0V " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UA , 8192 ) , exp_tpafe ( 8192 ) ) , " UA 8192 = 1.25V " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UA , 32767 ) , exp_tpafe ( 32767 ) ) , " UA full scale minus 1 (bipolar top code) " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UA , - 32768 ) , exp_tpafe ( - 32768 ) ) , " UA -FS (bipolar) " ) ;
printf ( " [TC-ADCCAL-003] UB (STM32) apply \n " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UB , 0 ) , exp_stm32 ( 0 ) ) , " UB code 0 = 0V " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UB , 2048 ) , exp_stm32 ( 2048 ) ) , " UB mid = 1.65V " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UB , 4095 ) , exp_stm32 ( 4095 ) ) , " UB +FS 3.2992V " ) ;
printf ( " [TC-ADCCAL-004] other six channels one mid point each \n " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UC , 123 ) , exp_stm32 ( 123 ) ) , " UC 123 " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UN , - 10000 ) , exp_tpafe ( - 10000 ) ) , " UN -10000 " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_IA , 11452 ) , exp_tpafe ( 11452 ) ) , " IA 11452 (bench first-frame code) " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_IB , 4000 ) , exp_stm32 ( 4000 ) ) , " IB 4000 " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_IC , 1 ) , exp_stm32 ( 1 ) ) , " IC 1 " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_IN , 4094 ) , exp_stm32 ( 4094 ) ) , " IN 4094 " ) ;
}
/* TC-ADCCAL-005: TEMP 透传与守卫 */
static void tc_005_temp ( void ) {
printf ( " [TC-ADCCAL-005] TEMP passthrough + guards \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
CHECK ( adc_cal_apply ( ADC_SEM_TEMP , 3000 ) = = 3000.0f , " TEMP raw passthrough " ) ;
CHECK ( adc_cal_flags ( ADC_SEM_TEMP ) = = 0u , " TEMP flags = 0 (both) " ) ;
CHECK ( adc_cal_k_uv ( ADC_SEM_TEMP ) = = 0.0f , " TEMP k_uv = 0 " ) ;
CHECK ( adc_cal_flags ( ( adc_semantic_t ) ADC_SEM_COUNT ) = = 0u , " flags sem OOB = 0 " ) ;
CHECK ( adc_cal_set ( ( adc_semantic_t ) ADC_SEM_COUNT , 10000000L , 0L ) = = - 1 , " set sem OOB rejected " ) ;
CHECK ( adc_cal_apply ( ADC_SEM_UA , 0 ) = = 0.0f , " UA still applies after guards " ) ;
}
/* TC-ADCCAL-006:默认三态位图 */
static void tc_006_default_flags ( void ) {
uint32_t vl = 0u , cl = 0u ;
uint8_t i ;
printf ( " [TC-ADCCAL-006] default flags: no VALID, CLIPPED=0xE6 \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
for ( i = 0u ; i < 8u ; i + + ) {
const uint32_t f = adc_cal_flags ( ( adc_semantic_t ) i ) ;
if ( ( f & ADC_CAL_F_VALID ) ! = 0u ) { vl | = ( 1uL < < i ) ; }
if ( ( f & ADC_CAL_F_CLIPPED ) ! = 0u ) { cl | = ( 1uL < < i ) ; }
}
CHECK ( vl = = 0u , " VALID map = 0x00 (uncalibrated default build) " ) ;
/* bit1 UB, bit2 UC, bit5 IB, bit6 IC, bit7 IN */
CHECK ( cl = = ( ( 1uL < < 1 ) | ( 1uL < < 2 ) | ( 1uL < < 5 ) | ( 1uL < < 6 ) | ( 1uL < < 7 ) ) ,
" CLIPPED map = UB/UC/IB/IC/IN (0xE6) " ) ;
}
/* TC-ADCCAL-007: k_uv 精确值(Q16 无舍入口径) */
static void tc_007_k_uv ( void ) {
printf ( " [TC-ADCCAL-007] k_uv exact (Q16 no-rounding defaults) \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
CHECK ( adc_cal_k_uv ( ADC_SEM_UA ) > 152.5877f & & adc_cal_k_uv ( ADC_SEM_UA ) < 152.5881f ,
" UA k_uv = 152.587890625 " ) ;
CHECK ( adc_cal_k_uv ( ADC_SEM_UB ) > 805.6639f & & adc_cal_k_uv ( ADC_SEM_UB ) < 805.6643f ,
" UB k_uv = 805.6640625 " ) ;
}
/* TC-ADCCAL-008: set() 合法带边界与拒绝 */
static void tc_008_set ( void ) {
float before ;
printf ( " [TC-ADCCAL-008] set band edges inclusive / rejects leave RAM \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
CHECK ( adc_cal_set ( ADC_SEM_UA , 10500000L , - 12L ) = = 0 , " set in-band ok " ) ;
CHECK ( ( adc_cal_flags ( ADC_SEM_UA ) & ADC_CAL_F_VALID ) ! = 0u , " VALID set after accepted " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UA , 1000 ) , exp_q16 ( 1000 , - 12 , 10500000 ) ) ,
" apply uses accepted k/b " ) ;
CHECK ( adc_cal_set ( ADC_SEM_UA , 5000000L , 0L ) = = 0 , " ROM/2 boundary accepted " ) ;
CHECK ( adc_cal_set ( ADC_SEM_UA , 20000000L , 0L ) = = 0 , " ROM*2 boundary accepted " ) ;
before = adc_cal_apply ( ADC_SEM_UA , 100 ) ;
CHECK ( adc_cal_set ( ADC_SEM_UA , 4999999L , 0L ) = = - 1 , " below ROM/2 rejected " ) ;
CHECK ( adc_cal_set ( ADC_SEM_UA , 20000001L , 0L ) = = - 1 , " above ROM*2 rejected " ) ;
CHECK ( adc_cal_set ( ADC_SEM_UA , 10000000L , 8193L ) = = - 1 , " b +8193 rejected " ) ;
CHECK ( adc_cal_set ( ADC_SEM_UA , 10000000L , - 8193L ) = = - 1 , " b -8193 rejected " ) ;
CHECK ( adc_cal_set ( ADC_SEM_TEMP , 10000000L , 0L ) = = - 1 , " TEMP rejected " ) ;
CHECK ( adc_cal_apply ( ADC_SEM_UA , 100 ) = = before , " RAM untouched after all rejects " ) ;
}
/* TC-ADCCAL-009:合法带哨兵(白盒直写 RAM 模拟引擎收到坏值) */
static void tc_009_sanitize ( void ) {
double want ;
printf ( " [TC-ADCCAL-009] band sentinel: ROM fallback + one-shot ERR + no VALID \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
s_kcfg . k [ 0 ] = 30000000L ; /* > ROM*2:模拟坏文件/坏标定已写入 RAM */
s_kcfg . valid | = 0x01u ; /* 位图也说「已标定」——更要用哨兵兜住 */
want = exp_tpafe ( 1000 ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UA , 1000 ) , want ) , " out-of-band k falls back to ROM " ) ;
CHECK ( ( adc_cal_flags ( ADC_SEM_UA ) & ADC_CAL_F_VALID ) = = 0u , " VALID suppressed while k out of band " ) ;
CHECK ( g_caltest_err_count = = 1 , " first bad observation logs exactly one ERR " ) ;
( void ) adc_cal_apply ( ADC_SEM_UA , 2000 ) ;
( void ) adc_cal_apply ( ADC_SEM_UA , 3000 ) ;
CHECK ( g_caltest_err_count = = 1 , " repeat observations do not spam ERR " ) ;
/* b 越界同哨兵(k 合法、b 坏) */
s_bcfg . b [ 1 ] = 99999L ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UB , 1000 ) , exp_stm32 ( 1000 ) ) , " bad b -> ROM + b=0 " ) ;
CHECK ( g_caltest_err_count = = 2 , " bad b noted on its own channel " ) ;
/* init 重置留痕位 = 新观测窗 */
adc_cal_init ( ) ;
CHECK ( adc_cal_k_uv ( ADC_SEM_UA ) > 152.5f & & s_kcfg . valid = = 0u , " init restores ROM + clear valid " ) ;
/* stub 计数器是测试侧累计量,init 不清它——用增量断言 */
{
const int e0 = g_caltest_err_count ;
s_kcfg . k [ 0 ] = 30000000L ;
( void ) adc_cal_apply ( ADC_SEM_UA , 1 ) ;
CHECK ( g_caltest_err_count = = e0 + 1 , " ERR window restarts after init (delta 1) " ) ;
}
}
/* TC-ADCCAL-014: b 生效(经 set 正规路径) */
static void tc_00a_b_effect ( void ) {
printf ( " [TC-ADCCAL-014] zero offset b via set path \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
CHECK ( adc_cal_set ( ADC_SEM_IA , 10000000L , - 77L ) = = 0 , " set IA b=-77 " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_IA , 1077 ) , exp_q16 ( 1077 , - 77 , 10000000 ) ) ,
" IA apply = (code-77)*ROMk " ) ;
}
/* TC-ADCCAL-015:两点标定公式 + 写回往返 */
static void tc_00b_two_point ( void ) {
int32_t kq = 0 , bb = 0 ;
const int32_t k0 = 10000000L ;
const int32_t b0 = - 30L ;
int32_t c1 = - 250 , c2 = 32000 ;
int32_t v1 , v2 ;
int rc ;
printf ( " [TC-ADCCAL-015] two-point formula recovers synthetic k/b \n " ) ;
/* 合成测量:v = (c + b0) * k0/65536 µV( lround 模拟量化读数) */
v1 = ( int32_t ) my_lround ( ( ( double ) c1 + ( double ) b0 ) * ( ( double ) k0 / 65536.0 ) ) ;
v2 = ( int32_t ) my_lround ( ( ( double ) c2 + ( double ) b0 ) * ( ( double ) k0 / 65536.0 ) ) ;
rc = adc_cal_two_point ( c1 , c2 , v1 , v2 , & kq , & bb ) ;
CHECK ( rc = = 0 , " two_point ok " ) ;
CHECK ( kq > ( k0 - 5 ) & & kq < ( k0 + 5 ) , " k recovered within +-5 q16 " ) ;
CHECK ( bb > ( b0 - 3 ) & & bb < ( b0 + 3 ) , " b recovered within +-3 codes " ) ;
/* 写回往返:set 后 apply(c2) 应还原 v2(µV 级) */
reset_all ( ) ;
adc_cal_init ( ) ;
CHECK ( adc_cal_set ( ADC_SEM_UA , kq , bb ) = = 0 , " set recovered coeffs " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UA , c2 ) , ( double ) v2 / 1.0e6 ) , " roundtrip v(c2) = measured " ) ;
printf ( " [TC-ADCCAL-015] two_point guards \n " ) ;
CHECK ( adc_cal_two_point ( 100 , 100 , 0L , 100000L , & kq , & bb ) = = - 1 , " dc=0 rejected " ) ;
CHECK ( adc_cal_two_point ( 100 , 200 , 50000L , 50000L , & kq , & bb ) = = - 1 , " dv=0 rejected " ) ;
CHECK ( adc_cal_two_point ( 100 , 200 , 100000L , 50000L , & kq , & bb ) = = - 1 , " inverted slope rejected " ) ;
CHECK ( adc_cal_two_point ( 100 , 200 , 0L , 1000L , NULL , & bb ) = = - 1 , " NULL k rejected " ) ;
CHECK ( adc_cal_two_point ( 100 , 200 , 0L , 1000L , & kq , NULL ) = = - 1 , " NULL b rejected " ) ;
/* 真零点 +9000 码 > B_MAX:斜率对但 b 越界 -> -2(先查前端,不用系数凑) */
v1 = ( int32_t ) my_lround ( ( 100.0 + 9000.0 ) * ( ( double ) k0 / 65536.0 ) ) ;
v2 = ( int32_t ) my_lround ( ( 2000.0 + 9000.0 ) * ( ( double ) k0 / 65536.0 ) ) ;
CHECK ( adc_cal_two_point ( 100 , 2000 , v1 , v2 , & kq , & bb ) = = - 2 , " b beyond +-8192 -> -2 " ) ;
}
/* TC-ADCCAL-016:贴轨门限表 */
static void tc_00c_rail_ths ( void ) {
int32_t lo = 0 , hi = 0 ;
printf ( " [TC-ADCCAL-016] rail thresholds by source \n " ) ;
CHECK ( adc_cal_rail_ths ( ADC_SEM_UA , & lo , & hi ) = = 0 & & hi = = 32112L & & lo = = - 32112L ,
" TPAFE band = +-98% half-FS " ) ;
CHECK ( adc_cal_rail_ths ( ADC_SEM_UB , & lo , & hi ) = = 0 & & hi = = 4013L & & lo = = 81L ,
" STM32 band = 98%/2% of 4095 " ) ;
CHECK ( adc_cal_rail_ths ( ADC_SEM_TEMP , & lo , & hi ) = = - 1 , " TEMP not rail-watched " ) ;
CHECK ( adc_cal_rail_ths ( ADC_SEM_UA , NULL , & hi ) = = - 1 , " NULL guard " ) ;
}
/* TC-ADCCAL-017: init 幂等 */
static void tc_00d_idempotent ( void ) {
printf ( " [TC-ADCCAL-017] re-init restores defaults \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
( void ) adc_cal_set ( ADC_SEM_UA , 12345678L , 5L ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UA , 1000 ) , exp_q16 ( 1000 , 5 , 12345678 ) ) , " pre-check tuned " ) ;
adc_cal_init ( ) ; /* 幂等重入:RAM 复位 + 再注册 */
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UA , 1000 ) , exp_tpafe ( 1000 ) ) , " re-init back to ROM " ) ;
CHECK ( adc_cal_flags ( ADC_SEM_UA ) = = 0u , " re-init clears VALID too " ) ;
}
/* ===================== 用例(引擎集成组,卡 4 DoD) ===================== */
/* TC-ADCCAL-010:文件值经 load 生效 + VALID */
static void tc_010_file_apply ( void ) {
printf ( " [TC-ADCCAL-010] file values applied via cfg_file_load \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
vfs_put ( " [adc_cal] \r \n k_ua=12000000 \r \n valid=1 \r \n "
" [adc_cal_ofs] \r \n b_ua=-40 \r \n " ) ;
CHECK ( cfg_file_load ( ) = = 0 , " load ok " ) ;
CHECK ( adc_cal_k_uv ( ADC_SEM_UA ) > 183.105f & & adc_cal_k_uv ( ADC_SEM_UA ) < 183.106f ,
" UA k = 12000000 q16 = 183.10546875 uV " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UA , 6557 ) , exp_q16 ( 6557 , - 40 , 12000000 ) ) ,
" apply uses file k and negative file b " ) ;
CHECK ( ( adc_cal_flags ( ADC_SEM_UA ) & ADC_CAL_F_VALID ) ! = 0u , " VALID from file bitmap " ) ;
/* 未列键保留默认 */
CHECK ( adc_cal_k_uv ( ADC_SEM_UB ) > 805.663f & & adc_cal_k_uv ( ADC_SEM_UB ) < 805.665f ,
" UB untouched keeps ROM " ) ;
}
/* TC-ADCCAL-011:越界文件值由哨兵兜住(含位图说谎) */
static void tc_011_file_bad ( void ) {
printf ( " [TC-ADCCAL-011] out-of-band file k -> sentinel ROM fallback, VALID denied \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
vfs_put ( " [adc_cal] \r \n k_ua=999 \r \n k_ub=-999 \r \n valid=7 \r \n " ) ;
CHECK ( cfg_file_load ( ) = = 0 , " load ok (values structurally valid INT) " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UA , 500 ) , exp_tpafe ( 500 ) ) , " UA sanitized to ROM " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UB , 500 ) , exp_stm32 ( 500 ) ) , " UB sanitized to ROM " ) ;
CHECK ( adc_cal_flags ( ADC_SEM_UA ) = = 0u , " UA VALID denied despite bitmap bit (k out of band) " ) ;
CHECK ( adc_cal_flags ( ADC_SEM_UB ) = = ADC_CAL_F_CLIPPED , " UB keeps only CLIPPED " ) ;
CHECK ( g_caltest_err_count > = 2 , " both sanitizations logged " ) ;
}
/* TC-ADCCAL-012:段损坏(超长文件)→ 回退默认且 flags 无 VALID(卡 4 DoD 原句) */
static void tc_012_file_corrupt ( void ) {
char big [ CFG_FILE_MAX_LEN + 64 ] ;
size_t i ;
printf ( " [TC-ADCCAL-012] oversized corrupt file -> ROM defaults + no VALID \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
/* 先把有效值 + valid 位图写入 RAM(模拟曾标定过),再用超长文件触发引擎复位 */
( void ) adc_cal_set ( ADC_SEM_UA , 11000000L , 3L ) ;
for ( i = 0u ; i < ( sizeof ( big ) - 1u ) ; i + + ) {
big [ i ] = ' A ' ;
}
big [ sizeof ( big ) - 1u ] = ' \0 ' ;
vfs_put ( big ) ;
CHECK ( cfg_file_load ( ) = = 0 , " load survives corruption " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_UA , 4096 ) , exp_tpafe ( 4096 ) ) , " corrupt -> ROM coefficients " ) ;
CHECK ( adc_cal_flags ( ADC_SEM_UA ) = = 0u , " corrupt -> no VALID anywhere (DoD wording) " ) ;
CHECK ( s_syslog_count > = 1 , " corruption left self-test trail " ) ;
}
/* TC-ADCCAL-013:段缺失 → 引擎默认(=ROM),VALID 全 0 */
static void tc_013_section_missing ( void ) {
printf ( " [TC-ADCCAL-013] missing sections -> factory ROM, vl=0 \n " ) ;
reset_all ( ) ;
adc_cal_init ( ) ;
vfs_put ( " ; only a comment, no managed sections \n " ) ;
CHECK ( cfg_file_load ( ) = = 0 , " load ok with unrelated file " ) ;
CHECK ( near_d ( adc_cal_apply ( ADC_SEM_IN , 2048 ) , exp_stm32 ( 2048 ) ) , " IN still ROM " ) ;
CHECK ( adc_cal_flags ( ADC_SEM_IA ) = = 0u , " no VALID with missing sections " ) ;
}
/* ===================== main ===================== */
int main ( void ) {
printf ( " === ADC Cal Layer Tests === \n " ) ;
tc_001_init_registers ( ) ;
tc_002_004_apply ( ) ;
tc_005_temp ( ) ;
tc_006_default_flags ( ) ;
tc_007_k_uv ( ) ;
tc_008_set ( ) ;
tc_009_sanitize ( ) ;
tc_00a_b_effect ( ) ;
tc_00b_two_point ( ) ;
tc_00c_rail_ths ( ) ;
tc_00d_idempotent ( ) ;
tc_010_file_apply ( ) ;
tc_011_file_bad ( ) ;
tc_012_file_corrupt ( ) ;
tc_013_section_missing ( ) ;
printf ( " \n === ADC Cal Layer Tests: %d/%d PASSED (failed=%d) === \n " ,
s_passed , s_total , s_failed ) ;
return ( s_failed = = 0 ) ? 0 : 1 ;
}