C-сценарий — программа с точкой входа int main(void), которую контроллер исполняет непосредственно на процессоре Arm Cortex-M4. Компиляция выполняется на компьютере с telematic_c_compiler.exe или на сервере.

Компиляция и запуск

По умолчанию контроллер подключается к серверу и использует серверную компиляцию. Это наиболее удобный и предпочтительный вариант: отдельное приложение на компьютере запускать не требуется.

Кнопка Компиляция получает исполняемый образ без запуска. Кнопка Старт при необходимости компилирует программу и запускает её. Ошибки компиляции показываются в интерфейсе; ошибки выполнения и сообщения API доступны в консоли сценария и /txt?log.

Для локальной компиляции запустите telematic_c_compiler.exe, разрешите входящие подключения в брандмауэре и укажите в настройках контроллера адрес компьютера и порт приложения. Порт по умолчанию — 8089, его можно изменить при запуске приложения.

При серверной компиляции между контроллером и сервером создаётся шифрованный туннель; C-сценарии передаются по нему. Локальное приложение telematic_c_compiler.exe предназначено для работы в ЛВС: соединение контроллера с ним не шифруется.

Заголовки dev.h, display.h, modbus.h, tools.h и адреса функций зависят от прошивки. Компилятор получает их от контроллера и хранит отдельно для каждой контрольной суммы прошивки. После обновления прошивки сценарий следует перекомпилировать. Бинарник проверяется по модели устройства, версии формата, контрольной сумме прошивки и CRC.

Язык и библиотеки

Компилятор сценариев — GCC GNU Arm Embedded Toolchain 10.3.1. Применяются C99, Thumb, Cortex-M4, fpv4-sp-d16, ABI с аппаратной передачей аргументов с плавающей точкой, unsigned char, короткие enum и 16-битный wchar_t.

В этой конфигурации int, long, указатель и size_t занимают 32 бита, long long — 64 бита, float — 32 бита, double и long double — 64 бита. Аппаратный FPU выполняет операции одинарной точности; для double и части целочисленных операций вызываются runtime-функции прошивки.

Доступен ограниченный набор функций из stdlib.h, stdio.h, string.h, time.h, math.h и ctype.h. Наличие стандартного имени заголовка не означает, что доступна вся библиотека настольного C: файловый ввод-вывод, stdin и настройка locale в этом API не предоставлены.

Прикладные заголовки: dev.h — ресурсы и планировщик, display.h — дисплей, modbus.h — Modbus RTU Master, tools.h — время, преобразование диапазонов и PID.

Простейшая программа

  #include <dev.h>
#include <stdio.h>

int main(void) {
    printf("Registers: %d\n", countGet(devReg));
    return 0;
}
  

Функции API, возвращающие int, обычно сообщают об ошибке отрицательным кодом. Точная семантика приведена у каждой функции: например, некоторые setter возвращают 1, если значение не изменилось, а runAdd() возвращает -1 сразу для нескольких причин отказа.

Память и завершение

C-сценарий выполняется в задаче контроллера. Динамические выделения malloc/calloc/realloc обслуживаются отдельной кучей размером около 8 Кбайт; размер программы и её данных ограничен общей областью загрузки. В текущей прошивке для X0 исходник stdin ограничен 7168 байтами, область бинарника — 62 Кбайтами с учётом служебного заголовка. Значения зависят от прошивки; /json?static публикует доступные размеры stdin/stdout.

Возврат из main() и exit(status) завершают сценарий и вызывают зарегистрированные atexit() обработчики в обратном порядке. Ненулевой status означает ошибку. abort() завершает сценарий с ошибкой без обработчиков; штатный вызов abort не является HardFault и не перезагружает контроллер. Принудительная внешняя остановка может удалить задачу без выполнения обработчиков.

Инициализация переменных

Перед каждым запуском C-сценария глобальные и статические переменные без инициализатора, а также переменные с нулевым инициализатором автоматически обнуляются. Это относится и к повторному запуску того же сценария: значения от предыдущего выполнения не сохраняются. Массивы и структуры из этой области также заполняются нулями.

Исправление действует после обновления прошивки контроллера. В старых версиях эти переменные могли сохранять произвольные данные; явное static int counter = 0; не устраняло ошибку загрузки. Если нужна совместимость со старой прошивкой, задавайте начальные значения в main до использования.

  #include <dev.h>
#include <stdio.h>

static unsigned int counter;

static int step(uint32_t count) {
    counter++;
    printf("Step %u\n", counter);
    return count >= 2 ? -1 : 0;
}

int main(void) {
    return run(step, 100);
}
  

Диагностика

C-код работает с памятью и оборудованием без изоляции виртуальной машиной. Выход за границу массива, неверный указатель или повреждение стека могут вызвать HardFault всей прошивки. В случае таких ошибок контроллер перезапускается посредством watchdog с последующей активацией системной аварии. Сохранённые сведения о HardFault и stackOverflow доступны через GET /txt?sys.

Проверка компиляции выявляет ошибки синтаксиса и недоступные символы, но не подтверждает безопасное выполнение. Примеры, изменяющие Do, Ao, конфигурацию или параметры Modbus, выполняйте на подходящем стенде.