From db70af46d48a14c1b0b9539a7cb1cbbe2c2c3b70 Mon Sep 17 00:00:00 2001
From: demin
Date: Wed, 22 Oct 2025 21:07:41 +0300
Subject: [PATCH] =?UTF-8?q?=D0=91=D0=B0=D0=B7=D0=B0=20=D0=B4=D0=BB=D1=8F?=
=?UTF-8?q?=20Monjaro=20=D0=B8=20Atlas2024?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/CAN_FW.h | 36 +++++++-
src/CAN_Inputs.h | 189 ++++++++++++++++++++++++++++++++++++++---
src/CAN_Outputs.h | 126 +++++++++++++++++++++++++++
src/SettingsCANTable.h | 26 ++++--
src/myfunc/VinReq.h | 79 +++++++++++++++++
src/myfunc/_MFM3.h | 119 ++++++++++++++++++++++++++
6 files changed, 555 insertions(+), 20 deletions(-)
create mode 100644 src/myfunc/VinReq.h
create mode 100644 src/myfunc/_MFM3.h
diff --git a/src/CAN_FW.h b/src/CAN_FW.h
index b83a97b..45bd790 100644
--- a/src/CAN_FW.h
+++ b/src/CAN_FW.h
@@ -6,12 +6,44 @@
#include "Periodic.h"
#include "Can.h"
-#define CAN_FW_DESCRIPTION "Test"
+enum TPeriodicTasks
+{
+ PELightButton, // Задача управления триггерной кнопкой.
+ PeriodicCount, // количество задач
+};
// структура с пользовательскими переменными
struct TCanFwMem {
- CSettings::TSettings Settings; // структура с настройками - должна быть всегда в начале
+ CSettings::TSettings Settings; // структура с настройками - должна быть всегда в начале
+ CPeriodic PeriodicTask;
+ bool ign; // флаг зажигания
+ bool arm;
+ bool prearm; // предыдущий статус охраны автомобиля
+ bool cmf; // флаг "комфорт"
+ bool mirror; // флаг "сложить зеркала"
+
+ struct{
+ bool hazlamp; // состояние аварийки (любой)
+ bool seized; // Флаг захвата кнопки аварийки
+ uint8_t step; // Шаги функции Emergency Light Button
+ TimerMs tmr; // Таймер сбрасывается в момент фронта/спада автомобильной (не нашей!) аварийки
+ uint8_t cnt;
+ } ELight;
+
+ struct{
+ bool multipkt;
+ uint8_t mode;
+ uint8_t pid;
+ char vin[17];
+ } Diag;
};
+void Wake_CAN (TCanFwMem * vars);
+void ELight_Button2 (TCanFwMem * vars);
+
+// OBDII
+void DiagRcv (TCanFwMem * vars, TCanPkt * pkt, TCanChannel channel);
+void DiagReq (TCanFwMem * vars, uint8_t mode, uint8_t pid, TCanChannel channel);
+
#endif /* CAN_FW_H_ */
diff --git a/src/CAN_Inputs.h b/src/CAN_Inputs.h
index 7755ff4..2c36a40 100644
--- a/src/CAN_Inputs.h
+++ b/src/CAN_Inputs.h
@@ -2,21 +2,186 @@
#include "Can.h"
#include "IO.h"
#include "Utils.h"
-
+#include "myfunc/VinReq.h"
+#include "myfunc/_MFM3.h"
void Init (TCanFwMem * vars)
{
- CoreFunc->DebugConsole("CAN_FW version: %s\n", (const char*)(gCanFwInfo.TextInfo));
+ static const TCanInit can1_init =
+ {
+ CCan::CanBaudrate500,
+ 1,
+ {
+ CCan::Filter::List11 (0x010, 0x020),
+ CCan::Filter::List11 (0x040, 0x0E0),
+ CCan::Filter::List11 (0x095, 0x100),
+ CCan::Filter::List11 (0x300, 0x310),
+ CCan::Filter::List11 (0x334, 0x355),
- static const TCanInit can1_init =
- {
- CCan::CanBaudrate500,
- 1,
- {
- CCan::Filter::List11 (0x660),
- CCan::Filter::Mask11 (0x7E0, 0x7F0), // 0x7E0..0x7EF
- }
- };
- CoreFunc->CanInit (CANch1, &can1_init);
+ CCan::Filter::List11 (0x380),
+ CCan::Filter::List11 (0x0C3, 0x210),
+ }
+ };
+ CoreFunc->CanInit (CANch1, &can1_init);
+//---------------------------------------------------------
+ static const TCanInit can2_init =
+ {
+ CCan::CanBaudrate500,
+ 1,
+ {
+ CCan::Filter::List11 (0x04B, 0x050),
+ CCan::Filter::List11 (0x111, 0x138),
+ CCan::Filter::Mask11 (0x7E0, 0x7F0),
+ }
+ };
+ CoreFunc->CanInit (CANch2, &can2_init);
+//---------------------------------------------------------
+ vars->PeriodicTask.AddTask (PELightButton, ELight_Button2);
+}
+
+void Can1Received (TCanFwMem * vars, TCanPkt *apPkt)
+{
+ // int32_t temp; // знаковое!
+ // int16_t tmp; // знаковое!
+ uint8_t * buf = apPkt->data;
+ TCanPkt pkt;
+ TCanPkt pktm;
+
+ switch (apPkt->id)
+ {
+ case 0x010:
+ CoreFunc->InputState (CIO::iLock, (Rx_D & 0x0C)==0x08);
+ break;
+
+ case 0x040:
+ CoreFunc->InputState (CIO::iDoorDrv, (Rx_G & 0xC0)==0x40);
+ CoreFunc->InputState (CIO::iDoorRL, (Rx_F & 0x0C)==0x04);
+
+ if (vars->cmf)
+ {
+ pkt = *apPkt;
+ pkt.data[4] |= 0x0A;
+ CoreFunc->CanSend (CANch1, &pkt, 7);
+ CoreFunc->CanSend (CANch1, &pkt, 17);
+ vars->cmf = false;
+ }
+ break;
+
+ case 0x0E0:
+ CoreFunc->InputState (CIO::iDoorFP, (Rx_D & 0xC0)==0x40);
+ CoreFunc->InputState (CIO::iDoorRR, (Rx_B & 0x30)==0x10);
+ CoreFunc->InputState (CIO::iTrunk, (Rx_F & 0xC0)==0x40);
+ break;
+
+ case 0x095:
+ if (Rx_A == 0x52) CoreFunc->Command (CoreCmdTrunkOpened, 0x04); // Br-Hf
+ break;
+
+ case 0x100:
+
+ if (Rx_B == 0x1F || Rx_B == 0x2F || Rx_B == 0x9F) CoreFunc->Command (CoreCmdArm, 0x04); // Br, HF-L,R
+ else if (Rx_B == 0x17 || Rx_B == 0x27 || Rx_B == 0x97) CoreFunc->Command (CoreCmdDisarm, 0x04); // Br, HF-L,R
+/*
+ if (vars->mirror) не закрывается и зеркала зависают
+ {
+ pktm = *apPkt;
+ pktm.data[1] == 0x1F;
+ // попробовать это !!! pktm.data[7] |= 0x08;
+ for (int i=0; i<30; i++) CoreFunc->CanSend (CANch1, &pktm, 30);
+ vars->mirror = false;
+ }
+ */
+ break;
+ case 0x210:
+ CoreFunc->DataValue (CanData_CoolantTemp, Rx_C - 10);
+ break;
+
+ //case 0x270: XC90
+ //CoreFunc->DataValue (CanData_FuelLevel, buf[6]*100/256);//
+ // break;
+
+ case 0x300:
+ CoreFunc->DataValue (CanData_RPM, (Rx_C << 8 | Rx_D) >> 1);
+ break;
+ case 0x310:
+ CoreFunc->DataValue (CanData_Odometer, ((Rx_A & 0x0F) <<16 | buf[1]<<8 | buf[2]) * 10);
+ break;
+
+ case 0x334:
+ CoreFunc->InputState (CIO::iLeftTurnLight, Rx_H & BIT(4));
+ CoreFunc->InputState (CIO::iRightTurnLight, Rx_H & BIT(2));
+ CoreFunc->InputState (CIO::iEmergency, (Rx_H & 0x14) == 0x14);
+ break;
+ case 0x355:
+ CoreFunc->InputState (CIO::iLauncher, Rx_F & 0x03);
+ break;
+
+ case 0x000:
+ // CoreFunc->InputState (CIO::iLamp, Rx_B & BIT(2)); // от XC90 не подходит ID0C3
+ // CoreFunc->DataValue (CanData_Speed, (Rx_B << 8 | Rx_C)>>4);
+ // CoreFunc->DataValue (CanData_FuelLevel, Rx_H); // от XC90 не подходит ID310
+ // CoreFunc->DataValue (CanData_FuelConsumption, Rx_C);
+ break;
+ }
+}
+void Can2Received (TCanFwMem * vars, TCanPkt *apPkt)
+{
+ // int32_t temp; // знаковое!
+ // int16_t tmp; // знаковое!
+ uint8_t * buf = apPkt->data;
+
+ switch (apPkt->id)
+ {
+
+ case 0x04B:
+ CoreFunc->InputState (CIO::iPark, (Rx_D & 0x0E) == 0x00);
+ break;
+ case 0x050:
+ Rx_C = Rx_C/2;
+ if (Rx_C > 100) Rx_C = 100;
+ CoreFunc->DataValue (CanData_Accelerator, Rx_C);
+ break;
+ case 0x111:
+ CoreFunc->InputState (CIO::iIgn, Rx_C & BIT(7));
+ break;
+ case 0x138:
+ //CoreFunc->InputState (CIO::iHBrake, (Rx_A & 0x0C)==0x04); // ID144
+ CoreFunc->DataValue (CanData_BrakeForce,(Rx_E << 8 | Rx_F)/20);
+ CoreFunc->InputState (CIO::iBrake, Rx_E );
+ break;
+
+ case 0x2B0:
+ //tmp = Rx_D << 8 | Rx_E ;
+ //CoreFunc->DataValue (CanData_WheelAngle, tmp);
+ break;
+ }
+
+}
+
+void InputChanged (TCanFwMem * vars, uint32_t aInputNum, bool aSwitchedOn)
+{
+// TCanPkt pkt;
+
+ if ((aInputNum == CIO::iIgn) && ! aSwitchedOn)
+ {
+ CoreFunc->InputState (CIO::iBrake, false);
+ CoreFunc->DataValue (CanData_Invalid | CanData_RPM, 0);
+ }
+
+ if (aInputNum == CIO::iIgn) vars->ign = aSwitchedOn;
+
+ if (aInputNum == CIO::iEmergency)
+ {
+ vars->ELight.hazlamp = aSwitchedOn;
+
+ if (!vars->ELight.seized) vars->ELight.tmr.Restart (); // при собственных моргках таймер не перезапускаем
+ //tt(aSwitchedOn);
+ }
+}
+
+void GuardEvent (TCanFwMem * vars, TGuardEvents aEvent)
+{
+ if (aEvent == geARM) vars->arm = true;
+ if (aEvent == geDISARM) vars->arm = false;
}
\ No newline at end of file
diff --git a/src/CAN_Outputs.h b/src/CAN_Outputs.h
index cce6be2..4834095 100644
--- a/src/CAN_Outputs.h
+++ b/src/CAN_Outputs.h
@@ -3,3 +3,129 @@
#include "IO.h"
#include "Utils.h"
+void OutputChanged (TCanFwMem * vars, uint32_t aOutputNum, bool aSwitchedOn)
+{
+ TCanPkt pkt;
+
+ switch (aOutputNum)
+ {
+ case CIO::oLights:
+ if(aSwitchedOn)
+ {
+ if (vars->ELight.tmr.Value() > 800 && vars->ELight.step == 0)
+ {
+ CoreFunc->RunSequence(SEQ(oEmergency, seqProg8)); // Выход на кнопку аварийки импульс 250 ms
+ vars->PeriodicTask.SetTimeout (PELightButton, 260);
+ vars->ELight.step++;
+ vars->ELight.cnt = 0;
+ vars->ELight.seized = true;
+ //tt(0x10);
+ }
+ }
+ break;
+
+ case CIO::oCloseWindows:
+ if (aSwitchedOn) vars->cmf = true;
+ break;
+
+
+ case CIO::oFoldMirrors:
+ if (aSwitchedOn) vars->mirror = true;
+ break;
+ }
+}
+
+void ELight_Button2(TCanFwMem * vars) // Нажатия на триггерную кнопку аварийки с ожиданием вспышки
+{
+ switch (vars->ELight.step)
+ {
+ case 1:
+ if (!vars->ELight.hazlamp)
+ {
+ if (vars->ELight.cnt < 8) // ожидаем когда лампа загориться
+ {
+ vars->PeriodicTask.SetTimeout (PELightButton, 50); // подождем еще
+ vars->ELight.cnt++;
+ //tt(0x15);
+ }
+ else {vars->ELight.step = 0; /*tt(3);*/} // не удалось зажечь
+ }
+ else // если лампа загорелась (~440mS для Monjaro)
+ {
+ vars->PeriodicTask.SetTimeout (PELightButton, 150); // пусть погорит хотя бы 150mS
+ vars->ELight.step++;
+ //tt(0x20);
+ }
+ break;
+ case 2: // лампа горит уже 150m
+ CoreFunc->RunSequence(SEQ(oEmergency, seqProg9)); // гасим ее импульсом не менее 150 мс ( 100mS для Monjaro мало)
+ vars->PeriodicTask.SetTimeout (PELightButton, 160);
+ vars->ELight.step++;
+ break;
+ case 3:
+ if (!vars->ELight.hazlamp) // ожидаем когда лампа погаснет
+ {
+
+ vars->ELight.step = 0;
+ vars->ELight.seized = false;
+ //tt(0x0F);
+ }
+ else vars->PeriodicTask.SetTimeout (PELightButton, 10);
+ break;
+ }
+}
+
+
+// события arm, disarm, alarm, ...
+void Command (TCanFwMem * vars, TCanFwCommands aCmd, uint32_t aCmdParam)
+{
+ TCanPkt pkt;
+ uint8_t i;
+
+ switch (aCmd)
+ {
+
+ case ccLockDoors:
+ Wake_CAN(vars);
+ pkt.id = 0x020;
+ pkt.data_len = 8;
+ pkt.SetData("\x28\x12\x28\x2B\xAA\x00\x00\x68"); // FL=0x2X RL=0xX2 FR=0x2X RR=0x2X
+ for (i=0; i<4; i++) CoreFunc->CanSend (CANch1, &pkt, 18);
+ break;
+ case ccUnLockDoors: // можно сделать mccPriority
+ Wake_CAN(vars);
+ pkt.id = 0x020;
+ pkt.data_len = 8;
+ pkt.SetData("\x18\x11\x18\x1B\xAA\x00\x00\x68");
+ for (i=0; i<4; i++) CoreFunc->CanSend (CANch1, &pkt, 18);
+ break;
+
+ case ccSendObdReq:
+ pkt.id = 0x7DF;
+ pkt.data_len = 8;
+ pkt.data[0] = 0x01;
+ pkt.data[1] = aCmdParam;
+ CoreFunc->CanSend (CANch2, &pkt, 1);
+ break;
+ case ccUpdateVin: // отправить запрос VIN-кода
+ DiagReq (vars, 0x09, 0x02, CANch2);
+ break;
+
+ default: break;
+ }
+}
+
+//============== Пробуждение ==================================================
+void Wake_CAN(TCanFwMem * vars)
+{
+ TCanPkt pkt;
+// uint8_t i;
+
+ if (CoreFunc->CanGetRxTimeout (CANch1) < 1000) return; // Если шина не спит то не будим!
+
+ pkt.id = 0x501;
+ pkt.data_len = 8;
+ pkt.SetData("\x01\x40\x26\x00\x01");
+ CoreFunc->CanSend (CANch1, &pkt, 2);
+ CoreFunc->CanSend (CANch1, &pkt, 20);
+}
\ No newline at end of file
diff --git a/src/SettingsCANTable.h b/src/SettingsCANTable.h
index 6739ada..0097582 100644
--- a/src/SettingsCANTable.h
+++ b/src/SettingsCANTable.h
@@ -1,10 +1,24 @@
-//=====================================================================================
+#define iWireLock iDevice6
+#define iWireUnlock iDevice7
+#define iEmergency iDevice8
+#define oWireLock oDevice6
+#define oWireUnlock oDevice7
+#define oEmergency oDevice8
-// SETTING_RSRV позволяет сделать "дырки" в номерах настроек
-// если не определена, значит, она не используется
-#ifndef SETTING_RSRV
-#define SETTING_RSRV(name)
-#endif
+/** Новая классификация прошивок **
+
+ (A) - Обычный обходчик
+ (B) - Baypass
+ (С) - Запуск через штатную CAN команду
+ (E) - Электрический автомобиль
+
+ Правила нумерации версий:
+ release - меняется только при появлении существенно новой версии приложения. Обратная совместимость может не обеспечиваться.
+ version - меняется при появлении новых возможостей.
+ update - меняется при исправлении ошибок в текущей версии.
+*/
+
+ #define CAN_FW_DESCRIPTION "Geely_Atlas2024_[C]"
//************************** Динамические параметры ***********************************************//
diff --git a/src/myfunc/VinReq.h b/src/myfunc/VinReq.h
new file mode 100644
index 0000000..5b96e32
--- /dev/null
+++ b/src/myfunc/VinReq.h
@@ -0,0 +1,79 @@
+#include "CAN_FW.h"
+#include "Can.h"
+#include
+
+void DiagReq (TCanFwMem * vars, uint8_t mode, uint8_t pid, TCanChannel channel)
+{
+ TCanPkt req;
+ req.id = 0x7DF;
+ req.SetData ("\x02\x00\x00");
+ req.data_len = 8;
+ req.data[1] = mode;
+ req.data[2] = pid;
+ CoreFunc->CanSend (channel, &req, 0);
+
+ vars->Diag.mode = mode;
+ vars->Diag.pid = pid;
+}
+
+
+void DiagRcv (TCanFwMem * vars, TCanPkt * pkt, TCanChannel channel)
+{
+ uint8_t * buf = pkt->data;
+
+ // заголовок мультипакета
+ if (buf[0] == 0x10)
+ {
+ uint8_t mode = buf[2] & (~0x40);
+ uint8_t pid = buf[3];
+
+ // отправить flow control
+ if (mode == vars->Diag.mode &&
+ pid == vars->Diag.pid)
+ {
+ TCanPkt fc;
+ fc.id = 0x7E0;
+ fc.SetData ("\x30\x00\x00");
+ fc.data_len = 8;
+ CoreFunc->CanSend (channel, &fc, 0);
+
+ vars->Diag.multipkt = true;
+ }
+ else
+ {
+ // сбросить автомат
+ vars->Diag.mode = vars->Diag.pid = 0;
+ return;
+ }
+
+ // заголовок мультипакета с VIN-кодом
+ if (mode == 9 && pid == 2)
+ memcpy (vars->Diag.vin, &buf[5], 3);
+
+ }
+ // тело мультипакета
+ else
+ if (((buf[0] & 0xF0) == 0x20) && vars->Diag.multipkt)
+ {
+ uint8_t cnt = buf[0] & 0x0F;
+
+ // мультипакет с VIN-кодом
+ if (vars->Diag.mode == 9 && vars->Diag.pid == 2)
+ {
+ if (cnt == 1)
+ memcpy (&vars->Diag.vin[3], &buf[1], 7);
+ if (cnt == 2)
+ {
+ memcpy (&vars->Diag.vin[10], &buf[1], 7);
+
+ // сообщить в ядро о приёме пакета
+ CoreFunc->DataValueArr (CanData_VIN, vars->Diag.vin);
+
+ // сбросить автомат
+ vars->Diag.mode = vars->Diag.pid = 0;
+ }
+ }
+ }
+ else
+ vars->Diag.multipkt = false;
+}
diff --git a/src/myfunc/_MFM3.h b/src/myfunc/_MFM3.h
new file mode 100644
index 0000000..7de998c
--- /dev/null
+++ b/src/myfunc/_MFM3.h
@@ -0,0 +1,119 @@
+#include "CAN_FW.h"
+#include "Can.h"
+
+#define Rx_DLC apPkt->data_len
+#define Rx_A buf[0]
+#define Rx_B buf[1]
+#define Rx_C buf[2]
+#define Rx_D buf[3]
+#define Rx_E buf[4]
+#define Rx_F buf[5]
+#define Rx_G buf[6]
+#define Rx_H buf[7]
+
+uint16_t bcd (uint16_t hexdata)
+{
+ uint16_t x;
+ uint8_t i;
+
+ if ( hexdata > 9999) return 0xFFFF;
+
+ x = hexdata >> (14-3);
+ hexdata <<= (2+3);
+
+ for (i=0; i<11; i++)
+ {
+ if ((x >> 0 & 0x0F) > 4) x+=0x0003;
+ if ((x >> 4 & 0x0F) > 4) x+=0x0030;
+ if ((x >> 8 & 0x0F) > 4) x+=0x0300;
+ if ((x >>12 & 0x0F) > 4) x+=0x3000;
+ x <<= 1;
+ if (hexdata & 0x8000) x++;
+ hexdata <<= 1;
+ }
+ return x;
+}
+#if defined ISiSLAVE
+void iSlaveTune (TCanFwMem * vars, bool isStep)
+{
+ TCanPkt pkt;
+ uint16_t bcdTime;
+
+ if (isStep)
+ {
+ pkt.data[0] = vars->iSlave.prestep;
+ pkt.data[1] = vars->iSlave.step;
+ }
+ else
+ {
+ pkt.data[0] = 0xFF;
+ pkt.data[1] = vars->iSlave.step;
+ }
+
+ bcdTime = bcd (vars->iSlave.tmrHaz.Value());
+ pkt.data[2] = bcdTime >> 8; // время вспышки в двоично-десятичном формате
+ pkt.data[3] = bcdTime >> 0;
+
+ // pkt.data[2] = (vars->iSlave.tmrHaz.Value()) >> 8;
+// pkt.data[3] = vars->iSlave.tmrHaz.Value();
+
+ pkt.id = 0x7dd;
+ pkt.data_len = 4;
+ CoreFunc->CanSend (CANch1, &pkt, 0);
+}
+void TestSteps (TCanFwMem * vars)
+{
+ if (vars->iSlave.step != vars->iSlave.prestep)
+ iSlaveTune (vars, true);
+ vars->iSlave.prestep = vars->iSlave.step;
+}
+void TestTimeFlash (TCanFwMem * vars)
+{
+ if (vars->iSlave.prehazlamp != vars->iSlave.hazlamp)
+ iSlaveTune (vars, false);
+}
+# endif
+//=================================================================
+
+void tt (uint8_t t)
+{
+ TCanPkt pkt;
+ pkt.id = 0x7dd;
+ pkt.data_len = 1;
+ pkt.data[0] = t;
+ CoreFunc->CanSend (CANch1, &pkt, 0);
+}
+void tt2 (uint8_t t)
+{
+ TCanPkt pkt;
+ pkt.id = 0x7dd;
+ pkt.data_len = 1;
+ pkt.data[0] = t;
+ CoreFunc->CanSend (CANch2, &pkt, 0);
+}
+
+void ttBcd (uint8_t t)
+{
+ TCanPkt pkt;
+ uint16_t T;
+
+ T = bcd (t);
+ pkt.id = 0x7dd;
+ pkt.data_len = 2;
+ pkt.data[0] = T >> 8;
+ pkt.data[1] = T >> 0;
+ CoreFunc->CanSend (CANch1, &pkt, 0);
+}
+
+void ttuint (uint32_t t)
+{
+ TCanPkt pkt;
+ pkt.id = 0x7dd;
+ pkt.data_len = 4;
+ pkt.data[0] = t >> 24;
+ pkt.data[1] = t >> 16;
+ pkt.data[2] = t >> 8;
+ pkt.data[3] = t >> 0;
+ CoreFunc->CanSend (CANch1, &pkt, 0);
+}
+//=================================================================
\ No newline at end of file