Перенести настройки выпуска в JSON, добавить переносимый сценарий сборки и проверки BIN. Обновить конфигурации CCS, исходники в UTF-8, обработку TESTLAMP, образы прошивки и регрессионные проверки.
70 lines
3.6 KiB
C
70 lines
3.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
|