Расширить обмен по шинам, CAN-адаптеры и обработку графиков
Добавить очереди операций PBUS/SBUS, общую консоль, настройки Candle и SLCAN, декодеры DSView и обработку плотных графиков. Обновить протокол Бальзам и проверки общих компонентов.
This commit is contained in:
@@ -6,6 +6,16 @@
|
||||
`include/balsam167_eeprom_layout.h`; PEEK, CRC и порядок байтов реализованы
|
||||
в `src/periph28335.c`. Проверка полного диапазона — `test_periph28335_eeprom`.
|
||||
|
||||
Декодер `balsam_can` знает служебные слова heartbeat периферии Бальзам-167:
|
||||
`0x0100` — счётчик секунд по модулю 65536, `0x0101` — аппаратный Mode,
|
||||
`0x0102` — код типа платы Desk. Назначения соответствуют `CAN_HEARTBEAT_ADDR`
|
||||
и ветке `CAN_send` в `Balsam_167_periph/Source/Internal/ecan.c`.
|
||||
Python-адаптер `set_devices.balsam_can` выделяет ответ с адресом `0x0100`
|
||||
и маской `111` как `BALZAM Heartbeat`, сохраняя исходные регистры и маршрут.
|
||||
Пример: `0x00BA0017`, `00 08 00 04 E1 00 00 5A` — heartbeat задатчика,
|
||||
90 секунд, Mode=8, Desk=4. Счётчик отображается без знака, в том числе
|
||||
при значениях `0x8000–0xFFFF`; обычные технологические int16 сохраняют знак.
|
||||
|
||||
Универсальные сервисы эмуляторов: [адресное пространство, API и порт новой платы](docs/EMULATOR_SERVICES.md).
|
||||
`set_regmap` объединяет УМП, 2812, IGBT и DAC за общим API для RTU, CAN и других оболочек.
|
||||
|
||||
|
||||
+5
-2
@@ -152,14 +152,17 @@ object BalsamCanProtocol {
|
||||
fun deviceName(device: Int): String = if (NativeSetProtocol.available) {
|
||||
NativeSetProtocol.nativeBalsamDeviceName(device)
|
||||
} else listOf(
|
||||
"Трансформатор 1", "Трансформатор 2", "Силовой блок 1", "Силовой блок 2",
|
||||
"УМП 1", "УМП 2", "Двигатель", "ВЭП", "Задатчик", "Узел 10", "Узел 11",
|
||||
"Трансформатор 1", "Трансформатор 2", "Силовой 1", "Силовой 2", // Имена узлов 1..4 повторяют таблицу адресов 167 и общее C-ядро.
|
||||
"УМП 1", "УМП 2", "Двигатель", "Задатчик", "ВЭП", "ПМУ", "Узел 11", // Узлы 5..10 соответствуют таблице, включая ПМУ на паре 0xBA0009/0xBA0019; узел 11 остаётся служебным.
|
||||
"Узел 12", "Терминал",
|
||||
).getOrElse(device - 1) { "Неизвестный узел" }
|
||||
|
||||
fun registerName(device: Int, address: Int): String =
|
||||
if (NativeSetProtocol.available) NativeSetProtocol.nativeBalsamRegisterName(device, address)
|
||||
else when {
|
||||
address == 0x0100 -> "Heartbeat: счётчик, с" // CAN_HEARTBEAT_ADDR из ecan.c содержит беззнаковый секундный счётчик по модулю 65536.
|
||||
address == 0x0101 -> "Heartbeat: Mode" // Второе слово служебного пакета передаёт аппаратный номер Mode платы.
|
||||
address == 0x0102 -> "Heartbeat: тип платы (Desk)" // Третье слово передаёт Desk, чтобы Android без JNI использовал названия общего C-ядра.
|
||||
device in 1..2 && address in 0x18..0x2B -> "Показания T° ${address - 0x17}"
|
||||
device in 3..4 && address in 0x18..0x27 -> "Показания T° ${address - 0x17}"
|
||||
device == 7 && address in 0x18..0x1F -> "Показания T° ${address - 0x17}"
|
||||
|
||||
@@ -61,7 +61,7 @@ int balsam_can_decode(uint32_t can_id, const uint8_t *data, size_t size,
|
||||
relative = can_id - BALSAM_CAN_BASE_ID;
|
||||
output->direction = relative >= BALSAM_CAN_DATA_OFFSET
|
||||
? BALSAM_CAN_FROM_NODE : BALSAM_CAN_TO_NODE;
|
||||
output->device = (uint8_t)((relative & 0x0FU) + 1U);
|
||||
output->device = (uint8_t)((relative & 0x0FU) + 1U); /* В ecan.c перед сборкой CAN ID выполняется DevNum--; маска 0x0F выделяет индекс 0..12, а +1 восстанавливает номер Mode. */
|
||||
if (can_id == 0x00745009UL || can_id == 0x00745019UL) {
|
||||
output->device = 10U;
|
||||
output->direction = can_id == 0x00745019UL
|
||||
@@ -80,9 +80,9 @@ int balsam_can_decode(uint32_t can_id, const uint8_t *data, size_t size,
|
||||
const char *balsam_can_device_name(uint8_t device)
|
||||
{
|
||||
static const char *const names[BALSAM_CAN_NODE_COUNT] = {
|
||||
"Трансформатор 1", "Трансформатор 2", "Силовой блок 1",
|
||||
"Силовой блок 2", "УМП 1", "УМП 2", "Двигатель", "ВЭП",
|
||||
"Задатчик", "Пульт (МПУ)", "Узел 11", "Узел 12", "Терминал"
|
||||
"Трансформатор 1", "Трансформатор 2", "Силовой 1", /* Первые три узла соответствуют строкам 1..3 предоставленной таблицы адресов 167. */
|
||||
"Силовой 2", "УМП 1", "УМП 2", "Двигатель", "Задатчик", /* Строки 4..8 таблицы; VPU1_CAN_DEVICE=8 соответствует задатчику, а не ВЭП. */
|
||||
"ВЭП", "ПМУ", "Узел 11", "Узел 12", "Терминал" /* Строка 9 — ВЭП, пара 0xBA0009/0xBA0019 — ПМУ; служебные узлы 11..13 сохраняют прежние назначения. */
|
||||
};
|
||||
return (device >= 1U && device <= BALSAM_CAN_NODE_COUNT)
|
||||
? names[device - 1U] : "Неизвестный узел";
|
||||
@@ -91,6 +91,13 @@ const char *balsam_can_device_name(uint8_t device)
|
||||
size_t balsam_can_register_name(uint8_t device, uint16_t address,
|
||||
char *output, size_t output_size)
|
||||
{
|
||||
if (address == 0x0100U) /* CAN_HEARTBEAT_ADDR в ecan.c периферии 167 задаёт служебный адрес вне технологической карты R0..R127. */
|
||||
return copy_name("Heartbeat: счётчик, с", output, output_size); /* Первое слово hiword содержит секунды после запуска по модулю 65536. */
|
||||
if (address == 0x0101U) /* Следующее слово heartbeat соответствует старшим 16 битам loword в CAN_send. */
|
||||
return copy_name("Heartbeat: Mode", output, output_size); /* Прошивка передаёт аппаратный Mode без уменьшения на единицу, применяемого только к CAN ID. */
|
||||
if (address == 0x0102U) /* Третье слово heartbeat соответствует младшим 16 битам loword. */
|
||||
return copy_name("Heartbeat: тип платы (Desk)", output, output_size); /* Значение Desk задаёт тип платы и отличается от номера Mode и номера CAN-узла. */
|
||||
|
||||
if ((device == 1U || device == 2U) && address < 20U)
|
||||
return indexed_name("Диагностика T°", address + 1U, output, output_size);
|
||||
if ((device == 1U || device == 2U) && address >= 0x18U && address <= 0x2BU)
|
||||
@@ -139,7 +146,7 @@ size_t balsam_can_register_name(uint8_t device, uint16_t address,
|
||||
if (device == 7U && address >= 0x18U && address <= 0x1FU)
|
||||
return indexed_name("Показания T°", address - 0x17U, output, output_size);
|
||||
|
||||
if (device == 8U) {
|
||||
if (device == 9U) { /* Карта напряжений принадлежит ВЭП с adr_SHKF=9; задатчик с номером 8 использует другую карту. */
|
||||
static const char *const vep_names[] = {
|
||||
"Диагностика 380В Ф1", "Диагностика 380В Ф2",
|
||||
"Диагностика 31В Ф1", "Диагностика 31В Ф2",
|
||||
@@ -164,15 +171,15 @@ size_t balsam_can_register_name(uint8_t device, uint16_t address,
|
||||
return copy_name("Дискретные датчики (14 бит)", output, output_size);
|
||||
}
|
||||
|
||||
if (device == 9U && address == 0U)
|
||||
if (device == 8U && address == 0U) /* У VPU1_CAN_DEVICE=8 слово 0 передаёт обороты ГЭД, а не диагностику ВЭП. */
|
||||
return copy_name("Обороты ГЭД, об/мин", output, output_size);
|
||||
if (device == 9U && address == 1U)
|
||||
if (device == 8U && address == 1U) /* Слово 1 той же платы задатчика содержит обороты гребного вала. */
|
||||
return copy_name("Обороты ГВ, об/мин", output, output_size);
|
||||
if (device == 9U && address == 2U)
|
||||
if (device == 8U && address == 2U) /* ADR_CAN_LAMPS_ON_VPU=2 задаёт регистр первой группы ламп задатчика. */
|
||||
return copy_name("Лампы (8 бит)", output, output_size);
|
||||
if (device == 9U && address == 3U)
|
||||
if (device == 8U && address == 3U) /* ADR_CAN_LAMPS_2_ON_VPU=3 задаёт регистр второй группы индикаторов задатчика. */
|
||||
return copy_name("Диоды (16 бит)", output, output_size);
|
||||
if (device == 9U && address == 0x10U)
|
||||
if (device == 8U && address == 0x10U) /* ADR_CAN_KEY_ON_VPU=16, то есть 0x10, задаёт слово кнопок VPU; адрес не перенумеровывается. */
|
||||
return copy_name("Кнопки (12 бит)", output, output_size);
|
||||
|
||||
if (address == 0x16U)
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
#include "set_plot.h"
|
||||
#include <math.h>
|
||||
|
||||
/* Allow sample-level X navigation in multi-million-point recordings. */
|
||||
#define MIN_VIEW_WIDTH (1.0 / 10000000)
|
||||
#define MIN_VIEW_SPAN 1e-12 /* Минимальная безразмерная доля полного диапазона по X и Y задаёт увеличение до 10^12 раз; запас относительно точности double сохраняет различимые границы нормированной области. */
|
||||
|
||||
static double clamp(double v, double lo, double hi) { return fmin(hi, fmax(lo, v)); }
|
||||
static int finite_values(const double *v, size_t n) {
|
||||
@@ -26,13 +25,13 @@ size_t set_plot_eval(uint32_t op, const double *v, size_t n, double *out, size_t
|
||||
cap < (op == SET_PLOT_TRANSFORM || op == SET_PLOT_LIMITS ? 4U : 1U)) return 0;
|
||||
if (op == SET_PLOT_TRANSFORM) {
|
||||
double w, h, fx, fy;
|
||||
if (!finite_values(v, 4) || v[2] < MIN_VIEW_WIDTH || v[2] > 1 ||
|
||||
v[3] < 1.0/128 || v[3] > 1 || v[0] < 0 || v[1] < 0 ||
|
||||
if (!finite_values(v, 4) || v[2] < MIN_VIEW_SPAN || v[2] > 1 || /* Проверяем конечность смещений и размеров, разрешая ширину до триллионной доли исходного диапазона X. */
|
||||
v[3] < MIN_VIEW_SPAN || v[3] > 1 || v[0] < 0 || v[1] < 0 || /* Такой же предел для Y сохраняет возможность дальнейшего перемещения и увеличения уже приближённой области. */
|
||||
v[0] + v[2] > 1.000000000001 || v[1] + v[3] > 1.000000000001) return 0;
|
||||
out[0] = v[0]; out[1] = v[1]; out[2] = v[2]; out[3] = v[3];
|
||||
if (!finite_values(v + 4, 6) || v[4] <= 0 || v[5] <= 0) return 4;
|
||||
w = clamp(v[2] / v[4], MIN_VIEW_WIDTH, 1);
|
||||
h = clamp(v[3] / v[5], 1.0/128, 1);
|
||||
w = clamp(v[2] / v[4], MIN_VIEW_SPAN, 1); /* Делим текущую долю X на положительный коэффициент приближения; ограничиваем результат между новым пределом и полным диапазоном. */
|
||||
h = clamp(v[3] / v[5], MIN_VIEW_SPAN, 1); /* Аналогично масштабируем Y, снимая прежнее ограничение 128 раз и сохраняя ненулевую высоту для координатных преобразований. */
|
||||
fx = clamp(v[8], 0, 1); fy = clamp(v[9], 0, 1);
|
||||
out[0] = clamp(v[0] + (v[2] - w) * fx - v[6] * w, 0, 1 - w);
|
||||
out[1] = clamp(v[1] + (v[3] - h) * fy - v[7] * h, 0, 1 - h);
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
{"name":"zoom_x_anchor","op":0,"input":[0,0,1,1,2,1,0,0,0.25,0.5],"output":[0.125,0,0.5,1]},
|
||||
{"name":"zoom_y_anchor","op":0,"input":[0,0,1,1,1,4,0,0,0.5,0.75],"output":[0,0.5625,1,0.25]},
|
||||
{"name":"pan_clamp","op":0,"input":[0.25,0.25,0.5,0.5,1,1,20,-20,0.5,0.5],"output":[0,0.5,0.5,0.5]},
|
||||
{"name":"zoom_limit","op":0,"input":[0,0,1,1,1000,1000,0,0,0.5,0.5],"output":[0.4995,0.49609375,0.001,0.0078125]},
|
||||
{"name":"zoom_limit","op":0,"input":[0,0,1,1,1e15,1e15,0,0,0.5,0.5],"output":[0.4999999999995,0.4999999999995,1e-12,1e-12]},
|
||||
{"name":"ignore_invalid_zoom","op":0,"input":[0.25,0.25,0.5,0.5,0,1,0,0,0.5,0.5],"output":[0.25,0.25,0.5,0.5]},
|
||||
{"name":"horizontal_spread","op":1,"input":[100,2,8],"output":[1]},
|
||||
{"name":"vertical_contract","op":1,"input":[2,-100,8],"output":[2]},
|
||||
|
||||
Reference in New Issue
Block a user