Перенести настройки выпуска в 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
|