Особенности
Особенности использования C на контроллере
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, выполняйте на подходящем стенде.