Устранена зависимость дискретов СИФУ от основного цикла в 166
Фронты START/STOP обрабатываются при принятии командного слова CAN/RS, а окончание импульса обслуживается таймером. STOP имеет приоритет, перекрытие выходов исключено, сохранена защита от самопуска при включении. Загрузка параметров больше не подменяет живые команды нагрузки. Добавлены подробные русские комментарии во все собственные C-модули и основные заголовки, описание поведения и host-тест автомата. Обновлены файлы прошивки. Полная сборка TI 22.6.1.LTS и тесты пройдены; проверка на физической плате не выполнялась.
This commit is contained in:
@@ -4,7 +4,8 @@ struct mock_gpio GpioDataRegs;
|
||||
int READY = 0, Desk = 6, Mode = 5;
|
||||
int modbus[128];
|
||||
int CanTimeOutErrorTR;
|
||||
unsigned long IMPowse, WAKEpowse;
|
||||
volatile unsigned long IMPowse;
|
||||
unsigned long WAKEpowse;
|
||||
unsigned int adc_table_lem[4];
|
||||
#define CHECK(x) if (!(x)) return __LINE__
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
extern unsigned long IMPowse, WAKEpowse;
|
||||
extern volatile unsigned long IMPowse;
|
||||
extern unsigned long WAKEpowse;
|
||||
|
||||
97
tests/ump_pulse/control_test.c
Normal file
97
tests/ump_pulse/control_test.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/* Исполняется настоящий автомат и аппаратная обёртка прошивки.
|
||||
* GPIO здесь заменены счётчиками: каждое переключение проверяется
|
||||
* отдельно, в том числе отсутствие кратковременного START+STOP.
|
||||
* В тесте нет main прошивки: этим воспроизводится его задержка на EEPROM.
|
||||
*/
|
||||
#include "ump_pulse.h"
|
||||
static unsigned int irq_mask, start_pin, stop_pin, relay_pin, overlap;
|
||||
static unsigned int start_edges, stop_edges;
|
||||
int Desk = 6;
|
||||
#define dsk_LOAD 6
|
||||
#define READY_FREQ 1000
|
||||
static volatile unsigned int command_word;
|
||||
#define Commands command_word
|
||||
volatile unsigned long IMPowse;
|
||||
static unsigned int __disable_interrupts(void)
|
||||
{ unsigned int old = irq_mask; irq_mask = 1; return old; }
|
||||
static void __restore_interrupts(unsigned int old) { irq_mask = old; }
|
||||
static void clear_START_DPTB(void) { start_pin = 0; }
|
||||
static void clear_STOP_DPTB(void) { stop_pin = 0; }
|
||||
static void set_START_DPTB(void)
|
||||
{ if (!start_pin) start_edges++; start_pin = 1; if (stop_pin) overlap++; }
|
||||
static void set_STOP_DPTB(void)
|
||||
{ if (!stop_pin) stop_edges++; stop_pin = 1; if (start_pin) overlap++; }
|
||||
static void set_RES_OUT_2(void) { relay_pin = 1; }
|
||||
static void clear_RES_OUT_2(void) { relay_pin = 0; }
|
||||
#include "ump_control_impl.h"
|
||||
#define CHECK(x) do { if (!(x)) return __LINE__; } while (0)
|
||||
__declspec(dllexport) int run_tests(void)
|
||||
{
|
||||
unsigned int i, edges;
|
||||
/* Команда до окончания инициализации не исполняется; разрешение
|
||||
* обработки не воспроизводит её задним числом. */
|
||||
UMP_commands_write(0x80);
|
||||
CHECK(!start_pin && !stop_pin);
|
||||
UMP_pulse_init();
|
||||
UMP_commands_write(0x80);
|
||||
CHECK(!start_pin);
|
||||
UMP_commands_write(0);
|
||||
UMP_commands_write(0x80);
|
||||
CHECK(start_pin && !stop_pin && relay_pin && IMPowse == 1000);
|
||||
CHECK(!irq_mask);
|
||||
/* Длительная единица не продлевает импульс. Ровно тысяча вызовов
|
||||
* таймера снимает выход; основной цикл ни разу не вызывался. */
|
||||
for (i = 0; i < 999; i++) {
|
||||
UMP_commands_write(0x80);
|
||||
UMP_pulse_tick();
|
||||
}
|
||||
CHECK(start_pin && IMPowse == 1);
|
||||
UMP_pulse_tick();
|
||||
CHECK(!start_pin && !stop_pin && relay_pin && !IMPowse);
|
||||
UMP_commands_write(0x80);
|
||||
CHECK(!start_pin);
|
||||
/* Короткий ноль замечается непосредственно в приёмном обработчике. */
|
||||
UMP_commands_write(0); UMP_commands_write(0x80);
|
||||
CHECK(start_pin && IMPowse == 1000 && start_edges == 2);
|
||||
UMP_pulse_tick();
|
||||
UMP_commands_write(0); UMP_commands_write(0x80);
|
||||
CHECK(IMPowse == 1000 && start_edges == 2);
|
||||
/* STOP прерывает активный START. Одновременная команда не выдаёт
|
||||
* даже короткого START; проверяется каждая запись мокового GPIO. */
|
||||
UMP_commands_write(0xC0);
|
||||
CHECK(!start_pin && stop_pin && !relay_pin && !overlap);
|
||||
edges = start_edges;
|
||||
for (i = 0; i < 1000; i++) UMP_pulse_tick();
|
||||
UMP_commands_write(0xC0);
|
||||
CHECK(!start_pin && !stop_pin && !IMPowse);
|
||||
UMP_commands_write(0x80);
|
||||
CHECK(!start_pin && start_edges == edges);
|
||||
UMP_commands_write(0); UMP_commands_write(0xC0);
|
||||
CHECK(!start_pin && stop_pin && start_edges == edges);
|
||||
UMP_commands_write(0x40); UMP_commands_write(0xC0);
|
||||
CHECK(!start_pin && !overlap);
|
||||
/* Снятый STOP + свежий фронт START могут переключить выход до
|
||||
* окончания STOP-импульса, но никогда не создают перекрытие. */
|
||||
UMP_commands_write(0); UMP_commands_write(0x80);
|
||||
CHECK(start_pin && !stop_pin && relay_pin && !overlap);
|
||||
/* Default/Load удаляют служебные биты, сохраняя уровень команды
|
||||
* и историю фронтов. Случайный повтор пакета не перезапускает импульс. */
|
||||
UMP_commands_write(0x88); UMP_pulse_tick();
|
||||
UMP_reset_parameter_commands();
|
||||
CHECK(Commands == 0x80 && IMPowse == 999);
|
||||
UMP_commands_write(0x80);
|
||||
CHECK(IMPowse == 999);
|
||||
/* Вложенный вызов не разрешает IRQ, если они были запрещены. */
|
||||
irq_mask = 1;
|
||||
UMP_commands_write(0x40); UMP_pulse_tick(); UMP_reset_parameter_commands();
|
||||
CHECK(irq_mask == 1 && stop_pin && !start_pin && !overlap);
|
||||
irq_mask = 0;
|
||||
/* Другие типы платы не получают дискреты нагрузки. */
|
||||
for (i = 0; i < 1000; i++) UMP_pulse_tick();
|
||||
Desk = 1; edges = start_edges;
|
||||
UMP_commands_write(0); UMP_commands_write(0x80);
|
||||
CHECK(Commands == 0x80 && start_edges == edges && !start_pin);
|
||||
UMP_reset_parameter_commands();
|
||||
CHECK(Commands == 0);
|
||||
return 0;
|
||||
}
|
||||
38
tests/ump_pulse/run_test.py
Normal file
38
tests/ump_pulse/run_test.py
Normal file
@@ -0,0 +1,38 @@
|
||||
"""Host test of the 166 START/STOP controller. Requires Clang/lld or MSVC (Windows).
|
||||
|
||||
Example: py -3 tests/ump_pulse/run_test.py --compiler "C:/path/to/cl.exe"
|
||||
All build products are written to a temporary directory.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--compiler', default=shutil.which('clang') or shutil.which('cl'))
|
||||
args = parser.parse_args()
|
||||
if not args.compiler:
|
||||
parser.error('Provide --compiler pointing to clang.exe or cl.exe')
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
with tempfile.TemporaryDirectory(prefix='ump-recorder-') as directory:
|
||||
output = Path(directory)
|
||||
library = output / 'recorder_test.dll'
|
||||
includes = [root / 'Source/Internal/Include']
|
||||
source = root / 'tests/ump_pulse/control_test.c'
|
||||
if Path(args.compiler).stem.lower() == 'cl':
|
||||
command = [args.compiler, '/nologo', '/LD', '/GS-', '/wd4068', '/wd4312',
|
||||
*['/I' + str(p) for p in includes], str(source),
|
||||
'/Fo' + str(output / 'recorder.obj'), '/link', '/NOENTRY',
|
||||
'/NODEFAULTLIB', '/OUT:' + str(library)]
|
||||
else:
|
||||
command = [args.compiler, '-shared', '-nostdlib', '-fuse-ld=lld',
|
||||
'-Wl,/noentry,/nodefaultlib', '-Wno-unknown-pragmas',
|
||||
'-Wno-int-to-pointer-cast', *['-I' + str(p) for p in includes],
|
||||
str(source), '-o', str(library)]
|
||||
subprocess.run(command, cwd=output, check=True)
|
||||
subprocess.run([os.sys.executable, '-c',
|
||||
'import ctypes,sys; r=ctypes.CDLL(sys.argv[1]).run_tests(); '
|
||||
'print("PASS" if r==0 else f"FAIL: C line {r}"); sys.exit(bool(r))',
|
||||
str(library)], check=True)
|
||||
Reference in New Issue
Block a user