diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..e827ec3 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,317 @@ + + + + + + + SETCAN · Руководство разработчика + + + +
+
+
STM32 · Classic CAN 2.0B · HAL
+

SETCAN
руководство разработчика

+

Карта библиотеки ProtoCAN, формат кадров и практический маршрут переноса в существующую прошивку STM32.

+
+ 29 бит Extended ID0…8 байт payload128 адресов устройств127 кадров в RX-очереди +
+
+ +
+ +
+ +
+

Что находится в проекте

+

SETCAN — не готовый CubeIDE-проект, а подключаемый C-модуль поверх STM32 HAL. Его задача — фильтровать, принимать, разбирать и отправлять прикладные сообщения ProtoCAN.

+
+
Структура

Минимальное ядро + нормативные документы

SETCAN/ +├── Inc/protocan.h публичные типы, настройки и API +├── Src/protocan.c фильтры, RX-очередь, разбор и отправка +├── docs/protocan/ спецификация протокола +│ ├── PROTOCOL.md 29-битный ID и типы сообщений +│ ├── OAP.md общее адресное пространство +│ ├── BOOTLOADER.md обновление прошивки по CAN (draft) +│ └── examples/ эталонные кадры JSON +├── Протокол CAN и ОАП.xlsx редактируемый реестр ОАП +└── doc/index.html эта страница
+
Зависимости

Что ожидает модуль

  • 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 не входят в ядро.

+
+
+
01 · FIFO0 IRQHAL сообщает о новом кадре
02 · RX bufferExtended ID помещается в очередь
03 · DispatcherРазбор по MsgType
04 · Weak handlerЛогика приложения
+
+
+ +
+

Состав библиотеки

Два файла образуют один модуль. Заголовок — контракт интеграции; C-файл — транспортная реализация и демонстрационные слабые обработчики.

+
+
Inc

protocan.h

Конфигурация текущего устройства, размеры буфера, enum типов сообщений, битовые представления ID/Body, структуры TX/RX и публичные функции.

Меняют при переносе: CURRENT_TYPE_DEVICE, CURRENT_ID_DEVICE, иногда PROTOCAN_RX_BUFFER_SIZE.

+
Src

protocan.c

Инициализация, фильтры, кольцевой буфер, диспетчеризация, RTC sync, pulse и упаковка кадров.

Обычно не правят: прикладную логику лучше добавлять переопределением __weak-функций.

+
HAL

Внешний слой

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 бит
Многобайтовые значения payload передаются little-endian. Разметка ID через C bit-fields требует проверки при смене компилятора или ABI.
+
+

Реестр MsgType

+ +
КодТипНазначениеСтатус
0x0BROADCASTОбщие команды всем устройствамstable
0x1DISCRETEСостояния, команды, флагиstable
0x2ANALOGДатчики U / I / T и универсальные данныеstable
0x3GASОбщее 16-битное адресное пространствоstable
0x4…0x7MODBUSCoil, Discrete, Holding, Inputstable
0x8ERRORКод ошибки и дополнительная информацияstable
0x9…0xDBOOTУправление, блоки A/B, статус, discoverydraft
0xESETTINGSПривязка 1-Wire ROM к локации Z/Ystable
0xFPULSEПрисутствие устройстваstable
+
+
+ +
+

Как портировать в свой STM32-проект

Рабочая последовательность для CubeMX/CubeIDE. Имена hcan, hrtc и htim2 замените на handles своего проекта.

+
+
Шаг 1

Добавьте исходники

  • Inc/protocan.h в include path
  • Src/protocan.c в сборку
  • Проверьте доступность main.h и can.h
+
Шаг 2

Настройте адрес

В protocan.h задайте тип 0…7 и ID 0…15. Пара должна быть уникальной на шине.

#define CURRENT_TYPE_DEVICE 0b011
+#define CURRENT_ID_DEVICE   0b0101
+
Шаг 3

Настройте CubeMX

  • CAN: Extended ID, FIFO0
  • RTC: если нужна синхронизация времени
  • TIM: период pulse
  • Проверьте filter banks 0, 1, 2 и границу 14
+
Шаг 4

Инициализация и основной цикл

#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();
+    }
+}
+
Шаг 5

Свяжите 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’ов.

+
Шаг 6

Перенесите прикладную логику

Создайте, например, 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);
+    }
+}
+
+ +
+ + +
+ + +