Files
Balsam_166/Doc/START_STOP_weak_points.md
andrey 085b587167 Исправлена обработка фронтов START/STOP в 166
Добавлены локальные копии команд и обновлены файлы прошивки. Сохранены анализ слабых мест START/STOP и тесты воспроизведения ошибки ограничения ЦАП проекта 167.
2026-09-22 12:37:24 +03:00

11 KiB
Raw Blame History

Слабые места обработки START/STOP

Дата: 22.09.2026. Проверен код проекта 166 после переноса исправления из коммита 799e6ba проекта 167. Документ описывает результаты просмотра кода; сценарии на устройстве не воспроизводились. Предлагаемые ниже изменения не внесены.

Что исправлено переносом

В Source/Internal/main.c:166 команды копируются в локальные umpstart и umpstop. Проверка фронта и сохранение предыдущего состояния используют одну копию каждой команды за проход основного цикла.

Раньше команда читалась дважды. При обновлении из CAN-прерывания между проверкой и сохранением состояния переход 0 → 1 мог быть пропущен: проверка видела 0, а предыдущее состояние уже сохранялось как 1. Перенос устраняет эту ошибку на уровне алгоритма, но не обеспечивает накопление событий и не решает остальные вопросы доступа из прерываний.

1. Команда может целиком пройти между опросами

Код: Source/Internal/main.c:166, Source/Internal/ecan.c:211.

CAN-приём записывает текущее значение в modbus. Основной цикл проверяет только последнее состояние. Если до следующего опроса придут включение и выключение команды (0 → 1 → 0), цикл увидит 0 и не выдаст импульс. Локальная копия этот случай не закрывает.

Когда существенно: если отправитель не гарантирует достаточное время удержания команды или основной цикл задерживается.

Возможное исправление: фиксировать фронт при приёме команды и хранить событие до обработки. Передача и сброс такого события должны быть согласованы с прерыванием. Если важен каждый повторный фронт, одного флага недостаточно: нужен счётчик либо очередь. Альтернатива — протокол с подтверждением обработки.

2. START и STOP могут быть активны одновременно

Код: Source/Internal/main.c:169–183.

Для START и STOP используются два независимых if. Каждый включает свой выход, не снимая противоположный. Оба выхода сбрасываются только после обнуления общего IMPowse.

Возможны два сценария:

  • Одновременно обнаружены оба фронта: включаются оба выхода. Значение RES_OUT_2 в итоге соответствует STOP, поскольку его обработка идёт второй.
  • STOP приходит во время активного START: включается STOP, START остаётся включённым, общий таймер запускается заново. Импульс START удлиняется. Аналогичное перекрытие возможно при обратном порядке команд.

Возможное исправление: определить по требованиям СИФУ приоритет команд, допустимость перекрытия и поведение повторной команды. Затем реализовать переключение состояний либо отдельные таймеры. Выбирать приоритет STOP без подтверждённых требований к устройству нельзя.

3. Длительность импульса зависит от основного цикла

Код: Source/Internal/DAC.c:107, Source/Internal/main.c:183.

Счётчик IMPowse уменьшается в Load_runner(), вызываемом из обработчика таймера (Source/Internal/measure.c:137). Однако физические выходы сбрасываются в основном цикле, когда он обнаруживает нулевой счётчик.

Если цикл задержался, счётчик уже может быть равен нулю, а выход остаётся активным. Перед проверкой команд выполняются, в частности, сохранение и загрузка параметров, расчёт коэффициентов. Время их выполнения в этой проверке не измерялось. Сброс также расположен внутри условия Desk == dsk_LOAD; если допускается смена режима во время импульса, нужен явный сброс при выходе из режима.

Возможное исправление: завершать импульс в обработчике таймера либо аппаратно. При этом запуск и завершение должны быть синхронизированы, чтобы обработчик не снял только что запущенный импульс.

4. Данные, общие с прерываниями, не имеют volatile

Код:

  • Source/Internal/message.c:21 — int modbus[ANSWER_LEN+1].
  • Source/Internal/Include/message.h:31 — объявление массива.
  • Source/Internal/Include/package.h:58 — Modbus приводится к WORDE *.
  • Source/Internal/DAC.c:21 и Source/Internal/Include/DAC.h:6 — IMPowse.

Команды изменяются в CAN-прерывании и читаются основным циклом. IMPowse записывается основным циклом и уменьшается обработчиком таймера. Обычные обращения к памяти не описывают компилятору асинхронное изменение этих объектов. Конкретное проявление зависит от оптимизации и машинного кода; сбой из-за этого в ходе проверки не подтверждён.

Возможное исправление: согласовать volatile в определениях, объявлениях и указателях доступа. Изменение только массива недостаточно, если макрос Modbus продолжит обращаться через WORDE * без volatile. Потребуется проверить и функции, которым передаётся массив.

volatile не делает последовательность операций атомарной и не заменяет синхронизацию. Для общего счётчика отдельно нужно проверить машинные инструкции доступа и допустимые точки прерывания.

5. START и STOP не образуют согласованный снимок

Код: Source/Internal/main.c:166–167, Source/Internal/Include/package.h:134.

Обе команды находятся в одном слове Commands, но читаются двумя выражениями. Если CAN обновит слово между ними, umpstart может относиться к старому состоянию, а umpstop — к новому. Например, при смене START на STOP локальные копии могут содержать две единицы, хотя ни одно полученное слово не содержало их одновременно. Фактическое число чтений памяти зависит от сгенерированного кода.

Возможное исправление: один раз прочитать всё слово команд через корректный volatile-доступ, затем выделить биты START и STOP из локальной копии. Это согласует снимок, но не решает потерю коротких событий.

Дополнительное замечание только к проекту 167

В ../167/Source/Internal/DAC.c:139 обнаружена вероятная опечатка:

if(dac_stop > 16) dac_go = 16;

По соседним ограничениям ожидается присваивание dac_stop = 16. Сейчас превышение конечного значения меняет начальное, а следующая проверка может дополнительно изменить конечное значение.

Этот участок отсутствует в текущем коде 166 и не перенесён вместе с 799e6ba. Он не относится к изменениям рассматриваемого коммита.

Порядок дальнейших действий

  1. Согласовать доступ к общим данным и чтение одного слова команд.
  2. Определить требования к удержанию команд, повторным фронтам и одновременным START/STOP.
  3. По этим требованиям выбрать фиксацию событий и управление выходами.
  4. Проверить на стенде короткую команду, смену START на STOP, оба фронта одновременно, повторную команду до завершения импульса и задержку основного цикла. Измерить длительность и перекрытие выходных импульсов.

Успешная сборка подтверждает собираемость перенесённого исправления, но не проверяет перечисленные временные сценарии.