feat: add M95M02-only GAS and EEPROM profiles
Expose the existing registers through Modbus and service CAN using one GAS provider only in M95M02 builds. Include the EEPROM layout/driver, LOAD protection and calibration, firmware identity and shared CCS build workflow. Add transport/profile and SCI8 validation tests, documentation and the checked M95M02 firmware image.
This commit is contained in:
103
tests/load_control/load_control_test.c
Normal file
103
tests/load_control/load_control_test.c
Normal file
@@ -0,0 +1,103 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../../Source/Internal/DAC.c"
|
||||
int READY = 1, Desk = 6, Mode = 5;
|
||||
int modbus[128];
|
||||
unsigned int test_start, test_stop, test_permission, test_tick;
|
||||
unsigned int test_starts, test_last_stop_tick, test_last_start_tick, test_init_dac;
|
||||
int test_bad_order;
|
||||
unsigned int ump_dac_ramp, ump_dac_duration, ump_dac_go10, ump_dac_stop10, ump_dac_position;
|
||||
#define CHECK(x) do { if (!(x)) { printf("FAIL line %d: %s\n", __LINE__, #x); return 1; } } while (0)
|
||||
static void setup(int mode) {
|
||||
memset(modbus, 0, sizeof(modbus)); Mode = mode; READY = 1;
|
||||
test_start = test_stop = test_permission = test_tick = test_starts = 0;
|
||||
test_bad_order = test_init_dac = 0;
|
||||
Load_control_init(); Setup_DAC_time(); WAKEpowse = 0;
|
||||
DAC_go = 4; DAC_stop = 16; DAC_min() = 100; DAC_max() = 3000;
|
||||
}
|
||||
static void tick(unsigned int n, int exchange, unsigned int volts) {
|
||||
while (n--) {
|
||||
if (exchange) Load_voltage_can(0x8000U | (Mode == 5 ? 305U : 8U), volts, 0, 0);
|
||||
test_tick++; Load_runner();
|
||||
}
|
||||
}
|
||||
int main(void) {
|
||||
unsigned int out[8], error, count, ramp, code, stopped_at;
|
||||
setup(5);
|
||||
/* Boot ISR before parameters: no divide-by-zero and no GPIO start. */
|
||||
Load_control_init(); Commands = 0x80; tick(10, 1, 0); CHECK(!test_starts);
|
||||
Setup_DAC_time(); WAKEpowse = 0;
|
||||
tick(10, 0, 0); CHECK(test_starts == 1 && test_permission);
|
||||
CHECK(LOAD_CAN_TIMEOUT == 600);
|
||||
/* Wrong ZPT and masked slots never confirm freshness. */
|
||||
setup(5); Commands = 0x80;
|
||||
Load_voltage_can(0x8000U | 8U, 200, 0, 0);
|
||||
Load_voltage_can(305U, 200, 0, 0); tick(10, 0, 0);
|
||||
CHECK(!test_starts && (LOAD_STATUS & 2));
|
||||
Load_voltage_can(0x2000U | 303U, 0, 0, 30);
|
||||
tick(1, 0, 0); CHECK(test_starts == 1);
|
||||
/* Freeze current and code at timeout; unrelated exchange cannot unfreeze. */
|
||||
tick(1000, 1, 30); tick(600, 0, 0);
|
||||
ramp = ump_dac_ramp; code = modbus[123];
|
||||
tick(2000, 0, 0); CHECK(ump_dac_ramp == ramp && (unsigned int)modbus[123] == code);
|
||||
CHECK(test_permission && !test_stop && (LOAD_STATUS & 2));
|
||||
Load_voltage_can(0x8000U | 8U, 200, 0, 0); tick(10, 0, 0);
|
||||
CHECK(ump_dac_ramp == ramp);
|
||||
tick(10, 1, 30); CHECK(ump_dac_ramp > ramp);
|
||||
/* Exactly 30 V permits growth; 29 V fails strictly above 6 mA. */
|
||||
setup(6); Commands = 0x80; tick(10, 1, 30);
|
||||
load_count_ramp = time_dac / 6; tick(5, 1, 30);
|
||||
CHECK(load_state == LOAD_RUN && modbus[0x1C] >= 60);
|
||||
tick(5, 1, 29); CHECK(load_state == LOAD_RELOAD && test_stop && !test_permission);
|
||||
CHECK(!test_start && ump_dac_ramp == 0 && modbus[0x1C] == 40);
|
||||
CHECK(modbus[123] == DAC_min()); stopped_at = test_last_stop_tick;
|
||||
tick(999, 1, 0); CHECK(test_starts == 1 && test_stop);
|
||||
tick(1, 1, 0); CHECK(test_starts == 2 && test_last_start_tick-stopped_at == 1000);
|
||||
CHECK(!test_stop && test_permission && !test_bad_order);
|
||||
/* Five total attempts, then a persistent fault; no sixth start. */
|
||||
while (load_state != LOAD_FAULT) {
|
||||
load_count_ramp = time_dac / 6; tick(5, 1, 0);
|
||||
if (load_state == LOAD_RELOAD) tick(1000, 1, 0);
|
||||
}
|
||||
CHECK(test_starts == 5 && LOAD_ATTEMPTS == 5 && (LOAD_STATUS & 1));
|
||||
tick(3000, 1, 200); CHECK(test_starts == 5 && test_stop && !test_permission);
|
||||
Commands = 0xC0; tick(10, 1, 200); CHECK(load_state == LOAD_FAULT);
|
||||
Commands = 0; tick(1000, 1, 200); CHECK(!(LOAD_STATUS & 1));
|
||||
Commands = 0x80; test_starts = 0; tick(10, 1, 200); CHECK(LOAD_ATTEMPTS == 1);
|
||||
/* Manual reset dominates START; releasing it alone cannot start again. */
|
||||
Commands = 0xC0; tick(1, 1, 200); CHECK(test_stop && !test_permission);
|
||||
Commands = 0x80; tick(1500, 1, 200); CHECK(test_starts == 1);
|
||||
/* Versioned simulation writes are bounded, read-only fields reject writes. */
|
||||
count = Load_protection_prepare(3, 0x1300, 8, out, &error);
|
||||
CHECK(count == 8 && !error && out[0] == 0x5550 && out[1] == 1);
|
||||
Load_protection_prepare(6, 0x1303, 0, out, &error); CHECK(error == 3);
|
||||
Load_protection_prepare(6, 0x1304, 0, out, &error); CHECK(error == 2);
|
||||
Load_protection_prepare(6, 0x1302, 4, out, &error); CHECK(error == 3);
|
||||
setup(5); Commands = 0x80; tick(10, 1, 200);
|
||||
Load_protection_prepare(6, 0x1303, 20, out, &error); CHECK(!error);
|
||||
Load_protection_prepare(6, 0x1302, 1, out, &error); CHECK(!error);
|
||||
tick(30, 1, 200); CHECK(LOAD_STATUS & 2);
|
||||
ramp = ump_dac_ramp; tick(100, 1, 200); CHECK(ump_dac_ramp == ramp);
|
||||
Load_protection_prepare(6, 0x1302, 0, out, &error);
|
||||
tick(10, 1, 200); CHECK(!(LOAD_STATUS & 2) && ump_dac_ramp > ramp);
|
||||
Load_protection_prepare(6, 0x1302, 2, out, &error);
|
||||
load_count_ramp = time_dac / 6; tick(5, 1, 200); CHECK(load_state == LOAD_RELOAD);
|
||||
/* Calibration must not replace the forced 4 mA output during reload. */
|
||||
Commands |= 0x200; DAC_cal() = 4095; tick(5, 1, 200);
|
||||
CHECK(modbus[123] == DAC_min() && modbus[0x1C] == 40);
|
||||
/* A calibration code preceding START must be replaced by 4 mA first. */
|
||||
setup(5); Commands = 0x200; DAC_cal() = 4095;
|
||||
tick(10, 1, 200); CHECK(modbus[123] == 4095 && !load_zero_sent);
|
||||
Commands = 0x280; tick(1, 1, 200); CHECK(!test_starts);
|
||||
tick(4, 1, 200); CHECK(!test_starts && modbus[123] == DAC_min());
|
||||
tick(1, 1, 200); CHECK(test_starts == 1);
|
||||
/* Timeout while STOP is held cannot initiate the pending retry. */
|
||||
setup(5); Commands = 0x80; tick(10, 1, 0);
|
||||
load_count_ramp = time_dac / 6; tick(5, 1, 0);
|
||||
CHECK(load_state == LOAD_RELOAD);
|
||||
tick(1500, 0, 0); CHECK(test_starts == 1 && test_stop);
|
||||
tick(1, 1, 0); CHECK(test_starts == 2 && !test_stop);
|
||||
Load_control_init(); CHECK(!load_simulation);
|
||||
puts("PASS: production DAC control, timeout, CAN slots, STOP ordering, five attempts, simulation");
|
||||
return 0;
|
||||
}
|
||||
16
tests/load_control/run_test.py
Normal file
16
tests/load_control/run_test.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""Compile the production DAC.c against GPIO/IRQ mocks; no hardware writes."""
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
compiler = os.environ.get('CC') or shutil.which('gcc') or 'C:/msys64/mingw64/bin/gcc.exe'
|
||||
os.environ['PATH'] = str(Path(compiler).resolve().parent) + os.pathsep + os.environ['PATH']
|
||||
with tempfile.TemporaryDirectory(prefix='load-control-') as tmp:
|
||||
exe = Path(tmp) / 'test.exe'
|
||||
subprocess.run([compiler, '-Wall', '-Wextra', '-std=c99',
|
||||
'-Itests/load_control/stubs', '-ISource/Internal/Include',
|
||||
'tests/load_control/load_control_test.c', '-o', str(exe)], cwd=root, check=True)
|
||||
subprocess.run([str(exe)], check=True, timeout=10)
|
||||
9
tests/load_control/stubs/DSP2833x_Device.h
Normal file
9
tests/load_control/stubs/DSP2833x_Device.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef TEST_DEVICE_H
|
||||
#define TEST_DEVICE_H
|
||||
struct bits { unsigned int GPIO59, GPIO60, GPIO63; };
|
||||
struct reg { struct bits bit; };
|
||||
static struct { struct reg GPBSET, GPBCLEAR; } GpioDataRegs;
|
||||
static unsigned int __disable_interrupts(void) { return 0; }
|
||||
static void __restore_interrupts(unsigned int saved) { (void)saved; }
|
||||
static void DSP28x_usDelay(unsigned long n) { (void)n; }
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
/* No IRQ registers needed in the host test. */
|
||||
1
tests/load_control/stubs/RS485.h
Normal file
1
tests/load_control/stubs/RS485.h
Normal file
@@ -0,0 +1 @@
|
||||
/* No serial dependencies in the host test. */
|
||||
1
tests/load_control/stubs/filter_bat2.h
Normal file
1
tests/load_control/stubs/filter_bat2.h
Normal file
@@ -0,0 +1 @@
|
||||
/* No filter dependencies in the host test. */
|
||||
4
tests/load_control/stubs/measure.h
Normal file
4
tests/load_control/stubs/measure.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#define READY_FREQ 1000
|
||||
#define DAC_FREQ 200
|
||||
#define LOAD_TIME 30
|
||||
extern int READY;
|
||||
1
tests/load_control/stubs/message.h
Normal file
1
tests/load_control/stubs/message.h
Normal file
@@ -0,0 +1 @@
|
||||
extern int modbus[128];
|
||||
17
tests/load_control/stubs/package.h
Normal file
17
tests/load_control/stubs/package.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#define dsk_LOAD 6
|
||||
#define adr_LOA1 5
|
||||
#define adr_LOA2 6
|
||||
#define Commands modbus[127]
|
||||
#define cUMPstart (Commands & 0x80)
|
||||
#define cUMPreset (Commands & 0x40)
|
||||
extern unsigned int test_init_dac;
|
||||
#define cInitDac test_init_dac
|
||||
#define cCalibrDac (Commands & 0x200)
|
||||
#define DAC_go modbus[0x50]
|
||||
#define DAC_stop modbus[0x51]
|
||||
#define LOAD_CAN_TIMEOUT modbus[0x52]
|
||||
#define LOAD_STATUS modbus[0x7C]
|
||||
#define LOAD_ATTEMPTS modbus[0x7D]
|
||||
#define DAC_min() modbus[0x79]
|
||||
#define DAC_max() modbus[0x78]
|
||||
#define DAC_cal() modbus[0x7A]
|
||||
14
tests/load_control/stubs/peripher.h
Normal file
14
tests/load_control/stubs/peripher.h
Normal file
@@ -0,0 +1,14 @@
|
||||
extern int Desk, Mode;
|
||||
extern unsigned int test_start, test_stop, test_permission, test_tick;
|
||||
extern unsigned int test_starts, test_last_stop_tick, test_last_start_tick;
|
||||
extern int test_bad_order;
|
||||
static void clear_START_DPTB(void) { test_start = 0; }
|
||||
static void set_START_DPTB(void) {
|
||||
if (test_stop || (test_starts && test_tick-test_last_stop_tick < 1000)) test_bad_order = 1;
|
||||
test_start = 1; test_last_start_tick = test_tick; test_starts++;
|
||||
}
|
||||
static void set_STOP_DPTB(void) { test_stop = 1; test_last_stop_tick = test_tick; }
|
||||
static void clear_STOP_DPTB(void) { test_stop = 0; }
|
||||
static void clear_RES_OUT_2(void) { test_permission = 0; }
|
||||
static void set_RES_OUT_2(void) { if (!test_start || test_stop) test_bad_order = 1; test_permission = 1; }
|
||||
static void toggle_RES_OUT_1(void) {}
|
||||
1
tests/load_control/stubs/tools.h
Normal file
1
tests/load_control/stubs/tools.h
Normal file
@@ -0,0 +1 @@
|
||||
/* Delay provided by the device stub. */
|
||||
Reference in New Issue
Block a user