diff --git a/APP/app_log.c b/APP/app_log.c index f104468..63248fc 100644 --- a/APP/app_log.c +++ b/APP/app_log.c @@ -727,6 +727,27 @@ uint8_t app_log_try_command(const char *cmd) SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_ERASE_OK\n"); return 1U; } + if (strncmp(cmd, "i2c scan", 8) == 0) { + uint8_t found[16]; + uint8_t n; + uint8_t i; + char line[48]; + + n = wk_i2c1_scan(found, (uint8_t)sizeof(found)); + for (i = 0U; i < n && i < (uint8_t)sizeof(found); i++) { + if (found[i] == 0x30U) { + SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "I2C ACK 0x30 AD85050\n"); + } else if (found[i] == 0x5AU) { + SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "I2C ACK 0x5A AW816\n"); + } else { + snprintf(line, sizeof(line), "I2C ACK 0x%02X\n", (unsigned)found[i]); + SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line); + } + } + snprintf(line, sizeof(line), "I2C_SCAN_END n=%u\n", (unsigned)n); + SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line); + return 1U; + } if (strncmp(cmd, "ui boot", 7) == 0) { /* 先 ACK:全屏 Logo 绘制很慢,避免 HIL/主机超时误判 */ SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "UI_BOOT_OK\n"); diff --git a/device/AD85020B/Drv_AD85020B.c b/device/AD85020B/Drv_AD85020B.c index 161eabc..9e61e73 100644 --- a/device/AD85020B/Drv_AD85020B.c +++ b/device/AD85020B/Drv_AD85020B.c @@ -231,6 +231,70 @@ AD85020B_StatusTypeDef AD85020B_I2C_Read(uint8_t reg, uint8_t *data) return AD85020B_I2C_ReadImpl(reg, data); } +static AD85020B_StatusTypeDef AD85020B_RamWaitClear(uint8_t mask) +{ + uint8_t i; + uint8_t st; + + for (i = 0U; i < 20U; i++) + { + if (AD85020B_I2C_Read(AD85020B_REG_CFRW, &st) != AD85020B_OK) + return AD85020B_ERROR; + if ((st & mask) == 0U) + return AD85020B_OK; + wk_delay_ms(1); + } + return AD85020B_TIMEOUT; +} + +/* 手册:CFA 只收 7 位;表中 0xA0~0xAE 与 CH1 同址、靠 RBS 分 bank。 + Channel-2 EQ16/17/18 用 bank=1。 */ +AD85020B_StatusTypeDef AD85020B_RamWrite24(uint8_t ram_addr, uint8_t bank, uint32_t value) +{ + uint8_t cfrw; + + if (AD85020B_I2C_Write(AD85020B_REG_CFA, (uint8_t)(ram_addr & 0x7FU)) != AD85020B_OK) + return AD85020B_ERROR; + if (AD85020B_I2C_Write(AD85020B_REG_A1_T, (uint8_t)((value >> 16) & 0xFFU)) != AD85020B_OK) + return AD85020B_ERROR; + if (AD85020B_I2C_Write(AD85020B_REG_A1_M, (uint8_t)((value >> 8) & 0xFFU)) != AD85020B_OK) + return AD85020B_ERROR; + if (AD85020B_I2C_Write(AD85020B_REG_A1_B, (uint8_t)(value & 0xFFU)) != AD85020B_OK) + return AD85020B_ERROR; + + cfrw = (uint8_t)(AD85020B_CFRW_W1 | (bank ? AD85020B_CFRW_RBS : 0U)); + if (AD85020B_I2C_Write(AD85020B_REG_CFRW, cfrw) != AD85020B_OK) + return AD85020B_ERROR; + return AD85020B_RamWaitClear(AD85020B_CFRW_W1); +} + +AD85020B_StatusTypeDef AD85020B_RamRead24(uint8_t ram_addr, uint8_t bank, uint32_t *value) +{ + uint8_t cfrw; + uint8_t hi, mid, lo; + + if (value == 0) + return AD85020B_ERROR; + if (AD85020B_I2C_Write(AD85020B_REG_CFA, (uint8_t)(ram_addr & 0x7FU)) != AD85020B_OK) + return AD85020B_ERROR; + + cfrw = (uint8_t)(AD85020B_CFRW_R1 | (bank ? AD85020B_CFRW_RBS : 0U)); + if (AD85020B_I2C_Write(AD85020B_REG_CFRW, cfrw) != AD85020B_OK) + return AD85020B_ERROR; + if (AD85020B_RamWaitClear(AD85020B_CFRW_R1) != AD85020B_OK) + return AD85020B_TIMEOUT; + + if (AD85020B_I2C_Read(AD85020B_REG_A1_T, &hi) != AD85020B_OK) + return AD85020B_ERROR; + if (AD85020B_I2C_Read(AD85020B_REG_A1_M, &mid) != AD85020B_OK) + return AD85020B_ERROR; + if (AD85020B_I2C_Read(AD85020B_REG_A1_B, &lo) != AD85020B_OK) + return AD85020B_ERROR; + + *value = ((uint32_t)hi << 16) | ((uint32_t)mid << 8) | (uint32_t)lo; + return AD85020B_OK; +} + /** * @brief AD85020B 初始化 * @retval AD85020B_StatusTypeDef: 初始化结果 diff --git a/device/AD85020B/Drv_AD85020B.h b/device/AD85020B/Drv_AD85020B.h index 8c63ea3..e71415a 100644 --- a/device/AD85020B/Drv_AD85020B.h +++ b/device/AD85020B/Drv_AD85020B.h @@ -40,6 +40,23 @@ #define AD85020B_REG_MVOL 0x03 // 主音量控制 #define AD85020B_REG_HEADPHONEVOL 0x87 // 耳机音量控制 +/* 系数 RAM 间接访问(手册 0x1D~0x2D) */ +#define AD85020B_REG_CFA 0x1D /* CFA[6:0] RAM 基址 */ +#define AD85020B_REG_A1_T 0x1E /* 系数 top [23:16] */ +#define AD85020B_REG_A1_M 0x1F /* 系数 middle [15:8] */ +#define AD85020B_REG_A1_B 0x20 /* 系数 bottom [7:0] */ +#define AD85020B_REG_CFRW 0x2D /* CfRW 读写触发 */ +#define AD85020B_CFRW_W1 0x01 /* 写单系数 */ +#define AD85020B_CFRW_WA 0x02 +#define AD85020B_CFRW_R1 0x04 /* 读单系数 */ +#define AD85020B_CFRW_RA 0x08 +#define AD85020B_CFRW_W3 0x10 +#define AD85020B_CFRW_R3 0x20 +#define AD85020B_CFRW_RBS 0x40 /* 0=bank0/CH1, 1=bank1/CH2 */ + +#define AD85020B_RAM_CH2_EQ16_A1 0xA0 /* CH2EQ16A1 */ +#define AD85020B_RAM_CH2_EQ18_A0 0xAE /* CH2EQ18A0 */ + /* 软件复位配置:SCTL6寄存器BIT5=SW_RSTB,0=复位,1=正常 */ #define AD85020B_SW_RESET 0x50 // BIT5=0(0x50二进制1010000,BIT5=0) #define AD85020B_SW_NORMAL 0x70 // BIT5=1(复位后恢复正常) @@ -56,6 +73,8 @@ typedef enum void AD85020B_I2C_Init(void); AD85020B_StatusTypeDef AD85020B_I2C_Write(uint8_t reg, uint8_t data); AD85020B_StatusTypeDef AD85020B_I2C_Read(uint8_t reg, uint8_t *data); +AD85020B_StatusTypeDef AD85020B_RamWrite24(uint8_t ram_addr, uint8_t bank, uint32_t value); +AD85020B_StatusTypeDef AD85020B_RamRead24(uint8_t ram_addr, uint8_t bank, uint32_t *value); AD85020B_StatusTypeDef AD85020B_Init(void); #endif diff --git a/project/inc/wk_i2c.h b/project/inc/wk_i2c.h index dc76c13..1b63e65 100644 --- a/project/inc/wk_i2c.h +++ b/project/inc/wk_i2c.h @@ -61,7 +61,8 @@ extern "C" { void wk_i2c1_init(void); /* add user code begin exported functions */ - + /* Probe I2C1 7-bit addresses 0x08..0x77 (address-only, no data write). */ + uint8_t wk_i2c1_scan(uint8_t *found, uint8_t max_found); /* add user code end exported functions */ #ifdef __cplusplus diff --git a/project/src/wk_i2c.c b/project/src/wk_i2c.c index 3f04ce0..21116af 100644 --- a/project/src/wk_i2c.c +++ b/project/src/wk_i2c.c @@ -84,5 +84,66 @@ void wk_i2c1_init(void) } /* add user code begin 1 */ +#include "i2c_application.h" +#define I2C_SCAN_ADDR_MIN 0x08U +#define I2C_SCAN_ADDR_MAX 0x77U +#define I2C_SCAN_TIMEOUT 8000U + +extern i2c_status_type i2c_master_write_addr(i2c_handle_type *hi2c, uint16_t address, uint32_t timeout); + +static void wk_i2c1_recover(void) +{ + i2c_reset(I2C1); + wk_i2c1_init(); +} + +/* Address-only probe: START + 8-bit write address + STOP. No register write. */ +static uint8_t wk_i2c1_probe(uint8_t addr7) +{ + i2c_handle_type hi2c; + i2c_status_type status; + uint16_t addr8; + + memset(&hi2c, 0, sizeof(hi2c)); + hi2c.i2cx = I2C1; + addr8 = (uint16_t)((uint16_t)addr7 << 1); + + if (i2c_wait_flag(&hi2c, I2C_BUSYF_FLAG, I2C_EVENT_CHECK_NONE, I2C_SCAN_TIMEOUT) != I2C_OK) { + wk_i2c1_recover(); + return 0U; + } + + status = i2c_master_write_addr(&hi2c, addr8, I2C_SCAN_TIMEOUT); + if (status == I2C_OK) { + i2c_flag_clear(I2C1, I2C_ADDR7F_FLAG); + i2c_stop_generate(I2C1); + (void)i2c_wait_flag(&hi2c, I2C_BUSYF_FLAG, I2C_EVENT_CHECK_NONE, I2C_SCAN_TIMEOUT); + return 1U; + } + + i2c_stop_generate(I2C1); + wk_i2c1_recover(); + return 0U; +} + +uint8_t wk_i2c1_scan(uint8_t *found, uint8_t max_found) +{ + uint8_t n = 0U; + uint8_t addr; + + wk_i2c1_init(); + + for (addr = I2C_SCAN_ADDR_MIN; addr <= I2C_SCAN_ADDR_MAX; addr++) { + if (wk_i2c1_probe(addr) != 0U) { + if (found != 0 && n < max_found) { + found[n] = addr; + } + n++; + } + } + + wk_i2c1_init(); + return n; +} /* add user code end 1 */ diff --git a/protocol/bl_uart_parse.c b/protocol/bl_uart_parse.c index ac0de6d..5c9bb0a 100644 --- a/protocol/bl_uart_parse.c +++ b/protocol/bl_uart_parse.c @@ -91,6 +91,9 @@ static void handleSectionB(uint8_t *data); static void handleSectionC(uint8_t *data); static void handleSectionD(uint8_t *data); +static void handleAmpRamRead(uint8_t *data); +static void handleAmpRamWrite(uint8_t *data); + @@ -149,6 +152,10 @@ static const BLE_SysExCmdItem bleSysExCmdTable[] = {{0x06, 0x06}, handleSectionB}, /* section B */ {{0x06, 0x07}, handleSectionC}, /* section C */ {{0x06, 0x08}, handleSectionD}, /* section D */ + + /*======== 7. amp RAM (AD85050 CH2 EQ16/17/18) 0x07 ========*/ + {{0x07, 0x00}, handleAmpRamRead}, /* read 24-bit coeff 0xA0..0xAE */ + {{0x07, 0x01}, handleAmpRamWrite}, /* write 24-bit coeff 0xA0..0xAE */ }; // ָ������ @@ -856,6 +863,124 @@ static void handleRhythmStyle(uint8_t *data) } } +/*-------- 7. amp RAM 0x07 : AD85050 Channel-2 EQ16/17/18 (0xA0..0xAE) -------- + * BLE-MIDI SysEx 数据必须 <0x80,地址/24bit 值用半字节拆开: + * 0xA0 -> 0A 00 + * 0x200000 -> 02 00 00 00 00 00 + * 读: F0 60 07 00 F7 + * 回: F0 60 07 00 <6 nibbles> F7 + * 写: F0 60 07 01 <6 nibbles> F7 + * 回: 同写帧;失败 F0 60 07 xx 7F F7 + */ +#define AMP_RAM_BANK_CH2 1U + +static uint8_t amp_parse_reg(const uint8_t *data, uint8_t *reg_out) +{ + uint8_t hi = data[4]; + uint8_t lo = data[5]; + uint8_t reg; + + if (hi > 0x0FU || lo > 0x0FU) + return 0U; + reg = (uint8_t)((hi << 4) | lo); + if (reg < AD85020B_RAM_CH2_EQ16_A1 || reg > AD85020B_RAM_CH2_EQ18_A0) + return 0U; + *reg_out = reg; + return 1U; +} + +static uint8_t amp_parse_u24(const uint8_t *nib, uint32_t *val_out) +{ + uint8_t i; + uint32_t v = 0; + + for (i = 0U; i < 6U; i++) + { + if (nib[i] > 0x0FU) + return 0U; + v = (v << 4) | (uint32_t)nib[i]; + } + *val_out = v; + return 1U; +} + +static void amp_put_reg_nibbles(uint8_t *dst, uint8_t reg) +{ + dst[0] = (uint8_t)((reg >> 4) & 0x0FU); + dst[1] = (uint8_t)(reg & 0x0FU); +} + +static void amp_put_u24_nibbles(uint8_t *dst, uint32_t value) +{ + dst[0] = (uint8_t)((value >> 20) & 0x0FU); + dst[1] = (uint8_t)((value >> 16) & 0x0FU); + dst[2] = (uint8_t)((value >> 12) & 0x0FU); + dst[3] = (uint8_t)((value >> 8) & 0x0FU); + dst[4] = (uint8_t)((value >> 4) & 0x0FU); + dst[5] = (uint8_t)(value & 0x0FU); +} + +static void amp_send_fail(uint8_t sub, uint8_t reg) +{ + uint8_t resp[8] = {0xF0, 0x60, 0x07, 0x00, 0x00, 0x00, 0x7F, 0xF7}; + resp[3] = sub; + amp_put_reg_nibbles(&resp[4], reg); + USART4_SendData(resp, sizeof(resp)); +} + +static void amp_send_ok(uint8_t sub, uint8_t reg, uint32_t value) +{ + uint8_t resp[13] = {0xF0, 0x60, 0x07, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0xF7}; + resp[3] = sub; + amp_put_reg_nibbles(&resp[4], reg); + amp_put_u24_nibbles(&resp[6], value); + USART4_SendData(resp, sizeof(resp)); +} + +static void handleAmpRamRead(uint8_t *data) +{ + uint8_t reg = 0; + uint32_t value = 0; + + if (amp_parse_reg(data, ®) == 0U) + { + amp_send_fail(0x00, (uint8_t)(((data[4] & 0x0FU) << 4) | (data[5] & 0x0FU))); + return; + } + if (AD85020B_RamRead24(reg, AMP_RAM_BANK_CH2, &value) != AD85020B_OK) + { + amp_send_fail(0x00, reg); + return; + } + LOG_I("AMP", "rd 0x%02X = 0x%06lX", (unsigned)reg, (unsigned long)value); + amp_send_ok(0x00, reg, value); +} + +static void handleAmpRamWrite(uint8_t *data) +{ + uint8_t reg = 0; + uint32_t value = 0; + + if (amp_parse_reg(data, ®) == 0U || amp_parse_u24(&data[6], &value) == 0U) + { + amp_send_fail(0x01, (uint8_t)(((data[4] & 0x0FU) << 4) | (data[5] & 0x0FU))); + return; + } + if (AD85020B_RamWrite24(reg, AMP_RAM_BANK_CH2, value) != AD85020B_OK) + { + amp_send_fail(0x01, reg); + return; + } + value = 0; + if (AD85020B_RamRead24(reg, AMP_RAM_BANK_CH2, &value) != AD85020B_OK) + { + amp_send_fail(0x01, reg); + return; + } + LOG_I("AMP", "wr 0x%02X = 0x%06lX", (unsigned)reg, (unsigned long)value); + amp_send_ok(0x01, reg, value); +} + /*-------- 5. reset / power off 0x05 --------*/ /* F0 60 05 00 F7 : ack then soft power off (executed in main-loop context) */ static void handleDeviceReset(uint8_t *data) diff --git a/tools/k1_harness/suites/app_protocol.py b/tools/k1_harness/suites/app_protocol.py index 568d630..9e43f8e 100644 --- a/tools/k1_harness/suites/app_protocol.py +++ b/tools/k1_harness/suites/app_protocol.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""app 套件:App SysEx 全协议(组 01~06)。 +"""app 套件:App SysEx 全协议(组 01~07)。 - transport=com: 进程内复用 test_protocol_app_sim.run_tests(UART4 桥) - transport=ble: 子进程跑 test_protocol_ble.py( bleak + BLE-MIDI framing), diff --git a/tools/rtt_i2c_scan.py b/tools/rtt_i2c_scan.py new file mode 100644 index 0000000..87473ac --- /dev/null +++ b/tools/rtt_i2c_scan.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +"""Scan K1 I2C1 via RTT and report amp (0x30) / mic (0x5A).""" + +from __future__ import annotations + +import os +import re +import sys +import time + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from rtt_pitch_reg_test import ( # noqa: E402 + RttSession, + connect_jlink, + find_rtt_control_block, + import_deps, + wait_rtt_ready, +) + +ACK_RE = re.compile(r"^I2C ACK 0x([0-9A-Fa-f]{2})(?:\s+(\S+))?") +END_RE = re.compile(r"^I2C_SCAN_END n=(\d+)") +AMP_ADDR = 0x30 +MIC_ADDR = 0x5A + + +def main() -> int: + if hasattr(sys.stdout, "reconfigure"): + try: + sys.stdout.reconfigure(encoding="utf-8", errors="replace") + except Exception: + pass + + import_deps() + jlink = connect_jlink("AT32F403AC") + try: + cb = find_rtt_control_block(jlink) + if cb is None: + raise SystemExit("RTT CB not found") + print(f"RTT CB @ 0x{cb:08X}", flush=True) + jlink.rtt_start(cb) + wait_rtt_ready(jlink) + + sess = RttSession(jlink) + sess.pump(0.3) + + found: dict[int, str] = {} + end_n: int | None = None + + def consume(lines: list[str]) -> None: + nonlocal end_n + for line in lines: + text = line.strip() + if not text: + continue + if text.startswith("I2C "): + print(text, flush=True) + m = ACK_RE.match(text) + if m: + found[int(m.group(1), 16)] = m.group(2) or "" + continue + m = END_RE.match(text) + if m: + end_n = int(m.group(1)) + + for attempt in range(1, 5): + mark = len(sess.lines) + sess.cmd("i2c scan", settle=0.4) + deadline = time.time() + 4.0 + while time.time() < deadline: + consume(sess.lines[mark:]) + mark = len(sess.lines) + if end_n is not None: + break + sess.pump(0.15) + consume(sess.lines[mark:]) + if end_n is not None: + break + print(f"!! no I2C_SCAN_END (try {attempt}/4)", flush=True) + + if end_n is None: + print("FAIL: firmware did not answer I2C_SCAN_END", flush=True) + return 1 + + others = [a for a in sorted(found) if a not in (AMP_ADDR, MIC_ADDR)] + amp_ok = AMP_ADDR in found + mic_ok = MIC_ADDR in found + + print("", flush=True) + print("======== I2C SCAN ========", flush=True) + print( + f"功放 AD85050 7bit 0x{AMP_ADDR:02X} / 写 0x{AMP_ADDR << 1:02X}: " + f"{'FOUND' if amp_ok else 'MISSING'}", + flush=True, + ) + print( + f"麦克风 AW816 7bit 0x{MIC_ADDR:02X} / 写 0x{MIC_ADDR << 1:02X}: " + f"{'FOUND' if mic_ok else 'MISSING'}", + flush=True, + ) + if others: + extra = " ".join(f"0x{a:02X}" for a in others) + print(f"其它 ACK: {extra}", flush=True) + else: + print("其它 ACK: (无)", flush=True) + print(f"I2C_SCAN_END n={end_n} unique={len(found)}", flush=True) + return 0 if amp_ok and mic_ok else 2 + finally: + try: + jlink.rtt_stop() + except Exception: + pass + jlink.close() + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/test_amp_reg_ble.py b/tools/test_amp_reg_ble.py new file mode 100644 index 0000000..bb6d22f --- /dev/null +++ b/tools/test_amp_reg_ble.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +"""BLE SysEx test: AD85050 CH2 EQ RAM 0xA0..0xAE read/write.""" + +from __future__ import annotations + +import argparse +import sys +import time + +from test_protocol_app_sim import Results, build, expect, hexs +from test_protocol_ble import BleMidiSim, DEFAULT_NAME + + +def u24_nibs(v: int) -> list[int]: + return [(v >> s) & 0x0F for s in (20, 16, 12, 8, 4, 0)] + + +def nibs_u24(n) -> int: + v = 0 + for x in n: + v = (v << 4) | (x & 0x0F) + return v + + +def reg_nibs(reg: int) -> tuple[int, int]: + return (reg >> 4) & 0x0F, reg & 0x0F + + +def main() -> int: + if hasattr(sys.stdout, "reconfigure"): + try: + sys.stdout.reconfigure(encoding="utf-8", errors="replace") + except Exception: + pass + + ap = argparse.ArgumentParser(description="K1 功放寄存器 BLE 读写测试") + ap.add_argument("--ble-name", default=DEFAULT_NAME) + ap.add_argument("--ble-address") + ap.add_argument("--no-unpair", action="store_true") + args = ap.parse_args() + + print(f"连接 BLE {args.ble_name!r} {args.ble_address or ''} ...", flush=True) + sim = BleMidiSim( + name=args.ble_name, + address=args.ble_address, + transport="midi", + unpair=not args.no_unpair, + ) + R = Results() + try: + print( + f"已连接 {sim.info.get('name')} {sim.info.get('address')}", + flush=True, + ) + + r = sim.query(build(0x01, 0x01), timeout=2.0) + ok, d = expect(r, (0x60, 0x01, 0x01), 6) + R.add("01 01 连接探测", "PASS" if ok else "FAIL", d) + + targets = ( + (0xA0, "CH2EQ16A1"), + (0xA4, "CH2EQ16A0"), + (0xA5, "CH2EQ17A1"), + (0xA9, "CH2EQ17A0"), + (0xAA, "CH2EQ18A1"), + (0xAE, "CH2EQ18A0"), + ) + originals: dict[int, int] = {} + + for reg, name in targets: + rh, rl = reg_nibs(reg) + r = sim.query(build(0x07, 0x00, rh, rl), timeout=1.5) + ok, d = expect(r, (0x60, 0x07, 0x00, rh, rl), 13) + if ok: + val = nibs_u24(r[6:12]) + originals[reg] = val + d += f" {name}=0x{val:06X}" + R.add(f"07 00 读 0x{reg:02X} {name}", "PASS" if ok else "FAIL", d) + + probe = 0x123456 + if 0xA4 in originals: + rh, rl = reg_nibs(0xA4) + r = sim.query(build(0x07, 0x01, rh, rl, *u24_nibs(probe)), timeout=1.5) + ok, d = expect(r, (0x60, 0x07, 0x01, rh, rl), 13) + if ok: + got = nibs_u24(r[6:12]) + ok = got == probe + d += f" wrote=0x{probe:06X} echo=0x{got:06X}" + R.add("07 01 写 0xA4=0x123456", "PASS" if ok else "FAIL", d) + + r = sim.query(build(0x07, 0x00, rh, rl), timeout=1.5) + ok = r is not None and len(r) == 13 and nibs_u24(r[6:12]) == probe + R.add("07 00 回读 0xA4=0x123456", "PASS" if ok else "FAIL", hexs(r)) + + r = sim.query( + build(0x07, 0x01, rh, rl, *u24_nibs(originals[0xA4])), timeout=1.5 + ) + ok = r is not None and len(r) == 13 and nibs_u24(r[6:12]) == originals[0xA4] + R.add( + f"07 01 恢复 0xA4=0x{originals[0xA4]:06X}", + "PASS" if ok else "FAIL", + hexs(r), + ) + else: + R.add("07 01 写 0xA4", "SKIP", "读 0xA4 失败") + R.add("07 00 回读 0xA4", "SKIP", "读 0xA4 失败") + R.add("07 01 恢复 0xA4", "SKIP", "读 0xA4 失败") + + rh, rl = reg_nibs(0xB0) + r = sim.query(build(0x07, 0x00, rh, rl), timeout=1.5) + ok = r is not None and len(r) == 8 and r[3] == 0x00 and r[6] == 0x7F + R.add("07 00 非法地址 0xB0", "PASS" if ok else "FAIL", hexs(r)) + + time.sleep(0.05) + return R.summary() + finally: + sim.close() + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/test_protocol_app_sim.py b/tools/test_protocol_app_sim.py index a49cf78..1b6d358 100644 --- a/tools/test_protocol_app_sim.py +++ b/tools/test_protocol_app_sim.py @@ -381,6 +381,44 @@ def run_tests(sim, allow_poweroff=False): R.add(f"06 {sub:02X} {name}", "SENT", "无应答属正常") time.sleep(0.05) + # ---------------- 7. 功放 RAM 0x07 (CH2 EQ16/17/18, 半字节编码) ---------------- + def _u24_nibs(v): + return [(v >> s) & 0x0F for s in (20, 16, 12, 8, 4, 0)] + + def _nibs_u24(n): + v = 0 + for x in n: + v = (v << 4) | (x & 0x0F) + return v + + r = sim.query(build(0x07, 0x00, 0x0A, 0x04), timeout=1.0) + ok, d = expect(r, (0x60, 0x07, 0x00, 0x0A, 0x04), 13) + orig_a4 = _nibs_u24(r[6:12]) if ok else None + if ok: + d += f" 0xA4=0x{orig_a4:06X}" + R.add("07 00 读 CH2EQ16A0(0xA4)", "PASS" if ok else "FAIL", d) + + if orig_a4 is not None: + probe = 0x123456 + r = sim.query(build(0x07, 0x01, 0x0A, 0x04, *_u24_nibs(probe)), timeout=1.0) + ok, d = expect(r, (0x60, 0x07, 0x01, 0x0A, 0x04), 13) + if ok: + got = _nibs_u24(r[6:12]) + ok = got == probe + d += f" wrote=0x{probe:06X} read=0x{got:06X}" + R.add("07 01 写 CH2EQ16A0=0x123456", "PASS" if ok else "FAIL", d) + r = sim.query(build(0x07, 0x00, 0x0A, 0x04), timeout=1.0) + ok = r is not None and len(r) == 13 and _nibs_u24(r[6:12]) == probe + R.add("07 00 回读=0x123456", "PASS" if ok else "FAIL", hexs(r)) + sim.query(build(0x07, 0x01, 0x0A, 0x04, *_u24_nibs(orig_a4)), timeout=1.0) + else: + R.add("07 01 写 CH2EQ16A0", "SKIP", "读 0xA4 失败") + R.add("07 00 回读", "SKIP", "读 0xA4 失败") + + r = sim.query(build(0x07, 0x00, 0x0B, 0x00), timeout=1.0) + ok = r is not None and len(r) == 8 and r[3] == 0x00 and r[6] == 0x7F + R.add("07 00 非法地址 0xB0", "PASS" if ok else "FAIL", hexs(r)) + # ---------------- 5. 复位/关机 0x05 ---------------- if allow_poweroff: time.sleep(0.3)