SETCAN
руководство разработчика
+ Карта библиотеки ProtoCAN, формат кадров и практический маршрут переноса в существующую прошивку STM32.
+Что находится в проекте
+SETCAN — не готовый CubeIDE-проект, а подключаемый C-модуль поверх STM32 HAL. Его задача — фильтровать, принимать, разбирать и отправлять прикладные сообщения ProtoCAN.
+Что ожидает модуль
main.hиcan.hиз CubeMX- STM32 HAL CAN, RTC и TIM
- CAN FIFO0 и Extended ID
- STM32 GCC ABI для C bit-fields
Путь кадра
IRQ вычитывает FIFO0. Pulse обрабатывается сразу, остальные Extended-кадры попадают в кольцевой буфер и разбираются в основном цикле.
Единая точка отправки
PROTOCAN_SEND() выбирает упаковщик по MsgType. GAS и Modbus автоматически разбиваются на кадры до 8 байт.
Что приложение реализует само
Прикладные действия, хранение SETTINGS, EEPROM, DS18B20, таймаут online/offline и полноценный bootloader не входят в ядро.
Состав библиотеки
Два файла образуют один модуль. Заголовок — контракт интеграции; C-файл — транспортная реализация и демонстрационные слабые обработчики.
+protocan.h
Конфигурация текущего устройства, размеры буфера, enum типов сообщений, битовые представления ID/Body, структуры TX/RX и публичные функции.
Меняют при переносе: CURRENT_TYPE_DEVICE, CURRENT_ID_DEVICE, иногда PROTOCAN_RX_BUFFER_SIZE.
protocan.c
Инициализация, фильтры, кольцевой буфер, диспетчеризация, RTC sync, pulse и упаковка кадров.
Обычно не правят: прикладную логику лучше добавлять переопределением __weak-функций.
Внешний слой
CAN_HandleTypeDef, RTC_HandleTypeDef и TIM_HandleTypeDef передаются в PROTOCAN_INIT(). Запуск периферии выполняет приложение.
Основной API
| Функция | Назначение | Где вызывать |
|---|---|---|
| PROTOCAN_INIT | Сохраняет HAL handles, настраивает фильтры и callback’и при доступной регистрации. | После MX_*_Init(), до HAL_CAN_Start(). |
| PROTOCAN_ProcessAllRxMsgs | Обрабатывает всю накопленную RX-очередь и возвращает первую ошибку. | В основном цикле. |
| PROTOCAN_ProcessSingleRxMsg | Обрабатывает максимум один кадр. | В планировщике или ограниченном временном слоте. |
| PROTOCAN_SEND | Упаковывает и отправляет сообщение выбранного типа. | Из логики приложения, не удерживая изменяемые данные. |
| ProtoCanRxFifo0MsgPendingCallback | Забирает кадры из CAN FIFO0. | Из HAL callback, если авто-регистрация выключена. |
| ProtoCanPulseCallback | Отправляет периодический pulse со счётчиком. | Из callback нужного базового таймера. |
| PROTOCAN_SEND_SETTINGS_RESPONSE / ERROR | Формирует успешный либо ошибочный ответ привязки ROM. | Из пользовательского обработчика SETTINGS. |
Точки расширения
Переопределите нужную __weak-функцию в собственном C-файле: семейства ProtoCanMsgToBroadcast…, …Discrete…, …Analog…, ProtoCanMsgToSettings, ProtoCanMsgToGeneralAddressSpace и …Modbus…. Не редактируйте демонстрационную реализацию в ядре — так обновлять библиотеку проще.
Формат ProtoCAN
Протокол использует только CAN 2.0B Extended ID. Адрес устройства — пара DeviceType/DeviceID; назначение младших 16 бит зависит от типа сообщения.
29-битный идентификатор
28 27 26··24 23··20 19··16 15········0
+Priority Route DeviceType DeviceID MsgType MsgBody
+1 бит 1 бит 3 бита 4 бита 4 бита 16 битРеестр MsgType
| Код | Тип | Назначение | Статус |
|---|---|---|---|
| 0x0 | BROADCAST | Общие команды всем устройствам | stable |
| 0x1 | DISCRETE | Состояния, команды, флаги | stable |
| 0x2 | ANALOG | Датчики U / I / T и универсальные данные | stable |
| 0x3 | GAS | Общее 16-битное адресное пространство | stable |
| 0x4…0x7 | MODBUS | Coil, Discrete, Holding, Input | stable |
| 0x8 | ERROR | Код ошибки и дополнительная информация | stable |
| 0x9…0xD | BOOT | Управление, блоки A/B, статус, discovery | draft |
| 0xE | SETTINGS | Привязка 1-Wire ROM к локации Z/Y | stable |
| 0xF | PULSE | Присутствие устройства | stable |
Как портировать в свой STM32-проект
Рабочая последовательность для CubeMX/CubeIDE. Имена hcan, hrtc и htim2 замените на handles своего проекта.
Добавьте исходники
Inc/protocan.hв include pathSrc/protocan.cв сборку- Проверьте доступность
main.hиcan.h
Настройте адрес
В protocan.h задайте тип 0…7 и ID 0…15. Пара должна быть уникальной на шине.
#define CURRENT_TYPE_DEVICE 0b011
+#define CURRENT_ID_DEVICE 0b0101Настройте CubeMX
- CAN: Extended ID, FIFO0
- RTC: если нужна синхронизация времени
- TIM: период pulse
- Проверьте filter banks 0, 1, 2 и границу 14
Инициализация и основной цикл
#include "protocan.h"
+
+int main(void)
+{
+ HAL_Init();
+ SystemClock_Config();
+
+ MX_GPIO_Init();
+ MX_CAN_Init();
+ MX_RTC_Init();
+ MX_TIM2_Init();
+
+ if (PROTOCAN_INIT(&hcan, &hrtc, &htim2) != PROTOCAN_INIT_OK) {
+ Error_Handler();
+ }
+ if (HAL_CAN_Start(&hcan) != HAL_OK) {
+ Error_Handler();
+ }
+ if (HAL_CAN_ActivateNotification(
+ &hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
+ Error_Handler();
+ }
+ if (HAL_TIM_Base_Start_IT(&htim2) != HAL_OK) {
+ Error_Handler();
+ }
+
+ while (1) {
+ (void)PROTOCAN_ProcessAllRxMsgs();
+ }
+}Свяжите callback’и при отключённой регистрации HAL
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan_ptr)
+{
+ if (hcan_ptr == &hcan) {
+ ProtoCanRxFifo0MsgPendingCallback(hcan_ptr);
+ }
+}
+
+void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim_ptr)
+{
+ if (htim_ptr == &htim2) {
+ ProtoCanPulseCallback(htim_ptr);
+ }
+}Не дублируйте события
Если USE_HAL_*_REGISTER_CALLBACKS == 1, библиотека регистрирует callback’и сама. Не вызывайте те же функции ещё раз из стандартных callback’ов.
Перенесите прикладную логику
Создайте, например, protocan_app.c и определите там только нужные weak handlers. Так ядро остаётся обновляемым.
#include "protocan.h"
+
+PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogTSens(struct RXMsg msg)
+{
+ msgBodyAnalogType body = {0};
+ body.Body = msg.eID.Fields.MsgBody;
+
+ uint16_t sensor_id = body.Fields.SensorID;
+ /* Получить температуру sensor_id и сформировать ответ. */
+
+ return PROTOCAN_OK;
+}Проверка после переноса
- Проект собирается без повторных определений HAL callback’ов.
- CAN стартует и принимает только Extended ID.
- Pulse появляется с периодом выбранного таймера.
- Эталонный ID
0x13590702декодируется как DeviceType=3, DeviceID=5, MsgType=9, Body=0x0702. - При нагрузке учтено, что буфер размера 128 хранит максимум 127 кадров.
Готовые шаблоны
Минимальные примеры формирования исходящего кадра и обработчика SETTINGS. Их можно вынести в прикладной слой проекта.
+Отправка трёх регистров GAS
uint16_t registers[] = { 0x1234, 0x5678, 0x9ABC };
+
+ProtoCanId_t id = {0};
+id.Fields.Priority = PROTOCAN_PRIORITY_STANDARD;
+id.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE;
+id.Fields.DeviceType = CURRENT_TYPE_DEVICE;
+id.Fields.DeviceID = CURRENT_ID_DEVICE;
+id.Fields.MsgType = PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE;
+
+ProtoCanData_t tx = {0};
+tx.GeneralAddressSpaceData.RegStartAdr = 100;
+tx.GeneralAddressSpaceData.Data = registers;
+tx.GeneralAddressSpaceData.RegCount = 3;
+
+if (PROTOCAN_SEND(id, tx) != PROTOCAN_OK) {
+ /* CAN занят или HAL вернул ошибку. */
+}SETTINGS: прикладное хранение ROM
PROTOCAN_StatusTypeDef ProtoCanMsgToSettings(
+ const ProtoCanSettingsMsg_t *message)
+{
+ uint8_t current_rom[PROTOCAN_SETTINGS_ROM_SIZE] = {0};
+
+ switch (message->Operation) {
+ case PROTOCAN_SETTINGS_GET:
+ /* Загрузить ROM из EEPROM в current_rom. */
+ return PROTOCAN_SEND_SETTINGS_RESPONSE(
+ message->AssemblySerial, message->Position, current_rom);
+
+ case PROTOCAN_SETTINGS_WRITE:
+ /* Проверить CRC/наличие и атомарно записать message->Rom. */
+ return PROTOCAN_SEND_SETTINGS_RESPONSE(
+ message->AssemblySerial, message->Position, message->Rom);
+
+ case PROTOCAN_SETTINGS_CLEAR:
+ /* Очистить запись; current_rom должен содержать нули. */
+ return PROTOCAN_SEND_SETTINGS_RESPONSE(
+ message->AssemblySerial, message->Position, current_rom);
+
+ default:
+ return PROTOCAN_SEND_SETTINGS_ERROR(
+ message->AssemblySerial, message->Position,
+ PROTOCAN_SETTINGS_RESULT_INVALID_DLC);
+ }
+}