Добавлены локальные копии команд и обновлены файлы прошивки. Сохранены анализ слабых мест START/STOP и тесты воспроизведения ошибки ограничения ЦАП проекта 167.
11 KiB
Слабые места обработки 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. Он не относится к изменениям рассматриваемого коммита.
Порядок дальнейших действий
- Согласовать доступ к общим данным и чтение одного слова команд.
- Определить требования к удержанию команд, повторным фронтам и одновременным START/STOP.
- По этим требованиям выбрать фиксацию событий и управление выходами.
- Проверить на стенде короткую команду, смену START на STOP, оба фронта одновременно, повторную команду до завершения импульса и задержку основного цикла. Измерить длительность и перекрытие выходных импульсов.
Успешная сборка подтверждает собираемость перенесённого исправления, но не проверяет перечисленные временные сценарии.