diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..1a81dc7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "third_party/templates"] + path = third_party/templates + url = https://git.rd12.ru/Andrey/templates.git diff --git a/Inc/protocan.h b/Inc/protocan.h index b5e94e9..f7a4b81 100644 --- a/Inc/protocan.h +++ b/Inc/protocan.h @@ -4,6 +4,7 @@ #define _protocan #include "main.h" #include "can.h" +#include "../third_party/templates/c/set-protocol/include/pcan_id.h" #define _DEMO @@ -259,6 +260,38 @@ typedef union{ unsigned int BitAll:29; /**< Общее представление 29-битного идентификатора. */ } ProtoCanId_t; +/** + * Переходная граница legacy API -> переносимый формат линии. + * + * Прикладной код SETCAN пока может заполнять исторические Fields, но CAN ID + * всегда собирается и разбирается общим pcan_id без зависимости от порядка + * битовых полей конкретного компилятора. + */ +static inline uint32_t PROTOCAN_IdPack(const ProtoCanId_t *legacy) +{ + pcan_id_t shared; + shared.msg_body = (uint16_t)legacy->Fields.MsgBody; + shared.msg_type = (uint8_t)legacy->Fields.MsgType; + shared.device_id = (uint8_t)legacy->Fields.DeviceID; + shared.device_type = (uint8_t)legacy->Fields.DeviceType; + shared.route = (uint8_t)legacy->Fields.Route; + shared.priority = (uint8_t)legacy->Fields.Priority; + return pcan_id_pack(&shared); +} + +static inline void PROTOCAN_IdUnpack(uint32_t raw, ProtoCanId_t *legacy) +{ + pcan_id_t shared; + pcan_id_unpack(raw, &shared); + legacy->BitAll = 0U; + legacy->Fields.MsgBody = shared.msg_body; + legacy->Fields.MsgType = (ProtoCanMsgType)shared.msg_type; + legacy->Fields.DeviceID = shared.device_id; + legacy->Fields.DeviceType = shared.device_type; + legacy->Fields.Route = (ProtoCanRouteType)shared.route; + legacy->Fields.Priority = (ProtoCanPriorityType)shared.priority; +} + /** * @brief Структура для общего адресного пространства CAN. * diff --git a/README.md b/README.md index cd18a82..96e3cea 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # SETCAN -`SETCAN` — небольшой модуль прикладного протокола поверх классического CAN для микроконтроллеров STM32 и библиотеки STM32 HAL. Модуль принимает только кадры с расширенным 29-битным идентификатором, складывает их в кольцевой буфер и передаёт обработчикам по типу сообщения. Также он умеет отправлять широковещательные, дискретные, аналоговые, адресные, Modbus-подобные сообщения, команды привязки датчиков `SETTINGS`, ошибки и периодический «пульс» устройства. +`SETCAN` — legacy-адаптер прикладного ProtoCAN для STM32 HAL. Модуль принимает +кадры, управляет HAL CAN/RTC/TIM и вызывает обработчики приложения. Формат +29-битного идентификатора и его переносимая реализация принадлежат общему +сабмодулю `third_party/templates/c/set-protocol`. Это не самостоятельный проект STM32CubeIDE: здесь нет `.ioc`, startup-файлов, linker script или настроек тактирования. Каталоги `Inc` и `Src` нужно добавить в существующую прошивку STM32. @@ -8,27 +11,30 @@ ```text SETCAN/ +├── third_party/templates/ # единое переносимое ядро ProtoCAN ├── Inc/ │ └── protocan.h # типы протокола, настройки устройства и публичный API └── Src/ └── protocan.c # приём, буферизация, разбор и отправка CAN-кадров ``` -Модуль зависит от файлов `main.h` и `can.h`, сгенерированных STM32CubeMX, а также от HAL-драйверов CAN, RTC и TIM. +Модуль зависит от файлов `main.h` и `can.h`, сгенерированных STM32CubeMX, а также от HAL-драйверов CAN, RTC и TIM. Перед сборкой выполните +`git submodule update --init --recursive` и добавьте +`third_party/templates/c/set-protocol/src/pcan_id.c` в проект. Заголовки +берутся из `third_party/templates/c/set-protocol/include`. ## Документация протокола -Нормативное описание разделено по назначению: +Каноническое описание теперь находится рядом с переносимой реализацией: -- [`doc/protocan/PROTOCOL.md`](doc/protocan/PROTOCOL.md) — структура ProtoCAN; -- [`doc/protocan/BOOTLOADER.md`](doc/protocan/BOOTLOADER.md) — прошивка по CAN; -- [`doc/protocan/OAP.md`](doc/protocan/OAP.md) — правила ведения ОАП; -- [`doc/protocan/examples/test-vectors.json`](doc/protocan/examples/test-vectors.json) — машинные эталоны; +- [`third_party/templates/c/set-protocol/docs/legacy/PROTOCOL.md`](third_party/templates/c/set-protocol/docs/legacy/PROTOCOL.md) — структура ProtoCAN; +- [`third_party/templates/c/protocan-boot/docs/BOOTLOADER.md`](third_party/templates/c/protocan-boot/docs/BOOTLOADER.md) — прошивка по CAN; +- [`third_party/templates/c/set-protocol/docs/legacy/OAP.md`](third_party/templates/c/set-protocol/docs/legacy/OAP.md) — правила ведения ОАП; +- [`third_party/templates/c/set-protocol/tests/vectors/test-vectors.json`](third_party/templates/c/set-protocol/tests/vectors/test-vectors.json) — машинные эталоны; - [`doc/index.html`](doc/index.html) — единая собранная HTML-версия всей документации. -Большой файл `Протокол CAN и ОАП.md` и исходный XLSX сохранены как табличное -представление реестра. Правила приоритета источников описаны в -[`doc/protocan/README.md`](doc/protocan/README.md). +Файлы в `doc/protocan` сохранены как исторический снимок SETCAN и больше не +являются источником истины. ## Формат расширенного CAN ID @@ -560,11 +566,12 @@ CRC32 обнаруживает случайное повреждение, но ## Краткий порядок запуска -1. Добавить `protocan.c/.h` в STM32-проект. -2. Задать `CURRENT_TYPE_DEVICE` и `CURRENT_ID_DEVICE`. -3. Настроить CAN, RTC и периодический TIM в CubeMX. -4. Вызвать `PROTOCAN_INIT()` после `MX_..._Init()`. -5. Запустить CAN, активировать `CAN_IT_RX_FIFO0_MSG_PENDING` и запустить таймер с прерыванием. -6. При необходимости подключить HAL-callback’и вручную. -7. Переопределить нужные `__weak`-обработчики. -8. Вызывать `PROTOCAN_ProcessAllRxMsgs()` в основном цикле. +1. Инициализировать сабмодуль `templates`. +2. Добавить `protocan.c/.h` и общий `pcan_id.c` в STM32-проект. +3. Задать `CURRENT_TYPE_DEVICE` и `CURRENT_ID_DEVICE`. +4. Настроить CAN, RTC и периодический TIM в CubeMX. +5. Вызвать `PROTOCAN_INIT()` после `MX_..._Init()`. +6. Запустить CAN, активировать `CAN_IT_RX_FIFO0_MSG_PENDING` и запустить таймер с прерыванием. +7. При необходимости подключить HAL-callback’и вручную. +8. Переопределить нужные `__weak`-обработчики. +9. Вызывать `PROTOCAN_ProcessAllRxMsgs()` в основном цикле. diff --git a/Src/protocan.c b/Src/protocan.c index 731444c..3b2e76e 100644 --- a/Src/protocan.c +++ b/Src/protocan.c @@ -360,7 +360,7 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastStatus(struct RXMsg _rxMsg) tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; tmp_eID.Fields.DeviceType = CURRENT_TYPE_DEVICE; tmp_eID.Fields.DeviceID = CURRENT_ID_DEVICE; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); RTC_TimeTypeDef sTime = {0}; HAL_RTC_GetTime(_HRTC, &sTime, RTC_FORMAT_BIN); data[0] = sTime.Hours; @@ -652,7 +652,7 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogUniversal(struct RXMsg _rxMsg) ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); TxHeader.DLC = 6; data[0] = 'U'; data[1] = 'N'; @@ -680,7 +680,7 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogSettings(struct RXMsg _rxMsg) ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); TxHeader.DLC = 7; data[0] = 'U'; data[1] = 'S'; @@ -709,7 +709,7 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogUSens(struct RXMsg _rxMsg) ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); TxHeader.DLC = 6; msgBodyAnalogType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; @@ -739,7 +739,7 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogISens(struct RXMsg _rxMsg) ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); TxHeader.DLC = 6; msgBodyAnalogType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; @@ -769,7 +769,7 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogTSens(struct RXMsg _rxMsg) ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); TxHeader.DLC = 6; msgBodyAnalogType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; @@ -813,7 +813,7 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND_CORE(const ProtoCanId_t id, const struct Pr body.Fields.Type = coreData.Type; body.Fields.Body = coreData.Body; localId.Fields.MsgBody = body.Body; - TxHeader.ExtId = localId.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&localId); TxHeader.DLC = 0; if(HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK) { @@ -832,7 +832,7 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND_CORE(const ProtoCanId_t id, const struct Pr uint8_t regsInPacket = (regsRemaining > 8) ? 8 : regsRemaining; body.Fields.Body = coreData.Body + currentIndex; localId.Fields.MsgBody = body.Body; - TxHeader.ExtId = localId.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&localId); TxHeader.DLC = regsInPacket; for(int i = 0; i < regsInPacket; i++) { @@ -859,7 +859,7 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND_CORE(const ProtoCanId_t id, const struct Pr uint8_t regsInPacket = (regsRemaining > 8) ? 8 : regsRemaining; body.Fields.SensorID = coreData.Body + currentIndex; localId.Fields.MsgBody = body.Body; - TxHeader.ExtId = localId.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&localId); TxHeader.DLC = regsInPacket; for(int i = 0; i < regsInPacket; i++) { @@ -967,7 +967,7 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND_SETTINGS( { canData[index] = data.Data[index]; } - TxHeader.ExtId = id.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&id); TxHeader.IDE = CAN_ID_EXT; TxHeader.RTR = CAN_RTR_DATA; TxHeader.TransmitGlobalTime = DISABLE; @@ -1032,7 +1032,7 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToGeneralAddressSpace(struct RXMsg _rxM ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); TxHeader.DLC = 8; data[0] = 'G'; data[1] = 'A'; @@ -1084,7 +1084,7 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND_GENERAL_ADDRESS_SPACE(ProtoCanId_t id, uint uint8_t regsInPacket = (regsRemaining > 4) ? 4 : regsRemaining; tmp_eID.Fields.MsgBody = regStartAdr + currentIndex; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); TxHeader.DLC = regsInPacket*2; for(int i = 0; i < regsInPacket; i++) @@ -1162,7 +1162,7 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToModbusCoil(struct RXMsg _rxMsg) ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); TxHeader.DLC = 8; msgBodyModbusType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; @@ -1194,7 +1194,7 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToModbusDiscrete(struct RXMsg _rxMsg) ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); TxHeader.DLC = 8; msgBodyModbusType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; @@ -1226,7 +1226,7 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToModbusHolding(struct RXMsg _rxMsg) ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); TxHeader.DLC = 8; msgBodyModbusType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; @@ -1258,7 +1258,7 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToModbusInput(struct RXMsg _rxMsg) ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); TxHeader.DLC = 8; msgBodyModbusType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; @@ -1303,7 +1303,7 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND_MODBUS(const ProtoCanId_t id, const struct // Устанавливаем коллизию ExtId с body.Body localId.Fields.MsgBody = (body.Body & 0xFFFF); // Обновляем ExtId - TxHeader.ExtId = localId.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&localId); // Остальные настройки TxHeader.IDE = CAN_ID_EXT; TxHeader.RTR = CAN_RTR_DATA; @@ -1332,7 +1332,7 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND_MODBUS(const ProtoCanId_t id, const struct body.Fields.RegCount = regsInPacket; // Обновляем ExtId localId.Fields.MsgBody = (body.Body & 0xFFFF); - TxHeader.ExtId = localId.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&localId); TxHeader.IDE = CAN_ID_EXT; TxHeader.RTR = CAN_RTR_DATA; TxHeader.TransmitGlobalTime = DISABLE; @@ -1381,7 +1381,7 @@ __weak PROTOCAN_StatusTypeDef PROTOCAN_RequestError(struct RXMsg _rxMsg) msg.Fields.ErrorCode = 0xFF; msg.Fields.Info = 0; tmp_eID.Fields.MsgBody = msg.Body; - TxHeader.ExtId = tmp_eID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&tmp_eID); TxHeader.DLC = 0; return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } @@ -1406,7 +1406,7 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND_ERROR(const ProtoCanId_t id, const struct P uint8_t canData[8]; localId.Fields.MsgBody = ((errorData.Info << 8) | errorData.Code); // Обновляем ExtId - TxHeader.ExtId = localId.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(&localId); // Остальные настройки TxHeader.IDE = CAN_ID_EXT; TxHeader.RTR = CAN_RTR_DATA; @@ -1466,7 +1466,7 @@ void ProtoCanRxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) if(RxHeader.IDE == CAN_ID_EXT) { ProtoCanId_t ExtID_Of_RX_MSG; - ExtID_Of_RX_MSG.BitAll = RxHeader.ExtId; + PROTOCAN_IdUnpack(RxHeader.ExtId, &ExtID_Of_RX_MSG); //Полученное сообщение - пульс устройств в сети. //Место в буфере не занимает, поэтому проверяется до проверки переполнения. if(ExtID_Of_RX_MSG.Fields.MsgType == PROTOCAN_MSGTYPE_PULSE) @@ -1510,7 +1510,7 @@ void ProtoCanPulseCallback(TIM_HandleTypeDef *htim) currentID.Fields.DeviceID = CURRENT_ID_DEVICE; currentID.Fields.MsgType = PROTOCAN_MSGTYPE_PULSE; currentID.Fields.MsgBody = 0; - TxHeader.ExtId = currentID.BitAll; + TxHeader.ExtId = PROTOCAN_IdPack(¤tID); uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; diff --git a/third_party/templates b/third_party/templates new file mode 160000 index 0000000..19becd7 --- /dev/null +++ b/third_party/templates @@ -0,0 +1 @@ +Subproject commit 19becd7b8c40c2268b843f189e65dfbefa43bd09