Record PM67 ZPT voltages in UMP logger v3

This commit is contained in:
2026-10-01 16:00:48 +03:00
parent 316dd52f99
commit 1ce42a1877
4 changed files with 77 additions and 24 deletions

View File

@@ -35,10 +35,10 @@ EEPROM не используется: под отключением в этой
## Память и точность
Использована существующая секция `.logg` по адресу 0x200000: 64000
16-битных слов. Два банка по 800 записей × 40 слов занимают её целиком.
Текущий банк кольцевой. При триггере сохраняются последние до 399 записей,
16-битных слов. Два банка по 760 записей × 42 слова занимают 63840 слов (160 слов свободны).
Текущий банк кольцевой. При триггере сохраняются последние до 379 записей,
добавляется событие отключения, оставшаяся половина банка отведена
постинтервалу. При 20 мс это до примерно 8 секунд до отключения и 1 секунды
постинтервалу. При 20 мс это до примерно 7.6 секунды до отключения и 1 секунды
после. Дополнительные события CAN и дискретов сокращают временную глубину.
При интенсивном потоке событий или периоде 1 мс постинтервал может заполнить
@@ -51,7 +51,7 @@ EEPROM не используется: под отключением в этой
одинаковую отметку; порядок сохраняется порядком записей. На перезапуске
регистратора счётчик продолжает прежнюю шкалу, при перезагрузке платы — 0.
Онлайн-опрос возвращает три последних записи каждые 40 мс. Он не гарантирует
Онлайн-опрос возвращает две последние записи каждые 40 мс. Он не гарантирует
передачу каждого события при высоком CAN-трафике, медленном UART или паузах
GUI. Номера записей позволяют убрать дубликаты и явно показать пропуски.
Полный сохранившийся фрагмент следует читать кнопкой архива. Частота 20 мс
@@ -63,7 +63,7 @@ GUI. Номера записей позволяют убрать дублика
EEPROM, выделения памяти или копирования целого банка. Реальную задержку
прерываний и длительность критических секций нужно измерить на F28335.
## Состав записи: 40 слов
## Состав записи v3: 42 слова
| Слова | Данные | Единицы/смысл |
|---|---|---|
@@ -91,6 +91,16 @@ EEPROM, выделения памяти или копирования целог
| 33–34 | эффективные dac_go/dac_stop + 4 | 0.1 мА после ограничений алгоритма; GUI переводит в мА |
| 35 | kod | целочисленное положение, используемое Anal_output |
| 36–39 | ADC_table[0..3] | значения таблицы АЦП в исходном масштабе прошивки |
| 40 | ЗПТ1: U1 + U2, ПМ67 регистр 306 / индекс 305 | исходное 16-битное слово без пересчёта |
| 41 | ЗПТ2: U3 + U4, ПМ67 регистр 9 / индекс 8 | исходное 16-битное слово без пересчёта |
Напряжения принимаются из технологических CAN-кадров с нулевыми индексами
305 и 8. Учитываются все три позиции и биты маски 0x8000/0x4000/0x2000.
Они хранятся отдельно от массива modbus[0..127], обновляются и при выключенном
логгере. Запись содержит последние принятые значения; до первого приёма — 0.
При потере связи остаётся последнее значение: отдельного признака свежести нет.
Масштаб и знаковость физического напряжения не заданы исходной таблицей;
SetGUI показывает исходное беззнаковое слово, коэффициент задаётся в интерфейсе.
**Отправленный код ЦАП не является измеренным током выхода.** В этом проекте
не установлено, какой вход аппаратно измеряет ток ЦАП. При включённом
@@ -116,9 +126,9 @@ FC03, 0x1000, 16 слов:
| Смещение | Значение |
|---|---|
| 0,1 | 0x554D, версия 2 |
| 0,1 | 0x554D, версия 3 |
| 2,3 | состояние и число записей выбранного источника |
| 4,5 | 40 слов в записи, 800 записей в банке |
| 4,5 | 42 слова в записи, 760 записей в банке |
| 6 | период периодических снимков, мс |
| 7,8 | счётчик времени, младшее/старшее слово |
| 9,10 | Desk (требуется 6 / dsk_LOAD), Mode |
@@ -133,25 +143,28 @@ FC03, 0x1000, 16 слов:
FC06:
- 0x1000: 1 — новый текущий журнал, 0 — остановить его;
- 0x1001: период 1..1000, менять только при остановленном журнале;
- 0x1002: хронологический индекс первой записи окна 0..799;
- 0x1002: хронологический индекс первой записи окна 0..759;
- 0x1003: источник окна, 0 — текущий остановленный банк, 1 — архив отключения.
FC03, 0x1010, до 120 слов: окно до трёх записей от установленного индекса.
FC03, 0x1010, до 120 слов: окно от установленного индекса; для целых записей v3 читать 42 или 84 слова.
CAN допускает 42/84 и прежние размеры 40/80/120; RTU допускает 1..120 слов.
Чтение текущего банка этим окном во время записи не выдаёт изменяющиеся
данные. GUI сначала останавливает его; архив можно читать при работающем
текущем банке. После всех блоков GUI повторно проверяет поколение архива;
при замене во время чтения результат не подменяет предыдущий график.
FC03, 0x1100, ровно 124 слова: онлайн-блок. Слова 0,1 — 32-битный номер
первой записи, 2 — число записей 0..3, 3 — поколение архива, далее до трёх
записей по 40 слов. Неиспользованные слова равны нулю. Окно онлайн не
первой записи, 2 — число записей 0..2, 3 — поколение архива, далее до двух
записей по 42 слова. Неиспользованные слова равны нулю. Окно онлайн не
зависит от курсора/выбора архива. Разделение 0x1010 и 0x1100 предотвращает
пересечение двух окон.
Недопустимые диапазоны и значения возвращают исключения Modbus 2/3.
CRC и разбор стандартного ответа GUI используют существующее общее ядро.
Расширение описано отдельно в core/ump_logger.py, без изменения общей
библиотеки и без необходимости пересобирать её DLL.
SetGUI распознаёт v2 (40 слов, 800 записей) и v3 (42 слова, 760 записей).
Для v2 новые напряжения недоступны (NaN), для v3 доступны графики и CSV.
CAN-валидация общей библиотеки расширена размерами 42/84 слова.
Исходники протокола подключаются к прошивке из templates/c/set-protocol.
## Проверка и запуск
@@ -176,3 +189,6 @@ TI. Отдельно выполнена синтаксическая прове
подать откл. → через секунду прочитать архив → сверить код ЦАП, ток внешним
измерителем и физические входы/выходы; затем повторить с разрывом RS.
Период 1 мс проверить отдельно под максимальной CAN-нагрузкой.
Для сборки v3 нужен templates/main начиная с коммита c0ee7c4
(поддержка CAN-чтения 42/84 слов и порт TMS320F28335).

View File

@@ -33,12 +33,13 @@ extern LOG Log;
void clear_mem();
/* Ëîããåð ÓÌÏ: îòäåëüíîå àäðåñíîå ïðîñòðàíñòâî Modbus, áåç èçìåíåíèÿ
* 128 òåõíîëîãè÷åñêèõ ðåãèñòðîâ. Äâà áàíêà ïî 800 çàïèñåé:
* 128 òåõíîëîãè÷åñêèõ ðåãèñòðîâ. Äâà áàíêà ïî 760 çàïèñåé:
* òåêóùèé êîëüöåâîé æóðíàë è çàôèêñèðîâàííûé ôðàãìåíò îòêëþ÷åíèÿ. */
#define UMP_LOG_BASE 0x1000U
#define UMP_LOG_DATA 0x1010U
#define UMP_LOG_WORDS 40U
#define UMP_LOG_CAPACITY 800U
#define UMP_LOG_WORDS 42U
#define UMP_LOG_CAPACITY 760U
#define UMP_LOG_LIVE_RECORDS (120U / UMP_LOG_WORDS)
#define UMP_LOG_LIVE 0x1100U
extern unsigned int ump_dac_ramp, ump_dac_duration, ump_dac_go10, ump_dac_stop10, ump_dac_position;
void ump_log_tick(void);

View File

@@ -43,6 +43,8 @@ static unsigned int ump_bank, ump_archive_bank = 1;
static unsigned int ump_archive_count, ump_archive_next, ump_generation;
static unsigned int ump_source, ump_post, ump_post_dropped;
static unsigned int ump_can_header, ump_can_data[3], ump_can_count;
/* Latest PM67 sums: register 306 (index 305), register 9 (index 8). */
static unsigned int ump_pm67_voltage[2];
static unsigned int ump_prev_commands, ump_prev_inputs, ump_prev_outputs;
extern int CanTimeOutErrorTR;
/* Äèàãíîñòèêà âíóòðåííèõ ïåðåìåííûõ ðàçãîíà. Îáíîâëÿåòñÿ ñàìèì Load_runner,
@@ -113,6 +115,7 @@ static void ump_capture(unsigned int reason)
p[31] = ump_dac_ramp; p[32] = ump_dac_duration;
p[33] = ump_dac_go10; p[34] = ump_dac_stop10; p[35] = ump_dac_position;
for (i = 0; i < 4; i++) p[36+i] = ADC_table[i];
p[40] = ump_pm67_voltage[0]; p[41] = ump_pm67_voltage[1];
ump_prev_commands = commands; ump_prev_inputs = p[4]; ump_prev_outputs = p[5];
ump_next++;
if (ump_next == UMP_LOG_CAPACITY) ump_next = 0;
@@ -161,9 +164,20 @@ void ump_log_io(void)
* íîâûå êîìàíäû, à òàêæå èñõîäíûé àäðåñ/ìàñêó è âñå òðè ñëîâà êàäðà. */
void ump_log_can(unsigned int header, unsigned int a, unsigned int b, unsigned int c)
{
unsigned int saved;
if (ump_state != 1 || Desk != dsk_LOAD) return;
unsigned int saved, i, address, mask, data[3];
if (Desk != dsk_LOAD) return;
saved = __disable_interrupts();
data[0] = a; data[1] = b; data[2] = c;
address = header & 0x1FFFU;
mask = 0x8000U;
/* Track reception even while recording is stopped. Each mask bit
* refers to its own address, including disabled words in between. */
for (i = 0; i < 3; i++, address++, mask >>= 1) {
if (!(header & mask)) continue;
if (address == 305U) ump_pm67_voltage[0] = data[i];
if (address == 8U) ump_pm67_voltage[1] = data[i];
}
if (ump_state != 1) { __restore_interrupts(saved); return; }
ump_can_header = header;
ump_can_data[0] = a; ump_can_data[1] = b; ump_can_data[2] = c;
ump_can_count++;
@@ -227,7 +241,7 @@ unsigned int ump_log_read(unsigned int address)
unsigned long first;
switch (address) {
case UMP_LOG_BASE: return 0x554D;
case UMP_LOG_BASE+1: return 2;
case UMP_LOG_BASE+1: return 3;
case UMP_LOG_BASE+2: return ump_source ? 3 : ump_state;
case UMP_LOG_BASE+3: return ump_source ? ump_archive_count : ump_count;
case UMP_LOG_BASE+4: return UMP_LOG_WORDS;
@@ -250,7 +264,7 @@ unsigned int ump_log_read(unsigned int address)
/* Ïîñëåäíèå òðè çàïèñè äëÿ îíëàéí-ãðàôèêà, áåç îñòàíîâêè è áåç
* èçìåíåíèÿ êóðñîðà àðõèâíîãî ÷òåíèÿ. Ïåðâûå äâà ñëîâà — èõ íîìåð. */
if (address >= UMP_LOG_LIVE && address < UMP_LOG_LIVE+124) {
count = ump_count < 3 ? ump_count : 3;
count = ump_count < UMP_LOG_LIVE_RECORDS ? ump_count : UMP_LOG_LIVE_RECORDS;
first = ump_sequence - count;
offset = address - UMP_LOG_LIVE;
if (offset == 0) return (unsigned int)first;

View File

@@ -20,12 +20,23 @@ __declspec(dllexport) int run_tests(void)
modbus[127] = 0x80;
modbus[0x50] = 6; modbus[0x51] = 16;
modbus[0x7B] = 1234; modbus[0x1C] = 160;
/* PM67 reception is independent of recording; all three CAN slots work. */
ump_log_can(0x8000U | 305U, 0xFEDCU, 0, 0);
ump_log_can(0x4000U | 7U, 0, 456, 0);
CHECK(ump_pm67_voltage[0] == 0xFEDCU && ump_pm67_voltage[1] == 456);
ump_log_can(305U, 1, 2, 3); /* Masked-out word must not overwrite. */
ump_log_can(0x2000U | 303U, 0, 0, 123);
ump_log_can(0x2000U | 6U, 0, 0, 789);
CHECK(ump_pm67_voltage[0] == 123 && ump_pm67_voltage[1] == 789);
ump_log_can(0x8000U | 306U, 999, 0, 0); /* Not a one-based CAN address. */
CHECK(ump_log_write(UMP_LOG_BASE, 1));
CHECK(ump_word(0, 0, 40) == 123 && ump_word(0, 0, 41) == 789);
CHECK(2UL * UMP_LOG_CAPACITY * UMP_LOG_WORDS <= LOG_PAGE_LEN);
CHECK(!ump_log_write(UMP_LOG_BASE, 1));
CHECK(!ump_log_write(UMP_LOG_BASE+1, 20));
for (i=0; i<900; i++) ump_log_tick();
CHECK(ump_count == UMP_LOG_CAPACITY);
CHECK(ump_word(0, 0, 0) == 101);
CHECK(ump_word(0, 0, 0) == 901-UMP_LOG_CAPACITY);
CHECK(ump_word(0, UMP_LOG_CAPACITY-1, 0) == 900);
CHECK(ump_word(0, 0, 8) == 1234);
CHECK(ump_word(0, 0, 24) == 6);
@@ -43,6 +54,11 @@ __declspec(dllexport) int run_tests(void)
CHECK(ump_word(1, UMP_LOG_CAPACITY/2-1, 2) == 0x102);
CHECK(ump_word(1, UMP_LOG_CAPACITY-1, 2) == 0x205);
CHECK(ump_count == 0 && ump_state == 1);
CHECK(ump_word(1, UMP_LOG_CAPACITY-1, 40) == 123);
CHECK(ump_word(1, UMP_LOG_CAPACITY-1, 41) == 789);
ump_log_can(0x8000U | 305U, 999, 0, 0);
CHECK(ump_word(0, ump_count-1, 40) == 999);
CHECK(ump_word(1, UMP_LOG_CAPACITY-1, 40) == 123);
generation = ump_generation;
archived = ump_word(1, UMP_LOG_CAPACITY-1, 0);
sequence = ump_sequence;
@@ -50,8 +66,8 @@ __declspec(dllexport) int run_tests(void)
CHECK(ump_word(1, UMP_LOG_CAPACITY-1, 0) == archived);
CHECK(ump_generation == generation);
CHECK(ump_sequence == sequence + 200);
CHECK(ump_log_read(UMP_LOG_LIVE+2) == 3);
CHECK(ump_log_read(UMP_LOG_LIVE+4) == ump_time-2);
CHECK(ump_log_read(UMP_LOG_LIVE+2) == UMP_LOG_LIVE_RECORDS);
CHECK(ump_log_read(UMP_LOG_LIVE+4) == ump_time-(UMP_LOG_LIVE_RECORDS-1));
CHECK(ump_log_write(UMP_LOG_BASE+3, 1));
CHECK(ump_log_write(UMP_LOG_BASE+2, UMP_LOG_CAPACITY-1));
CHECK(ump_log_read(UMP_LOG_DATA) == archived);
@@ -72,9 +88,15 @@ __declspec(dllexport) int run_tests(void)
/* CAN читает те же регистры и формирует цельный снимок из 124 слов.
* Запись по старым технологическим адресам через сервис запрещена. */
length = ump_can_prepare(3, UMP_LOG_BASE, 16, reply, &error);
CHECK(!error && length == 16 && reply[0] == 0x554D && reply[1] == 2);
CHECK(!error && length == 16 && reply[0] == 0x554D && reply[1] == 3);
length = ump_can_prepare(3, UMP_LOG_LIVE, 124, reply, &error);
CHECK(!error && length == 124);
CHECK(reply[2] <= UMP_LOG_LIVE_RECORDS);
for (i=4+reply[2]*UMP_LOG_WORDS; i<124; i++) CHECK(reply[i] == 0);
length = ump_can_prepare(3, UMP_LOG_DATA, 84, reply, &error);
CHECK(!error && length == 84);
length = ump_can_prepare(3, UMP_LOG_DATA, 42, reply, &error);
CHECK(!error && length == 42);
ump_can_prepare(3, UMP_LOG_LIVE, 126, reply, &error);
CHECK(error == 2);
ump_can_prepare(6, 127, 0x80, reply, &error);