Фронты START/STOP обрабатываются при принятии командного слова CAN/RS, а окончание импульса обслуживается таймером. STOP имеет приоритет, перекрытие выходов исключено, сохранена защита от самопуска при включении. Загрузка параметров больше не подменяет живые команды нагрузки. Добавлены подробные русские комментарии во все собственные C-модули и основные заголовки, описание поведения и host-тест автомата. Обновлены файлы прошивки. Полная сборка TI 22.6.1.LTS и тесты пройдены; проверка на физической плате не выполнялась.
70 lines
2.6 KiB
C
70 lines
2.6 KiB
C
/* Управление импульсами СИФУ. Чистый автомат без доступа к GPIO.
|
||
* Один и тот же код используется прошивкой и host-тестом.
|
||
* ВАЖНО: вызывающая сторона сериализует init/accept/tick запретом IRQ.
|
||
* volatile обеспечивает обращения к общему состоянию, но не заменяет
|
||
* критическую секцию: обновление нескольких полей не является атомарным.
|
||
*/
|
||
#ifndef UMP_PULSE_H
|
||
#define UMP_PULSE_H
|
||
#define UMP_START_MASK 0x0080U
|
||
#define UMP_STOP_MASK 0x0040U
|
||
#define UMP_PULSE_NONE 0U
|
||
#define UMP_PULSE_START 1U
|
||
#define UMP_PULSE_STOP 2U
|
||
|
||
typedef struct {
|
||
unsigned int previous;
|
||
unsigned int remaining;
|
||
unsigned int output;
|
||
unsigned int relay;
|
||
} UMP_PULSE_STATE;
|
||
|
||
static void ump_pulse_init(volatile UMP_PULSE_STATE *s, unsigned int commands)
|
||
{
|
||
/* Уже активная команда при окончании загрузки не является новым пуском.
|
||
* Снимаем физические выходы отдельно в аппаратной обёртке.
|
||
* Если начальное слово нулевое, первая свежая единица будет принята:
|
||
* больше не нужен случайный проход main, успевший прочитать ноль. */
|
||
s->previous = commands;
|
||
s->remaining = 0;
|
||
s->output = UMP_PULSE_NONE;
|
||
s->relay = 0;
|
||
}
|
||
|
||
static void ump_pulse_accept(volatile UMP_PULSE_STATE *s,
|
||
unsigned int commands, unsigned int ticks)
|
||
{
|
||
unsigned int rising = commands & ~s->previous;
|
||
/* История относится к каждому принятому слову CAN/RS, а не к редким
|
||
* снимкам main. Поэтому даже 1->0->1 между проходами main замечается.
|
||
* Повторные пакеты с удерживаемой единицей не продлевают импульс. */
|
||
s->previous = commands;
|
||
if (commands & UMP_STOP_MASK) {
|
||
/* STOP имеет приоритет даже при одновременном фронте START.
|
||
* Его удержание запрещает новый START. Запрещённый пуск не ставим
|
||
* в очередь: снятие STOP не должно неожиданно запускать СИФУ. */
|
||
if (rising & UMP_STOP_MASK) {
|
||
s->output = UMP_PULSE_STOP;
|
||
s->remaining = ticks;
|
||
s->relay = 0;
|
||
}
|
||
} else if (rising & UMP_START_MASK) {
|
||
/* Новый START после снятия команды STOP переключает выходы.
|
||
* Новый фронт во время START продлевает существующий импульс,
|
||
* как раньше; отдельный электрический фронт здесь не обещается. */
|
||
s->output = UMP_PULSE_START;
|
||
s->remaining = ticks;
|
||
s->relay = 1;
|
||
}
|
||
}
|
||
|
||
static void ump_pulse_tick(volatile UMP_PULSE_STATE *s)
|
||
{
|
||
/* Единственный владелец отсчёта — таймер 1 кГц. main не участвует
|
||
* в завершении импульса, поэтому EEPROM не растягивает его.
|
||
* RES_OUT_2 сохраняет последнее состояние после конца импульса. */
|
||
if (s->remaining && --s->remaining == 0)
|
||
s->output = UMP_PULSE_NONE;
|
||
}
|
||
#endif
|