Full kline refactor, passes 815 verification initialization
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_board.1645530980" name="Board" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_board" useByScannerDiscovery="false" value="genericBoard" valueType="string"/>
|
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_board.1645530980" name="Board" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_board" useByScannerDiscovery="false" value="genericBoard" valueType="string"/>
|
||||||
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.defaults.2021318695" name="Defaults" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.defaults" useByScannerDiscovery="false" value="com.st.stm32cube.ide.common.services.build.inputs.revA.1.0.6 || Debug || true || Executable || com.st.stm32cube.ide.mcu.gnu.managedbuild.option.toolchain.value.workspace || STM32G431KBUx || 0 || 0 || arm-none-eabi- || ${gnu_tools_for_stm32_compiler_path} || ../Core/Inc | ../Drivers/STM32G4xx_HAL_Driver/Inc | ../Drivers/STM32G4xx_HAL_Driver/Inc/Legacy | ../Drivers/CMSIS/Device/ST/STM32G4xx/Include | ../Drivers/CMSIS/Include || || || USE_HAL_DRIVER | STM32G431xx || || Drivers | Core/Startup | Core || || || ${workspace_loc:/${ProjName}/STM32G431KBUX_FLASH.ld} || true || NonSecure || || secure_nsclib.o || || None || || || " valueType="string"/>
|
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.defaults.2021318695" name="Defaults" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.defaults" useByScannerDiscovery="false" value="com.st.stm32cube.ide.common.services.build.inputs.revA.1.0.6 || Debug || true || Executable || com.st.stm32cube.ide.mcu.gnu.managedbuild.option.toolchain.value.workspace || STM32G431KBUx || 0 || 0 || arm-none-eabi- || ${gnu_tools_for_stm32_compiler_path} || ../Core/Inc | ../Drivers/STM32G4xx_HAL_Driver/Inc | ../Drivers/STM32G4xx_HAL_Driver/Inc/Legacy | ../Drivers/CMSIS/Device/ST/STM32G4xx/Include | ../Drivers/CMSIS/Include || || || USE_HAL_DRIVER | STM32G431xx || || Drivers | Core/Startup | Core || || || ${workspace_loc:/${ProjName}/STM32G431KBUX_FLASH.ld} || true || NonSecure || || secure_nsclib.o || || None || || || " valueType="string"/>
|
||||||
<option id="com.st.stm32cube.ide.mcu.debug.option.cpuclock.1251078204" name="Cpu clock frequence" superClass="com.st.stm32cube.ide.mcu.debug.option.cpuclock" useByScannerDiscovery="false" value="160" valueType="string"/>
|
<option id="com.st.stm32cube.ide.mcu.debug.option.cpuclock.1251078204" name="Cpu clock frequence" superClass="com.st.stm32cube.ide.mcu.debug.option.cpuclock" useByScannerDiscovery="false" value="160" valueType="string"/>
|
||||||
|
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.converthex.1315450990" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.converthex" value="true" valueType="boolean"/>
|
||||||
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.targetplatform.723718408" isAbstract="false" osList="all" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.targetplatform"/>
|
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.targetplatform.723718408" isAbstract="false" osList="all" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.targetplatform"/>
|
||||||
<builder buildPath="${workspace_loc:/KBU6_tests}/Debug" enabledIncrementalBuild="true" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.builder.140640651" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.builder"/>
|
<builder buildPath="${workspace_loc:/KBU6_tests}/Debug" enabledIncrementalBuild="true" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.builder.140640651" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.builder"/>
|
||||||
<tool command="gcc -gdwarf-4" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.1812956542" name="MCU/MPU GCC Assembler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler">
|
<tool command="gcc -gdwarf-4" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.1812956542" name="MCU/MPU GCC Assembler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler">
|
||||||
|
|||||||
@@ -24,20 +24,20 @@ struct AlphaStruct {
|
|||||||
float ME_RPM_Beta_array[FM_N_RPM][FM_N_ME]; //para la transpuesta
|
float ME_RPM_Beta_array[FM_N_RPM][FM_N_ME]; //para la transpuesta
|
||||||
};
|
};
|
||||||
extern struct AlphaStruct fuelmap_m12;
|
extern struct AlphaStruct fuelmap_m12;
|
||||||
//extern struct AlphaStruct fuelmap_m5;
|
extern struct AlphaStruct fuelmap_m5;
|
||||||
extern struct AlphaStruct fuelmap_10;
|
extern struct AlphaStruct fuelmap_10;
|
||||||
extern struct AlphaStruct fuelmap_25;
|
extern struct AlphaStruct fuelmap_25;
|
||||||
extern struct AlphaStruct fuelmap_60;
|
extern struct AlphaStruct fuelmap_60;
|
||||||
extern struct AlphaStruct fuelmap_80;
|
//extern struct AlphaStruct fuelmap_80;
|
||||||
extern struct fuelMapIndexes fuelMapI;
|
extern struct fuelMapIndexes fuelMapI;
|
||||||
|
|
||||||
static const struct AlphaStruct* g_FuelMaps[] = {
|
static const struct AlphaStruct* g_FuelMaps[] = {
|
||||||
&fuelmap_m12,
|
&fuelmap_m12,
|
||||||
//&fuelmap_m5,
|
&fuelmap_m5,
|
||||||
&fuelmap_10,
|
&fuelmap_10,
|
||||||
&fuelmap_25,
|
&fuelmap_25,
|
||||||
&fuelmap_60,
|
&fuelmap_60,
|
||||||
&fuelmap_80
|
//&fuelmap_80
|
||||||
|
|
||||||
// add/remove as needed; order must match fuelMapI.T_Index_array
|
// add/remove as needed; order must match fuelMapI.T_Index_array
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,25 +23,26 @@
|
|||||||
#define CYLINDERS 4
|
#define CYLINDERS 4
|
||||||
|
|
||||||
/* TIMING COMPENSATIONS */
|
/* TIMING COMPENSATIONS */
|
||||||
#define PHI1 25.102
|
#define PHI1 25.4 //25.4??
|
||||||
|
|
||||||
#define TEIN_NOMINAL 0
|
#define TEIN_NOMINAL 0
|
||||||
#define TEIN_FAULT 950.5
|
#define TEIN_FAULT 950.5
|
||||||
|
|
||||||
/* ALL FBKW */
|
/* ALL FBKW */
|
||||||
#define FBKW_DEM_M 0.5
|
#define FBKW_DEM_M 0.5
|
||||||
#define FBKW_DEM_TEMP_M -0.0146
|
|
||||||
#define FBKW_DEM_TEMP_N 2.25
|
|
||||||
|
|
||||||
#define FBKW_DEM_A1 0.576
|
#define FBKW_DEM_TEMP_M -0.0138271344
|
||||||
#define FBKW_DEM_A2 2.2
|
#define FBKW_DEM_TEMP_N 1.619546633
|
||||||
#define FBKW_DEM_A3 -0.636
|
|
||||||
|
#define FBKW_DEM_A1 -0.649
|
||||||
|
#define FBKW_DEM_A2 3.86
|
||||||
|
#define FBKW_DEM_A3 -0.795
|
||||||
|
|
||||||
#define FBKW_DEM_MIN 0
|
#define FBKW_DEM_MIN 0
|
||||||
|
|
||||||
#define FBKW_FEEDBACK_ZERO 7.75
|
#define FBKW_FEEDBACK_ZERO 8.345
|
||||||
#define FBKW_FEEDBACK_MIN -3.16
|
#define FBKW_FEEDBACK_MIN -3.77
|
||||||
#define FBKW_FEEDBACK_MAX 17.84
|
#define FBKW_FEEDBACK_MAX 18
|
||||||
#define FBKW_FEEDBACK_IC_DT 27
|
#define FBKW_FEEDBACK_IC_DT 27
|
||||||
|
|
||||||
#define FBKW_PID_KP 90 //16
|
#define FBKW_PID_KP 90 //16
|
||||||
@@ -68,7 +69,6 @@
|
|||||||
#define FM_N_ME 12
|
#define FM_N_ME 12
|
||||||
#define FM_N_T 5
|
#define FM_N_T 5
|
||||||
|
|
||||||
|
|
||||||
/* PEAK AND HOLD */
|
/* PEAK AND HOLD */
|
||||||
#define PH_PEAK_DEF 600
|
#define PH_PEAK_DEF 600
|
||||||
|
|
||||||
|
|||||||
108
Core/Inc/id_504009.h
Normal file
108
Core/Inc/id_504009.h
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* id.h
|
||||||
|
*
|
||||||
|
* Created on: Jul 28, 2025
|
||||||
|
* Author: herli
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INC_ID_H_
|
||||||
|
#define INC_ID_H_
|
||||||
|
|
||||||
|
/* DEBUG PARAMETERS*/
|
||||||
|
//#define T06301 //ford 004 -> 002 004 006 || 504 -> 010 018
|
||||||
|
//#define T06215 //bmw rover 004 -> 005 014 015 016 017 || 504 -> 005 007 017 || 006 -> 001 002 003 004 007 008
|
||||||
|
//#define T15021 //audi 506 -> 030 033
|
||||||
|
//#define T31804 //audi 506 -> 037 038
|
||||||
|
#define T06209 //eq: T06216
|
||||||
|
|
||||||
|
/* FORD */
|
||||||
|
#define FORD_SYNC_PULSE_OUT 0
|
||||||
|
|
||||||
|
#define ENABLE_AUDI_IMMO 0
|
||||||
|
#define HAS_PREINJECTION 0
|
||||||
|
|
||||||
|
#define CYLINDERS 4
|
||||||
|
|
||||||
|
/* TIMING COMPENSATIONS */
|
||||||
|
#define PHI1 41.016
|
||||||
|
|
||||||
|
#define TEIN_NOMINAL 1550 //faltaria confirmar esto
|
||||||
|
#define TEIN_FAULT 1027
|
||||||
|
|
||||||
|
/* ALL FBKW */
|
||||||
|
#define FBKW_DEM_M 0.5
|
||||||
|
#define FBKW_DEM_TEMP_M 0
|
||||||
|
#define FBKW_DEM_TEMP_N -7.6
|
||||||
|
|
||||||
|
//rpm comp
|
||||||
|
#define FBKW_DEM_A1 0
|
||||||
|
#define FBKW_DEM_A2 0
|
||||||
|
#define FBKW_DEM_A3 0
|
||||||
|
|
||||||
|
#define FBKW_DEM_MIN -6
|
||||||
|
|
||||||
|
#define FBKW_FEEDBACK_ZERO 55.12
|
||||||
|
#define FBKW_FEEDBACK_MIN -4.24
|
||||||
|
#define FBKW_FEEDBACK_MAX 19.75
|
||||||
|
#define FBKW_FEEDBACK_IC_DT 27
|
||||||
|
|
||||||
|
#define FBKW_PID_KP 90 //16
|
||||||
|
#define FBKW_PID_KI 0 //18
|
||||||
|
#define FBKW_PID_KD 0
|
||||||
|
#define FBKW_PID_KAW 0 //16
|
||||||
|
#define FBKW_PID_BIAS 60
|
||||||
|
#define FBKW_PID_INTEGRAL 0
|
||||||
|
#define FBKW_PID_MAXRATE 10000
|
||||||
|
|
||||||
|
#define FBKW_PWM_MAX 95
|
||||||
|
#define FBKW_PWM_MIN 5
|
||||||
|
|
||||||
|
#define FBKW_MAX 90 //en 504 parece que era 506
|
||||||
|
#define FBKW_MAX_REAL_DEM 165 //wtf is this
|
||||||
|
|
||||||
|
/* CAN DEFINITIONS */
|
||||||
|
#define CAN_BAUDRATE 500
|
||||||
|
#define CAN_RPM_SEND_ASYNC 250
|
||||||
|
#define CAN_EMPF2_INSTANT 0
|
||||||
|
|
||||||
|
/* ALL FUELMAP */
|
||||||
|
#define FM_N_RPM 6
|
||||||
|
#define FM_N_ME 10
|
||||||
|
#define FM_N_T 5
|
||||||
|
|
||||||
|
|
||||||
|
/* PEAK AND HOLD */
|
||||||
|
#define PH_PEAK_DEF 600
|
||||||
|
|
||||||
|
/* ANALOG CALIBRATION PARAMETERS */
|
||||||
|
// MOSFET
|
||||||
|
#define V_PEAK 2.94
|
||||||
|
#define V_HOLD 1.94
|
||||||
|
|
||||||
|
#define INJ_CLOSING_MARGIN 0 //o cero, o 20
|
||||||
|
|
||||||
|
// TOOTHED WHEEL
|
||||||
|
#define TW_MT_THRESHOLD 2.4
|
||||||
|
#define TW_THEETHS 120
|
||||||
|
#define TW_TOOTH_ALPHA 3
|
||||||
|
#define TW_STARTED_RPM 400
|
||||||
|
|
||||||
|
#define MIN_RPM 20
|
||||||
|
|
||||||
|
#define USTODEG 0.000006
|
||||||
|
// TIMINGS
|
||||||
|
#define TEIN_READING_OFFSET 8
|
||||||
|
|
||||||
|
/* ifdef things */
|
||||||
|
|
||||||
|
#if CYLINDERS == 4
|
||||||
|
#define TW_PERCYL_TEETH 26
|
||||||
|
#elif CYLINDERS == 6
|
||||||
|
#define TW_PERCYL_TEETH 16
|
||||||
|
#else
|
||||||
|
#define TW_PERCYL_TEETH 26
|
||||||
|
#error "Unsupported number of cylinders"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* INC_ID_H_ */
|
||||||
@@ -1,276 +0,0 @@
|
|||||||
/* IKW1281Connection.c — byte pump, RX FIFO, no HAL_Delay() in hot path */
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "IKW1281Connection.h"
|
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
extern UART_HandleTypeDef huart1;
|
|
||||||
|
|
||||||
uint8_t K_TxData[KLINE_BUFFER_SIZE];
|
|
||||||
volatile uint8_t rx_done_flag = 0;
|
|
||||||
|
|
||||||
// ---------- RX FIFO ----------
|
|
||||||
static uint8_t rx_fifo[KLINE_RX_FIFO_SZ];
|
|
||||||
static volatile uint16_t rx_head = 0, rx_tail = 0;
|
|
||||||
|
|
||||||
static inline int rx_fifo_push(uint8_t b){
|
|
||||||
uint16_t next = (uint16_t)((rx_head + 1U) % KLINE_RX_FIFO_SZ);
|
|
||||||
if (next == rx_tail) rx_tail = (uint16_t)((rx_tail + 1U) % KLINE_RX_FIFO_SZ); // drop oldest
|
|
||||||
rx_fifo[rx_head] = b;
|
|
||||||
rx_head = next;
|
|
||||||
rx_done_flag = 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int KLine_RxFifo_Pop(uint8_t *out){
|
|
||||||
if (rx_head == rx_tail) return 0;
|
|
||||||
uint8_t b = rx_fifo[rx_tail];
|
|
||||||
rx_tail = (uint16_t)((rx_tail + 1U) % KLINE_RX_FIFO_SZ);
|
|
||||||
if (out) *out = b;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- Non-blocking TX "byte pump" ----------
|
|
||||||
volatile KTxEngine ktx = {0};
|
|
||||||
static uint8_t tx_byte_1;
|
|
||||||
|
|
||||||
static uint8_t _packetCounter = 0;
|
|
||||||
static uint8_t _packetCounterInitialized = 0;
|
|
||||||
|
|
||||||
|
|
||||||
void KLine_BytePump_Init(void){ memset((void*)&ktx, 0, sizeof(ktx)); }
|
|
||||||
|
|
||||||
int KLine_TxStart(const uint8_t *data, uint16_t len, uint8_t append_end, uint8_t require_ack){
|
|
||||||
if (ktx.active==1) return 0;
|
|
||||||
if (len > KLINE_TXBUF_MAX) {
|
|
||||||
// defensive: refuse too-long sends (KWP1281 blocks are small, so this is fine)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
memcpy(ktx.ibuf, data, len);
|
|
||||||
ktx.buf = ktx.ibuf;
|
|
||||||
ktx.using_ibuf = 1;
|
|
||||||
ktx.active = 1; ktx.len = len; ktx.idx = 0;
|
|
||||||
ktx.append_end = append_end; ktx.last_tx = 0; ktx.awaiting_echo = 0;
|
|
||||||
ktx.require_ack = require_ack; ktx.tx_inflight = 0; ktx.next_time_ms = HAL_GetTick();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int KLine_TxBusy(void){ return ktx.active || ktx.tx_inflight || ktx.awaiting_echo; }
|
|
||||||
|
|
||||||
void KLine_BytePump_Service(void){
|
|
||||||
if (!ktx.active) return;
|
|
||||||
uint32_t now = HAL_GetTick();
|
|
||||||
if (ktx.awaiting_echo) return;
|
|
||||||
//if (ktx.tx_inflight) return;
|
|
||||||
if ((int32_t)(now - ktx.next_time_ms) < 0) return;
|
|
||||||
|
|
||||||
uint8_t b;
|
|
||||||
if (ktx.idx < ktx.len) b = ktx.buf[ktx.idx++];
|
|
||||||
else if (ktx.append_end) {
|
|
||||||
b = PACKET_END_EXPECTED;
|
|
||||||
ktx.append_end = 0;
|
|
||||||
ktx.active = 2; //ENDING
|
|
||||||
}
|
|
||||||
else { // === FINISHED: make sure state is fully reset ===
|
|
||||||
ktx.active = 0;
|
|
||||||
//ktx.awaiting_echo = 0; // <— add //TODO
|
|
||||||
//ktx.tx_inflight = 0; // <— add
|
|
||||||
// (optional) ktx.next_time_ms = now;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tx_byte_1 = b;
|
|
||||||
if (HAL_UART_Transmit_IT(&huart1, &tx_byte_1, 1) == HAL_OK){
|
|
||||||
//ktx.tx_inflight = 1;
|
|
||||||
ktx.last_tx = b;
|
|
||||||
if (ktx.require_ack && ktx.active == 1){
|
|
||||||
ktx.awaiting_echo = 1; // expect tester's ~b
|
|
||||||
} else {
|
|
||||||
ktx.awaiting_echo = 0;
|
|
||||||
}
|
|
||||||
ktx.next_time_ms = now + KWP_P1_GAP_MS;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//int pepe = 0;
|
|
||||||
// Called from HAL_UART_RxCpltCallback (kline.c)
|
|
||||||
void KLine_OnByteReceived(uint8_t byte)
|
|
||||||
{
|
|
||||||
|
|
||||||
uint32_t now = HAL_GetTick();
|
|
||||||
|
|
||||||
// ---- Self-echo suppression: only within a tiny time window after our TX ----
|
|
||||||
if(ktx.active){
|
|
||||||
if ((uint32_t)(now - ktx.last_tx_done_ms) <= KWP_ECHO_SUPPRESS_MS && byte == ktx.last_tx) {
|
|
||||||
if(ktx.active == 2){
|
|
||||||
ktx.active = 0;
|
|
||||||
ktx.awaiting_echo = 0;
|
|
||||||
}
|
|
||||||
return; // echo of our own last TX byte
|
|
||||||
//no se bien si me quita el echo o el counter, habra que ver despues cuando los mensajes no sean de 03
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*if(pepe==2){
|
|
||||||
pepe++;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/*if(ktx.awaiting_ack && byte == ktx.last_tx){
|
|
||||||
return;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// ---- Tester complement for our TX byte? consume only if it matches ~last_tx ----
|
|
||||||
if (ktx.active == 1) {
|
|
||||||
if(ktx.awaiting_echo){
|
|
||||||
if (byte == (uint8_t)~ktx.last_tx) {
|
|
||||||
ktx.awaiting_echo = 0;
|
|
||||||
//return; // consumed tester's complement
|
|
||||||
} else {
|
|
||||||
// Not a complement: treat as inbound payload, drop awaiting flag to resync
|
|
||||||
ktx.awaiting_echo = 0;
|
|
||||||
//ktx.active = 0; //we are done, communication failed drop message.
|
|
||||||
//return;
|
|
||||||
// fall through to inbound handling
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
//WriteComplement(byte); //then its inboud payload, echo
|
|
||||||
rx_fifo_push(byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Inbound byte from tester: ACK it immediately so we never race timing ----
|
|
||||||
// (Tiny 1-byte blocking write; safe and deterministic at 9600 bps.)
|
|
||||||
//WriteComplement(byte);
|
|
||||||
|
|
||||||
// Push for higher-level parsing
|
|
||||||
}
|
|
||||||
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){
|
|
||||||
if (huart->Instance == USART1) {
|
|
||||||
//ktx.tx_inflight = 0;
|
|
||||||
ktx.last_tx_done_ms = HAL_GetTick(); // <-- NEW
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- Legacy/compat API (kept) ----------
|
|
||||||
uint8_t ReadByte(){
|
|
||||||
uint32_t timeout = HAL_GetTick() + 1000;
|
|
||||||
uint8_t b;
|
|
||||||
while (!KLine_RxFifo_Pop(&b)){
|
|
||||||
if ((int32_t)(HAL_GetTick() - timeout) >= 0) return 0xFF;
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
uint8_t ReadAndAckByte(void){ uint8_t b = ReadByte(); WriteComplement(b); return b; }
|
|
||||||
void WriteComplement(uint8_t b){
|
|
||||||
uint8_t c = (uint8_t)~b;
|
|
||||||
WriteByteRaw(c); }
|
|
||||||
|
|
||||||
void WriteByteRaw(uint8_t b){
|
|
||||||
ktx.active = 0;
|
|
||||||
KLine_TxStart(&b, 1, 0, 0);
|
|
||||||
while (KLine_TxBusy()) KLine_BytePump_Service();
|
|
||||||
|
|
||||||
/*ktx.last_tx = b;
|
|
||||||
ktx.active = 2;
|
|
||||||
|
|
||||||
pepe++;
|
|
||||||
if(b == 0x01){
|
|
||||||
pepe++;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern UART_HandleTypeDef huart1;
|
|
||||||
(void)HAL_UART_Transmit(&huart1, &b, 1, 5);*/
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAX_PACKETS 16
|
|
||||||
static ParsedPacket packets_buffer[MAX_PACKETS];
|
|
||||||
|
|
||||||
ParsedPacket* ReceivePackets(int *out_count){
|
|
||||||
int count = 0;
|
|
||||||
while (1){
|
|
||||||
if (count >= MAX_PACKETS) break;
|
|
||||||
ParsedPacket p = ReceivePacket();
|
|
||||||
packets_buffer[count++] = p;
|
|
||||||
if (p.isAckNak) break;
|
|
||||||
SendAckPacket();
|
|
||||||
}
|
|
||||||
if (out_count) *out_count = count;
|
|
||||||
return packets_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ParsedPacket ReceivePacket(){
|
|
||||||
ParsedPacket packet = (ParsedPacket){0};
|
|
||||||
uint8_t idx = 0;
|
|
||||||
|
|
||||||
uint8_t packetLength = ReadAndAckByte(); packet.raw[idx++] = packetLength;
|
|
||||||
uint8_t packetCounter = ReadPacketCounter(); packet.raw[idx++] = packetCounter;
|
|
||||||
uint8_t packetCommand = ReadAndAckByte(); packet.raw[idx++] = packetCommand;
|
|
||||||
|
|
||||||
for (int i=0;i<packetLength-3;i++){
|
|
||||||
uint8_t b = ReadAndAckByte(); packet.raw[idx++] = b;
|
|
||||||
}
|
|
||||||
uint8_t end = ReadByte(); packet.raw[idx++] = end; packet.length = idx;
|
|
||||||
if (end != PACKET_END_EXPECTED) return packet;
|
|
||||||
|
|
||||||
packet.title = packetCommand;
|
|
||||||
switch (packetCommand){
|
|
||||||
case PACKET_CMD_ACK: packet.type = PACKET_TYPE_ACK; packet.isAckNak = 1; break;
|
|
||||||
case PACKET_CMD_NAK: packet.type = PACKET_TYPE_NAK; packet.isAckNak = 1; break;
|
|
||||||
case PACKET_CMD_AsciiData:
|
|
||||||
packet.type = (packet.raw[3] == 0x00) ? PACKET_TYPE_CODING_WSC : PACKET_TYPE_ASCII_DATA; break;
|
|
||||||
case PACKET_CMD_ReadEepromResponse: packet.type = PACKET_TYPE_READ_EEPROM_RESPONSE; break;
|
|
||||||
case PACKET_CMD_ReadRomEepromResponse: packet.type = PACKET_TYPE_READ_ROM_EEPROM_RESPONSE; break;
|
|
||||||
default: packet.type = PACKET_TYPE_UNKNOWN; break;
|
|
||||||
}
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
void ResetPacketCounter(){ _packetCounter = 0; _packetCounterInitialized = 0; }
|
|
||||||
uint8_t ReadPacketCounter(){
|
|
||||||
uint8_t v = ReadAndAckByte();
|
|
||||||
if (!_packetCounterInitialized){ _packetCounter = v; _packetCounterInitialized = 1; }
|
|
||||||
else if (v != _packetCounter){ /* optional mismatch handling */ }
|
|
||||||
_packetCounter++;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
inline int32_t tick_diff(uint32_t a, uint32_t b){ return (int32_t)(a - b); }
|
|
||||||
|
|
||||||
int ReadByte_Tmo(uint32_t ms, uint8_t *out){
|
|
||||||
uint32_t deadline = HAL_GetTick() + ms; uint8_t b;
|
|
||||||
while (!KLine_RxFifo_Pop(&b)){
|
|
||||||
if ((int32_t)(HAL_GetTick() - deadline) >= 0) return 0;
|
|
||||||
}
|
|
||||||
if (out) *out = b; return 1;
|
|
||||||
}
|
|
||||||
int ReadPacketCounter_Tmo(uint32_t ms, uint8_t *out){
|
|
||||||
uint8_t v; if (!ReadByte_Tmo(ms, &v)) return 0;
|
|
||||||
if (!_packetCounterInitialized){ _packetCounter = v; _packetCounterInitialized = 1; }
|
|
||||||
else if (v != _packetCounter){ _packetCounter = v+1; return 0; }
|
|
||||||
_packetCounter++; WriteComplement(v);
|
|
||||||
if (out) *out = v; return 1;
|
|
||||||
}
|
|
||||||
int ReadAndAckByte_Tmo(uint32_t ms, uint8_t *out){
|
|
||||||
uint8_t b; if (!ReadByte_Tmo(ms, &b)) return 0; WriteComplement(b); if (out) *out = b; return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SendAckPacket(){ uint8_t a = (uint8_t)PACKET_CMD_ACK; SendPacket(&a, 1); }
|
|
||||||
void SendNakPacket(){ uint8_t a = (uint8_t)PACKET_CMD_NAK; SendPacket(&a, 1); }
|
|
||||||
|
|
||||||
void SendPacket(uint8_t* payload, uint8_t length){
|
|
||||||
uint8_t packetLength = (uint8_t)(length + 2);
|
|
||||||
static uint8_t packet[1 + 1 + MAX_PACKET_SIZE + 1];
|
|
||||||
int idx = 0;
|
|
||||||
packet[idx++] = packetLength;
|
|
||||||
packet[idx++] = _packetCounter++;
|
|
||||||
for (int i=0;i<length;i++) packet[idx++] = payload[i];
|
|
||||||
KLine_TxStart(packet, idx, 1 /*append 0x03*/, 1 /*expect complement*/);
|
|
||||||
while (KLine_TxBusy()) KLine_BytePump_Service(); // cooperative wait (no HAL_Delay)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convenience used by your code
|
|
||||||
ParsedPacket* SendCustom(const uint8_t* data, int len, int *out_count, uint8_t isBRKT){
|
|
||||||
(void)isBRKT; SendPacket((uint8_t*)data, len);
|
|
||||||
int count = 0; ParsedPacket* p = ReceivePackets(&count);
|
|
||||||
if (out_count) *out_count = count; return p;
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
/* IKW1281Connection.h — non-blocking byte-pump + proper timings */
|
|
||||||
|
|
||||||
#ifndef INC_IKW1281CONNECTION_H_
|
|
||||||
#define INC_IKW1281CONNECTION_H_
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include "stdint.h"
|
|
||||||
|
|
||||||
extern uint8_t K_RxData[];
|
|
||||||
extern volatile uint8_t rx_done_flag;
|
|
||||||
|
|
||||||
#define KLINE_BUFFER_SIZE 20
|
|
||||||
#define PACKET_END_EXPECTED 0x03
|
|
||||||
|
|
||||||
// === Protocol sizing ===
|
|
||||||
#define MAX_PACKET_SIZE 16 // KWP1281 block size
|
|
||||||
|
|
||||||
// === Timings (per ISO 9141-2 / KWP1281) ===
|
|
||||||
#define KWP_P4_MIN_MS 5U // tester inter-byte (P4) 5..20 ms
|
|
||||||
#define KWP_P4_MAX_MS 20U
|
|
||||||
#define KWP_P1_GAP_MS 5U // ECU inter-byte (P1) target
|
|
||||||
|
|
||||||
#define KWP_ECHO_SUPPRESS_MS 3U // ~3 ms window after TX to drop self-echo
|
|
||||||
|
|
||||||
// 5-baud init post-switch delays
|
|
||||||
#define KWP_W1_MS 300U // ECU internal time before 0x55 (20..300 ms allowed)
|
|
||||||
#define KWP_W2_MS 10U // 0x55 -> first keyword (5..20 ms)
|
|
||||||
#define KWP_W3_MS 10U // KW LSB -> KW MSB (0..20 ms)
|
|
||||||
|
|
||||||
// RX FIFO
|
|
||||||
#define KLINE_RX_FIFO_SZ 64
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char client_ident[13];
|
|
||||||
char unk_ident1[11];
|
|
||||||
char soft_info[11];
|
|
||||||
char unk_ident2[11];
|
|
||||||
char unk_ident3[11];
|
|
||||||
char unk_ident4[7];
|
|
||||||
} ControllerInfo;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
PACKET_CMD_ReadIdent = 0x00,
|
|
||||||
PACKET_CMD_ReadRam = 0x01,
|
|
||||||
PACKET_CMD_WriteRam = 0x02,
|
|
||||||
PACKET_CMD_ReadRomEeprom = 0x03,
|
|
||||||
PACKET_CMD_ActuatorTest = 0x04,
|
|
||||||
PACKET_CMD_FaultCodesDelete = 0x05,
|
|
||||||
PACKET_CMD_End = 0x06,
|
|
||||||
PACKET_CMD_FaultCodesRead = 0x07,
|
|
||||||
PACKET_CMD_SingleRead = 0x08,
|
|
||||||
PACKET_CMD_ACK = 0x09,
|
|
||||||
PACKET_CMD_NAK = 0x0A,
|
|
||||||
PACKET_CMD_SoftwareCoding = 0x10,
|
|
||||||
PACKET_CMD_LoginEeprom = 0x18,
|
|
||||||
PACKET_CMD_ReadEeprom = 0x19,
|
|
||||||
PACKET_CMD_WriteEeprom = 0x1A,
|
|
||||||
PACKET_CMD_Custom = 0x1B,
|
|
||||||
PACKET_CMD_GroupReading = 0x29,
|
|
||||||
PACKET_CMD_Login = 0x2B,
|
|
||||||
PACKET_CMD_GroupReadingResponse = 0xE7,
|
|
||||||
PACKET_CMD_ReadEepromResponse = 0xEF,
|
|
||||||
PACKET_CMD_ActuatorTestResponse = 0xF5,
|
|
||||||
PACKET_CMD_AsciiData = 0xF6,
|
|
||||||
PACKET_CMD_WriteEepromResponse = 0xF9,
|
|
||||||
PACKET_CMD_SingleReadResponse = 0xFB,
|
|
||||||
PACKET_CMD_FaultCodesResponse = 0xFC,
|
|
||||||
PACKET_CMD_ReadRomEepromResponse = 0xFD
|
|
||||||
} PacketCommand;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
PACKET_TYPE_UNKNOWN = 0,
|
|
||||||
PACKET_TYPE_ACK,
|
|
||||||
PACKET_TYPE_NAK,
|
|
||||||
PACKET_TYPE_ASCII_DATA,
|
|
||||||
PACKET_TYPE_CODING_WSC,
|
|
||||||
PACKET_TYPE_READ_EEPROM_RESPONSE,
|
|
||||||
PACKET_TYPE_READ_ROM_EEPROM_RESPONSE
|
|
||||||
} PacketType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PacketType type;
|
|
||||||
uint8_t title;
|
|
||||||
uint8_t length;
|
|
||||||
uint8_t raw[MAX_PACKET_SIZE];
|
|
||||||
uint8_t isAckNak;
|
|
||||||
} ParsedPacket;
|
|
||||||
|
|
||||||
#define KLINE_TXBUF_MAX 24 // enough for KWP1281: 2 header + 16 data + 0x03 + margins
|
|
||||||
|
|
||||||
// === Non-blocking TX "byte pump" ===
|
|
||||||
typedef struct {
|
|
||||||
uint8_t active;
|
|
||||||
const uint8_t *buf;
|
|
||||||
uint16_t len;
|
|
||||||
uint16_t idx;
|
|
||||||
uint8_t append_end; // append 0x03
|
|
||||||
uint8_t last_tx;
|
|
||||||
uint8_t awaiting_echo; // waiting for complement
|
|
||||||
uint8_t require_ack; // expect complement per byte?
|
|
||||||
uint8_t tx_inflight; // HAL_UART_Transmit_IT in progress
|
|
||||||
uint32_t next_time_ms; // next earliest send time
|
|
||||||
uint32_t last_tx_done_ms; // last send time
|
|
||||||
|
|
||||||
uint8_t ibuf[KLINE_TXBUF_MAX];
|
|
||||||
uint8_t using_ibuf;
|
|
||||||
} KTxEngine;
|
|
||||||
|
|
||||||
extern volatile KTxEngine ktx;
|
|
||||||
|
|
||||||
void KLine_BytePump_Init(void);
|
|
||||||
void KLine_BytePump_Service(void);
|
|
||||||
int KLine_TxStart(const uint8_t *data, uint16_t len, uint8_t append_end, uint8_t require_ack);
|
|
||||||
int KLine_TxBusy(void);
|
|
||||||
|
|
||||||
// RX FIFO helpers
|
|
||||||
int KLine_RxFifo_Pop(uint8_t *out);
|
|
||||||
void KLine_OnByteReceived(uint8_t byte);
|
|
||||||
|
|
||||||
// Legacy/blocking API (still available)
|
|
||||||
void SendPacket(uint8_t* payload, uint8_t length);
|
|
||||||
void SendAckPacket(void);
|
|
||||||
void SendNakPacket(void);
|
|
||||||
void WriteByteAndReadAck(uint8_t b);
|
|
||||||
void WriteByteRaw(uint8_t b);
|
|
||||||
void WriteComplement(uint8_t b);
|
|
||||||
|
|
||||||
extern ParsedPacket ReceivePacket(void);
|
|
||||||
extern uint8_t ReadByte(void);
|
|
||||||
extern uint8_t ReadAndAckByte(void);
|
|
||||||
extern void ResetPacketCounter(void);
|
|
||||||
extern uint8_t ReadPacketCounter(void);
|
|
||||||
extern inline int32_t tick_diff(uint32_t a, uint32_t b);
|
|
||||||
|
|
||||||
// Timed reads used by higher layers
|
|
||||||
int ReadByte_Tmo(uint32_t ms, uint8_t *out);
|
|
||||||
int ReadPacketCounter_Tmo(uint32_t ms, uint8_t *out);
|
|
||||||
int ReadAndAckByte_Tmo(uint32_t ms, uint8_t *out);
|
|
||||||
|
|
||||||
#endif /* INC_IKW1281CONNECTION_H_ */
|
|
||||||
29
Core/Kline_Libs/PacketCommandEnum.txt
Normal file
29
Core/Kline_Libs/PacketCommandEnum.txt
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
typedef enum {
|
||||||
|
PACKET_CMD_ReadIdent = 0x00,
|
||||||
|
PACKET_CMD_ReadRam = 0x01,
|
||||||
|
PACKET_CMD_WriteRam = 0x02,
|
||||||
|
PACKET_CMD_ReadRomEeprom = 0x03,
|
||||||
|
PACKET_CMD_ActuatorTest = 0x04,
|
||||||
|
PACKET_CMD_FaultCodesDelete = 0x05,
|
||||||
|
PACKET_CMD_End = 0x06,
|
||||||
|
PACKET_CMD_FaultCodesRead = 0x07,
|
||||||
|
PACKET_CMD_SingleRead = 0x08,
|
||||||
|
PACKET_CMD_ACK = 0x09,
|
||||||
|
PACKET_CMD_NAK = 0x0A,
|
||||||
|
PACKET_CMD_SoftwareCoding = 0x10,
|
||||||
|
PACKET_CMD_LoginEeprom = 0x18,
|
||||||
|
PACKET_CMD_ReadEeprom = 0x19,
|
||||||
|
PACKET_CMD_WriteEeprom = 0x1A,
|
||||||
|
PACKET_CMD_Custom = 0x1B,
|
||||||
|
PACKET_CMD_GroupReading = 0x29,
|
||||||
|
PACKET_CMD_Login = 0x2B,
|
||||||
|
PACKET_CMD_GroupReadingResponse = 0xE7,
|
||||||
|
PACKET_CMD_ReadEepromResponse = 0xEF,
|
||||||
|
PACKET_CMD_EepromLoginResponse = 0xF0,
|
||||||
|
PACKET_CMD_ActuatorTestResponse = 0xF5,
|
||||||
|
PACKET_CMD_AsciiData = 0xF6,
|
||||||
|
PACKET_CMD_WriteEepromResponse = 0xF9,
|
||||||
|
PACKET_CMD_SingleReadResponse = 0xFB,
|
||||||
|
PACKET_CMD_FaultCodesResponse = 0xFC,
|
||||||
|
PACKET_CMD_ReadRomResponse = 0xFD
|
||||||
|
} PacketCommand;
|
||||||
53
Core/Kline_Libs/ProtocolDescription.txt
Normal file
53
Core/Kline_Libs/ProtocolDescription.txt
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
Communication flow description:
|
||||||
|
|
||||||
|
|
||||||
|
1.
|
||||||
|
Slave communication state is completely idle and rx gpio pin has exti awaiting for bitbang.
|
||||||
|
|
||||||
|
2.
|
||||||
|
When the Rx pin goes low (gpio exti), it shall start sampling at 5 bauds.
|
||||||
|
When the byte is received, it is verified that it equals 0xf1. If verification is passed, the huart should be initialized and the 9600 baud communication halted for a delay. If the verification fails, it returns to idle/awaiting bitbang.
|
||||||
|
|
||||||
|
3.
|
||||||
|
When the 9600 baud is enabled a handshake sequence will take place. The slave sends a sync byte, waits 10.5ms, then the keyword lsb, waits 10.5ms, and the keyword msb. After the keyword msb has been sent, it should schedule another handshake sequence to start after 43 ms. The handshake should be repeated for a maximum of 5 times before failing and the communication returning to idle.
|
||||||
|
After each time the three bytes of the handshake have been sent (sync lsb and msb), the master will have an opportunity to send an acknowledgement byte to end the handshake sequence. For advancing to the next state, the acked byte’s value should be equal to ~keyword msb.
|
||||||
|
|
||||||
|
4.
|
||||||
|
After the valid handshake has been completed, the slave should identify itself.
|
||||||
|
The identification will be a combined ascii string of data with lengths up to 64 bytes, sent in chunks in n packets.
|
||||||
|
These packets will respect the general packet structure and characteristics, described in the packets section.
|
||||||
|
The packets’ title will be 0xF6 which corresponds to CMD_AsciiData.
|
||||||
|
After each packet containing a chunk is sent, a CMD_Ack packet is expected, and when it is received, the next packet is sent. This repeats until all the data is sent. When all the data is sent and the last ACK packet is received, a final ack packet is sent and the next state is achieved.
|
||||||
|
|
||||||
|
5.
|
||||||
|
After all the initiation sequence is correctly done, the slave will be in a ready state, and the session will be alive until it reaches its deadline. The session deadline is kicked for 1000ms every time a valid packet is received.
|
||||||
|
|
||||||
|
In this state the master will be sending different types of packets that the slave should receive and answer accordingly.
|
||||||
|
|
||||||
|
If there are non valid packets received (unknown title or denied action), a NAK packet should be returned.
|
||||||
|
|
||||||
|
|
||||||
|
Packet based Protocol description:
|
||||||
|
Each packet is transmitted in the form:
|
||||||
|
[LENGTH] [COUNTER] [COMMAND] [DATA_0 ... DATA_N] [0x03]
|
||||||
|
|
||||||
|
The LENGTH byte defines the size of the logical packet excluding the end byte 0x03, and is calculated as the number of payload bytes plus two additional bytes for COUNTER and COMMAND. The COUNTER is an incrementing sequence byte used to keep synchronization between slave and master. The COMMAND defines the requested service or response type, and the DATA field is variable length depending on the service. The byte 0x03 is always used as packet terminator.
|
||||||
|
|
||||||
|
It may not need clarification, but a packet can be sent from either master or slave.
|
||||||
|
|
||||||
|
A key aspect of the protocol is the byte-by-byte acknowledge mechanism. Every byte transmitted by one side must be immediately acknowledged by the other side with its bitwise complement. In other words, for any received byte B, the acknowledge byte is ~B. For example, if the slave sends 0x10, the master responds with 0xEF. This acknowledge is required for LENGTH, COUNTER, COMMAND bytes and every data byte, except the important packet terminator byte.
|
||||||
|
|
||||||
|
Once a complete packet has been received and all bytes (except the packet terminator one) have been individually acknowledged, the receiving side processes the command and sends a packet-level response. This response is distinct from the per-byte complements and represents the application-level protocol behavior. The response packet itself follows exactly the same framing and byte acknowledge rules as the original request.
|
||||||
|
|
||||||
|
|
||||||
|
Implemented packets are:
|
||||||
|
CMD_ReadIdent -> Triggers a repeat of the ascii packets identification sequence.
|
||||||
|
CMD_WritesRam -> Returns variable values that are mapped to valid address requests. If valid returns ACK, else fails with NAK.
|
||||||
|
CMD_FaultCodesRead -> Triggers a chunk sending sequence of a dtc built stream awaiting ack packets between each packet containing a chunk and sends a final ack packet. It’s similar to the identification chunk sending.
|
||||||
|
CMD_LoginEeprom -> Compares login payload and if valid, unlocks implemented zones with a flag. If valid returns PACKET_CMD_EepromLoginResponse and awaits ACK, else fails with NAK.
|
||||||
|
CMD_WriteEeprom -> Checks requested write address and length is valid and unlocked. If valid returns CMD_WriteEepromResponse and awaits ACK, else fails with NAK.
|
||||||
|
CMD_ReadRom -> Checks requested read address and length is valid. If valid returns CMD_ReadRomResponse with the corresponding data as payload and awaits ack.
|
||||||
|
CMD_End -> Sends ack packet and terminates the session, returning to idle.
|
||||||
|
|
||||||
|
Special consideration:
|
||||||
|
As the byte by byte protocol is implemented in an embedded solution with a kline transceiver with the simpler huart setup, each transmitted byte is echoed through the rx and received instantly. This echo is the same value as the last sent byte and is received instantly upon transmission, and should not be confused with the acknowledged byte sent by the master, and should be discarded correctly. A simple comparison between the last transmitted byte and the currently received one is not enough as there are packets that start with length 0x03, and the echo detection would suppress this byte when comparing it when the last transmitted one (0x03 packet terminator). A better driver implementation strategy is expected.
|
||||||
30
Core/Kline_Libs/kl_api.c
Normal file
30
Core/Kline_Libs/kl_api.c
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* kl_api.c
|
||||||
|
* Top-level orchestration: init and service all K-Line layers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kl_api.h"
|
||||||
|
#include "kl_phy.h"
|
||||||
|
#include "kl_transport.h"
|
||||||
|
#include "kl_session.h"
|
||||||
|
#include "kl_app.h"
|
||||||
|
|
||||||
|
void KLine_Init(void)
|
||||||
|
{
|
||||||
|
KL_App_Init();
|
||||||
|
KL_Transport_Init();
|
||||||
|
KL_Session_Init(); /* Calls KL_Phy_Init internally, arms EXTI */
|
||||||
|
}
|
||||||
|
|
||||||
|
void KLine_Service(void)
|
||||||
|
{
|
||||||
|
KL_Phy_Service();
|
||||||
|
KL_Transport_Service();
|
||||||
|
KL_App_Service();
|
||||||
|
KL_Session_Service();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t KLine_GetStatus(void)
|
||||||
|
{
|
||||||
|
return KL_Session_GetStatus();
|
||||||
|
}
|
||||||
38
Core/Kline_Libs/kl_api.h
Normal file
38
Core/Kline_Libs/kl_api.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* kl_api.h
|
||||||
|
* Public entry points for the K-Line protocol stack.
|
||||||
|
*
|
||||||
|
* Integration:
|
||||||
|
* - Call KLine_Init() once after HAL peripheral init
|
||||||
|
* - Call KLine_Service() from the main loop (frequently, <3ms)
|
||||||
|
* - Route ISR callbacks as described below
|
||||||
|
*
|
||||||
|
* ISR wiring (add to your existing HAL callbacks):
|
||||||
|
*
|
||||||
|
* void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
|
||||||
|
* if (huart->Instance == USART1) KL_Phy_TxCpltCB();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
|
||||||
|
* if (huart->Instance == USART1) KL_Phy_RxCpltCB();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
|
||||||
|
* if (GPIO_Pin == KL_RX_PIN) KL_Session_OnExtiRxFalling();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
|
||||||
|
* if (htim->Instance == TIM17) KL_Session_OnTim17Elapsed();
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KL_API_H
|
||||||
|
#define KL_API_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void KLine_Init(void);
|
||||||
|
void KLine_Service(void);
|
||||||
|
uint8_t KLine_GetStatus(void); /* 0=idle, 1=connecting, 2=active */
|
||||||
|
|
||||||
|
#endif /* KL_API_H */
|
||||||
537
Core/Kline_Libs/kl_app.c
Normal file
537
Core/Kline_Libs/kl_app.c
Normal file
@@ -0,0 +1,537 @@
|
|||||||
|
/*
|
||||||
|
* kl_app.c
|
||||||
|
* Application layer: command dispatch, handlers, generic stream sender.
|
||||||
|
*
|
||||||
|
* All handlers are non-blocking. They prepare response payloads and
|
||||||
|
* start TX via the transport layer. KL_App_Service() drives the
|
||||||
|
* multi-step response state machines.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kl_app.h"
|
||||||
|
#include "kl_transport.h"
|
||||||
|
#include "kl_session.h"
|
||||||
|
#include "psg_prop.h"
|
||||||
|
#include "ee_manager.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* s_dfi_code: shared with ee_manager via extern in ee_manager.h */
|
||||||
|
int8_t s_dfi_code = 0;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Application context
|
||||||
|
* ================================================================ */
|
||||||
|
static struct {
|
||||||
|
KL_AppState state;
|
||||||
|
|
||||||
|
/* Single-response payload */
|
||||||
|
uint8_t resp_payload[KL_MAX_PACKET_PAYLOAD];
|
||||||
|
uint8_t resp_len;
|
||||||
|
|
||||||
|
/* Stream sender */
|
||||||
|
uint8_t stream_buf[KL_STREAM_BUF_SIZE];
|
||||||
|
uint8_t stream_len;
|
||||||
|
uint8_t stream_offset;
|
||||||
|
uint8_t stream_cmd; /* 0xF6 for ASCII, 0xFC for DTC */
|
||||||
|
uint8_t stream_chunk_size; /* 13 for ASCII, 15 for DTC */
|
||||||
|
uint8_t stream_cur_chunk; /* Current chunk size being sent */
|
||||||
|
|
||||||
|
/* EEPROM/ROM unlock flags */
|
||||||
|
uint8_t eeprom_unlocked_dfi;
|
||||||
|
uint8_t eeprom_unlocked_dfi_write;
|
||||||
|
uint8_t rom_unlocked_cust;
|
||||||
|
} app;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* DTC storage
|
||||||
|
* ================================================================ */
|
||||||
|
static KL_FaultCode s_dtc_list[KL_DTC_TOTAL_RECORDS];
|
||||||
|
static size_t s_dtc_count = 0;
|
||||||
|
|
||||||
|
/* Default test DTCs */
|
||||||
|
static const KL_FaultCode s_default_dtcs[] = {
|
||||||
|
{ 0x5B, 0x20, 0x55 },
|
||||||
|
{ 0x50, 0x20, 0x03 },
|
||||||
|
{ 0x56, 0x20, 0x56 },
|
||||||
|
{ 0x5E, 0x30, 0x60 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Password / login bodies (from old kline.c)
|
||||||
|
* ================================================================ */
|
||||||
|
static const uint8_t kPwdBody_DFI[] = { 0x00, 0x03, 0xFF, 0xFF };
|
||||||
|
static const uint8_t kLoginBody_DFI[] = { 0x00, 0x00, 0x00, 0xBF, 0x30, 0x35, 0x30, 0x30, 0x30, 0x31, 0x1C, 0x09, 0x04 };
|
||||||
|
static const uint8_t kPwdBody_DFI_WRITE[] = { 0x00, 0x03, 0x2F, 0xFF, 'K','H','T','C','A','8','G','0','E' };
|
||||||
|
static const uint8_t kPwdBody_CUST[] = { 0x00, 0x00, 0x82, 0x33 };
|
||||||
|
static const uint8_t kLoginBody_CUST[] = { 0x00, 0x00, 0x9F, 0xFF, 0x30, 0x35, 0x30, 0x30, 0x30, 0x31, 0x1C, 0x09, 0x04 };
|
||||||
|
static const uint8_t kPwdBody_OEM_1[] = { 0x00, 0x02, 0x2E, 0x10 };
|
||||||
|
static const uint8_t kLoginBody_OEM_1[] = { 0x9F, 0xD0, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||||
|
|
||||||
|
static const uint8_t kReadIdentAddrBody[] = { 0x02, 0x00, 0xC6 };
|
||||||
|
static const uint8_t kReadFahrSoftwareAddress[] = { 0x02, 0x00, 0xA4 };
|
||||||
|
static const uint8_t kReadStart55Address[] = { 0x01, 0x00, 0xAE };
|
||||||
|
|
||||||
|
static const uint8_t kActivateSyncOut[] = { 0x88, 0x01, 0x04, 0x06, 0x01 };
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Init / Reset
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_App_Init(void)
|
||||||
|
{
|
||||||
|
memset(&app, 0, sizeof(app));
|
||||||
|
app.state = KL_APP_IDLE;
|
||||||
|
|
||||||
|
KL_App_SetDTCList(s_default_dtcs, sizeof(s_default_dtcs) / sizeof(s_default_dtcs[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
void KL_App_ResetLocks(void)
|
||||||
|
{
|
||||||
|
app.eeprom_unlocked_dfi = 0;
|
||||||
|
app.eeprom_unlocked_dfi_write = 0;
|
||||||
|
app.rom_unlocked_cust = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KL_App_Busy(void)
|
||||||
|
{
|
||||||
|
return (app.state != KL_APP_IDLE && app.state != KL_APP_DONE) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KL_App_Done(void)
|
||||||
|
{
|
||||||
|
return (app.state == KL_APP_DONE || app.state == KL_APP_IDLE) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* DTC management
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_App_SetDTCList(const KL_FaultCode *list, size_t count)
|
||||||
|
{
|
||||||
|
if (!list) { s_dtc_count = 0; return; }
|
||||||
|
if (count > KL_DTC_TOTAL_RECORDS) count = KL_DTC_TOTAL_RECORDS;
|
||||||
|
for (size_t i = 0; i < count; ++i) s_dtc_list[i] = list[i];
|
||||||
|
s_dtc_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* DTC stream builder
|
||||||
|
* ================================================================ */
|
||||||
|
static size_t build_dtc_stream(uint8_t *out)
|
||||||
|
{
|
||||||
|
size_t w = 0;
|
||||||
|
for (size_t i = 0; i < KL_DTC_TOTAL_RECORDS; ++i) {
|
||||||
|
if (i < s_dtc_count) {
|
||||||
|
out[w++] = s_dtc_list[i].dtc;
|
||||||
|
out[w++] = s_dtc_list[i].status;
|
||||||
|
out[w++] = s_dtc_list[i].extra;
|
||||||
|
} else {
|
||||||
|
out[w++] = 0x00;
|
||||||
|
out[w++] = 0xFF;
|
||||||
|
out[w++] = 0xFF;
|
||||||
|
}
|
||||||
|
/* Pad to 8-byte record */
|
||||||
|
for (int j = 0; j < 5; j++) out[w++] = 0xFF;
|
||||||
|
}
|
||||||
|
return w; /* 64 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Ident stream builder (for ReadIdent command)
|
||||||
|
* ================================================================ */
|
||||||
|
static size_t app_build_ident(uint8_t *out, size_t maxlen)
|
||||||
|
{
|
||||||
|
size_t w = 0;
|
||||||
|
const char *strs[] = {
|
||||||
|
PSG_KUNDENNUMMER_STR, PSG_DATENSATZ_STR,
|
||||||
|
PSG_SOFTWARE_VER_STR, PSG_SOFTWARE_VER2_STR,
|
||||||
|
PSG_STEUERGERAET_STR, PSG_UNKNOWN_STR
|
||||||
|
};
|
||||||
|
const size_t lens[] = { 12, 10, 10, 10, 10, 6 };
|
||||||
|
for (int s = 0; s < 6; s++) {
|
||||||
|
for (size_t i = 0; i < lens[s] && strs[s][i] != '\0' && w < maxlen; i++) {
|
||||||
|
out[w++] = (uint8_t)strs[s][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Helper: start a single-response send
|
||||||
|
* ================================================================ */
|
||||||
|
static void start_response(const uint8_t *payload, uint8_t len)
|
||||||
|
{
|
||||||
|
memcpy(app.resp_payload, payload, len);
|
||||||
|
app.resp_len = len;
|
||||||
|
KL_Transport_SendPacket(app.resp_payload, app.resp_len);
|
||||||
|
app.state = KL_APP_RESP_WAIT_TX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Helper: start a stream send
|
||||||
|
* ================================================================ */
|
||||||
|
static void start_stream(uint8_t cmd, uint8_t chunk_size, const uint8_t *data, uint8_t len)
|
||||||
|
{
|
||||||
|
memcpy(app.stream_buf, data, len);
|
||||||
|
app.stream_len = len;
|
||||||
|
app.stream_offset = 0;
|
||||||
|
app.stream_cmd = cmd;
|
||||||
|
app.stream_chunk_size = chunk_size;
|
||||||
|
app.state = KL_APP_STREAM_SEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Command handlers
|
||||||
|
* ================================================================ */
|
||||||
|
static void handle_ack(void)
|
||||||
|
{
|
||||||
|
KL_Transport_SendAck();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_read_ident(void)
|
||||||
|
{
|
||||||
|
uint8_t buf[KL_STREAM_BUF_SIZE];
|
||||||
|
size_t n = app_build_ident(buf, sizeof(buf));
|
||||||
|
start_stream((uint8_t)KL_CMD_AsciiData, KL_ASCII_CHUNK_SIZE, buf, (uint8_t)n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_fault_codes_read(void)
|
||||||
|
{
|
||||||
|
uint8_t buf[KL_DTC_TOTAL_RECORDS * KL_DTC_RECORD_SIZE];
|
||||||
|
size_t n = build_dtc_stream(buf);
|
||||||
|
start_stream((uint8_t)KL_CMD_FaultCodesResponse, KL_DTC_CHUNK_SIZE, buf, (uint8_t)n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_read_eeprom(const KL_Packet *cmd)
|
||||||
|
{
|
||||||
|
if (cmd->data_len < 3U) { KL_Transport_SendNak(); app.state = KL_APP_SIMPLE_WAIT_TX; return; }
|
||||||
|
|
||||||
|
uint8_t len_req = cmd->data[0];
|
||||||
|
uint16_t addr = ((uint16_t)cmd->data[1] << 8) | cmd->data[2];
|
||||||
|
|
||||||
|
if (addr == 0x0044 && !app.eeprom_unlocked_dfi) {
|
||||||
|
KL_Transport_SendNak();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t data[16] = {0};
|
||||||
|
if (!PSG_EEPROM_GetData(addr, len_req, data)) {
|
||||||
|
KL_Transport_SendNak();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t payload[1 + 16];
|
||||||
|
payload[0] = (uint8_t)KL_CMD_ReadEepromResponse;
|
||||||
|
memcpy(&payload[1], data, len_req);
|
||||||
|
start_response(payload, (uint8_t)(1U + len_req));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_read_rom_eeprom(const KL_Packet *cmd)
|
||||||
|
{
|
||||||
|
if (cmd->data_len < 3U) { KL_Transport_SendNak(); app.state = KL_APP_SIMPLE_WAIT_TX; return; }
|
||||||
|
|
||||||
|
uint8_t len_req = cmd->data[0];
|
||||||
|
uint16_t addr = ((uint16_t)cmd->data[1] << 8) | cmd->data[2];
|
||||||
|
|
||||||
|
if ((addr == 0x9FFE || addr == (uint16_t)(PSG_CUST_CHANGE_ADDR + 3U)) && !app.rom_unlocked_cust) {
|
||||||
|
KL_Transport_SendNak();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t data[10];
|
||||||
|
if (!PSG_ROM_GetData(addr, len_req, data)) {
|
||||||
|
KL_Transport_SendNak();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t payload[1 + 10];
|
||||||
|
payload[0] = (uint8_t)KL_CMD_ReadRomResponse;
|
||||||
|
memcpy(&payload[1], data, len_req);
|
||||||
|
start_response(payload, (uint8_t)(1U + len_req));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_login_eeprom(const KL_Packet *cmd)
|
||||||
|
{
|
||||||
|
const uint8_t *body = cmd->data;
|
||||||
|
uint8_t body_len = cmd->data_len;
|
||||||
|
|
||||||
|
const uint8_t *login_body = NULL;
|
||||||
|
size_t login_len = 0;
|
||||||
|
|
||||||
|
if (body_len == sizeof(kPwdBody_DFI) && memcmp(body, kPwdBody_DFI, sizeof(kPwdBody_DFI)) == 0) {
|
||||||
|
app.eeprom_unlocked_dfi = 1;
|
||||||
|
login_body = kLoginBody_DFI;
|
||||||
|
login_len = sizeof(kLoginBody_DFI);
|
||||||
|
} else if (body_len == sizeof(kPwdBody_CUST) && memcmp(body, kPwdBody_CUST, sizeof(kPwdBody_CUST)) == 0) {
|
||||||
|
app.rom_unlocked_cust = 1;
|
||||||
|
login_body = kLoginBody_CUST;
|
||||||
|
login_len = sizeof(kLoginBody_CUST);
|
||||||
|
} else if (body_len == sizeof(kPwdBody_OEM_1) && memcmp(body, kPwdBody_OEM_1, sizeof(kPwdBody_OEM_1)) == 0) {
|
||||||
|
login_body = kLoginBody_OEM_1;
|
||||||
|
login_len = sizeof(kLoginBody_OEM_1);
|
||||||
|
} else if (body_len == sizeof(kPwdBody_DFI_WRITE) && memcmp(body, kPwdBody_DFI_WRITE, sizeof(kPwdBody_DFI_WRITE)) == 0) {
|
||||||
|
app.eeprom_unlocked_dfi_write = 1;
|
||||||
|
login_body = kLoginBody_DFI;
|
||||||
|
login_len = sizeof(kLoginBody_DFI);
|
||||||
|
} else {
|
||||||
|
/* Wrong password -- clear all locks */
|
||||||
|
app.eeprom_unlocked_dfi = 0;
|
||||||
|
app.eeprom_unlocked_dfi_write = 0;
|
||||||
|
app.rom_unlocked_cust = 0;
|
||||||
|
KL_Transport_SendNak();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t payload[1 + 16];
|
||||||
|
payload[0] = (uint8_t)KL_CMD_EepromLoginResponse;
|
||||||
|
memcpy(&payload[1], login_body, login_len);
|
||||||
|
start_response(payload, (uint8_t)(1U + login_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_write_eeprom(const KL_Packet *cmd)
|
||||||
|
{
|
||||||
|
if (cmd->data_len < 5U) { KL_Transport_SendNak(); app.state = KL_APP_SIMPLE_WAIT_TX; return; }
|
||||||
|
|
||||||
|
uint8_t len_req = cmd->data[0];
|
||||||
|
uint16_t addr = ((uint16_t)cmd->data[1] << 8) | cmd->data[2];
|
||||||
|
uint8_t value = cmd->data[3];
|
||||||
|
uint8_t csum = cmd->data[4];
|
||||||
|
|
||||||
|
if (!app.eeprom_unlocked_dfi_write) {
|
||||||
|
KL_Transport_SendNak();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr != 0x0044 || len_req != 0x02) {
|
||||||
|
KL_Transport_SendNak();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((uint8_t)(value + csum) != 0) {
|
||||||
|
KL_Transport_SendNak();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_dfi_code = (int8_t)value;
|
||||||
|
memWrite = 1;
|
||||||
|
|
||||||
|
uint8_t payload[1] = { (uint8_t)KL_CMD_WriteEepromResponse };
|
||||||
|
start_response(payload, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SYNC_PULSE_OUT;
|
||||||
|
|
||||||
|
static void handle_read_ram(const KL_Packet *cmd)
|
||||||
|
{
|
||||||
|
if (cmd->data_len >= sizeof(kReadIdentAddrBody) &&
|
||||||
|
memcmp(cmd->data, kReadIdentAddrBody, sizeof(kReadIdentAddrBody)) == 0) {
|
||||||
|
uint8_t payload[3];
|
||||||
|
payload[0] = 0xFE;
|
||||||
|
payload[1] = (uint8_t)(PSG_IDENT_ADDR & 0xFF);
|
||||||
|
payload[2] = (uint8_t)((PSG_IDENT_ADDR >> 8) & 0xFF);
|
||||||
|
start_response(payload, 3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd->data_len >= sizeof(kReadFahrSoftwareAddress) &&
|
||||||
|
memcmp(cmd->data, kReadFahrSoftwareAddress, sizeof(kReadFahrSoftwareAddress)) == 0) {
|
||||||
|
uint8_t payload[3];
|
||||||
|
payload[0] = 0xFE;
|
||||||
|
payload[1] = (uint8_t)(PSG_FAHRSOFTWARE_ADDR & 0xFF);
|
||||||
|
payload[2] = (uint8_t)((PSG_FAHRSOFTWARE_ADDR >> 8) & 0xFF);
|
||||||
|
start_response(payload, 3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd->data_len >= sizeof(kReadStart55Address) &&
|
||||||
|
memcmp(cmd->data, kReadStart55Address, sizeof(kReadStart55Address)) == 0) {
|
||||||
|
uint8_t payload[2];
|
||||||
|
payload[0] = 0xFE;
|
||||||
|
payload[1] = (uint8_t)(PSG_START55_ADDR);
|
||||||
|
start_response(payload, 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KL_Transport_SendNak();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_write_ram(const KL_Packet *cmd)
|
||||||
|
{
|
||||||
|
if (cmd->data_len >= sizeof(kActivateSyncOut) &&
|
||||||
|
memcmp(cmd->data, kActivateSyncOut, sizeof(kActivateSyncOut)) == 0) {
|
||||||
|
//SYNC_PULSE_OUT = 1;
|
||||||
|
SYNC_Pulse_RequestSyncout(1);
|
||||||
|
KL_Transport_SendAck();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KL_Transport_SendNak();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_end(void)
|
||||||
|
{
|
||||||
|
KL_Transport_SendAck();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
/* Session layer will check end_requested after we're done */
|
||||||
|
KL_Session_RequestEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Command dispatch
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_App_Dispatch(const KL_Packet *cmd)
|
||||||
|
{
|
||||||
|
app.state = KL_APP_IDLE;
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
|
||||||
|
switch (cmd->command) {
|
||||||
|
case KL_CMD_ACK:
|
||||||
|
handle_ack();
|
||||||
|
break;
|
||||||
|
case KL_CMD_ReadIdent:
|
||||||
|
handle_read_ident();
|
||||||
|
break;
|
||||||
|
case KL_CMD_FaultCodesRead:
|
||||||
|
handle_fault_codes_read();
|
||||||
|
break;
|
||||||
|
case KL_CMD_ReadEeprom:
|
||||||
|
handle_read_eeprom(cmd);
|
||||||
|
break;
|
||||||
|
case KL_CMD_ReadRomEeprom:
|
||||||
|
handle_read_rom_eeprom(cmd);
|
||||||
|
break;
|
||||||
|
case KL_CMD_LoginEeprom:
|
||||||
|
handle_login_eeprom(cmd);
|
||||||
|
break;
|
||||||
|
case KL_CMD_WriteEeprom:
|
||||||
|
handle_write_eeprom(cmd);
|
||||||
|
break;
|
||||||
|
case KL_CMD_ReadRam:
|
||||||
|
handle_read_ram(cmd);
|
||||||
|
break;
|
||||||
|
case KL_CMD_WriteRam:
|
||||||
|
handle_write_ram(cmd);
|
||||||
|
break;
|
||||||
|
case KL_CMD_End:
|
||||||
|
handle_end();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
KL_Transport_SendNak();
|
||||||
|
app.state = KL_APP_SIMPLE_WAIT_TX;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Service: drive response state machines
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_App_Service(void)
|
||||||
|
{
|
||||||
|
switch (app.state) {
|
||||||
|
|
||||||
|
case KL_APP_IDLE:
|
||||||
|
case KL_APP_DONE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---- Simple send (ACK/NAK, no follow-up ACK exchange) ---- */
|
||||||
|
case KL_APP_SIMPLE_WAIT_TX:
|
||||||
|
if (KL_Transport_TxDone()) {
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
app.state = KL_APP_DONE;
|
||||||
|
} else if (KL_Transport_TxError()) {
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
app.state = KL_APP_DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---- Single response: send and return to ready ---- */
|
||||||
|
case KL_APP_RESP_WAIT_TX:
|
||||||
|
if (KL_Transport_TxDone()) {
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
KL_Session_Kick();
|
||||||
|
app.state = KL_APP_DONE;
|
||||||
|
} else if (KL_Transport_TxError()) {
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
app.state = KL_APP_DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---- Stream: send chunks -> wait ACK each -> final ACK ---- */
|
||||||
|
case KL_APP_STREAM_SEND:
|
||||||
|
{
|
||||||
|
uint8_t remaining = app.stream_len - app.stream_offset;
|
||||||
|
app.stream_cur_chunk = (remaining > app.stream_chunk_size)
|
||||||
|
? app.stream_chunk_size : remaining;
|
||||||
|
|
||||||
|
uint8_t payload[1 + KL_DTC_CHUNK_SIZE]; /* max chunk size */
|
||||||
|
payload[0] = app.stream_cmd;
|
||||||
|
memcpy(&payload[1], &app.stream_buf[app.stream_offset], app.stream_cur_chunk);
|
||||||
|
|
||||||
|
if (KL_Transport_SendPacket(payload, (uint8_t)(1 + app.stream_cur_chunk))) {
|
||||||
|
app.state = KL_APP_STREAM_WAIT_TX;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case KL_APP_STREAM_WAIT_TX:
|
||||||
|
if (KL_Transport_TxDone()) {
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
app.stream_offset += app.stream_cur_chunk;
|
||||||
|
if (app.stream_offset >= app.stream_len) {
|
||||||
|
/* Last chunk sent -- return to ready state.
|
||||||
|
* No ACK is expected after the final chunk.
|
||||||
|
* The master's next packet (keepalive ACK or command)
|
||||||
|
* will be handled normally by KL_App_Dispatch. */
|
||||||
|
KL_Session_Kick();
|
||||||
|
app.state = KL_APP_DONE;
|
||||||
|
} else {
|
||||||
|
/* Intermediate chunk: wait for ACK before sending next */
|
||||||
|
KL_Transport_StartReceive(400);
|
||||||
|
app.state = KL_APP_STREAM_WAIT_ACK;
|
||||||
|
}
|
||||||
|
} else if (KL_Transport_TxError()) {
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
app.state = KL_APP_DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_APP_STREAM_WAIT_ACK:
|
||||||
|
if (KL_Transport_RxDone()) {
|
||||||
|
const KL_Packet *ack = KL_Transport_GetRxPacket();
|
||||||
|
KL_Transport_RxReset();
|
||||||
|
if (ack->command == (uint8_t)KL_CMD_ACK) {
|
||||||
|
KL_Session_Kick();
|
||||||
|
app.state = KL_APP_STREAM_SEND;
|
||||||
|
} else {
|
||||||
|
app.state = KL_APP_DONE;
|
||||||
|
}
|
||||||
|
} else if (KL_Transport_RxError()) {
|
||||||
|
KL_Transport_RxReset();
|
||||||
|
app.state = KL_APP_DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_APP_STREAM_FINAL_WAIT:
|
||||||
|
/* No longer used, but kept for safety */
|
||||||
|
if (KL_Transport_TxDone()) {
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
KL_Session_Kick();
|
||||||
|
app.state = KL_APP_DONE;
|
||||||
|
} else if (KL_Transport_TxError()) {
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
app.state = KL_APP_DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
app.state = KL_APP_DONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
Core/Kline_Libs/kl_app.h
Normal file
59
Core/Kline_Libs/kl_app.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* kl_app.h
|
||||||
|
* Application layer: command dispatch, handlers, stream sender.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KL_APP_H
|
||||||
|
#define KL_APP_H
|
||||||
|
|
||||||
|
#include "kl_protocol.h"
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Application states
|
||||||
|
* ================================================================ */
|
||||||
|
typedef enum {
|
||||||
|
KL_APP_IDLE,
|
||||||
|
|
||||||
|
/* Single response: send response -> wait ACK -> send final ACK */
|
||||||
|
KL_APP_RESP_TX,
|
||||||
|
KL_APP_RESP_WAIT_TX,
|
||||||
|
KL_APP_RESP_WAIT_ACK,
|
||||||
|
KL_APP_RESP_FINAL_ACK,
|
||||||
|
KL_APP_RESP_FINAL_WAIT,
|
||||||
|
|
||||||
|
/* Stream response: send chunks -> wait ACK each -> final ACK */
|
||||||
|
KL_APP_STREAM_SEND,
|
||||||
|
KL_APP_STREAM_WAIT_TX,
|
||||||
|
KL_APP_STREAM_WAIT_ACK,
|
||||||
|
KL_APP_STREAM_FINAL_ACK,
|
||||||
|
KL_APP_STREAM_FINAL_WAIT,
|
||||||
|
|
||||||
|
/* Simple send (ACK/NAK with no follow-up) */
|
||||||
|
KL_APP_SIMPLE_WAIT_TX,
|
||||||
|
|
||||||
|
KL_APP_DONE
|
||||||
|
} KL_AppState;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Fault code structure
|
||||||
|
* ================================================================ */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t dtc;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t extra;
|
||||||
|
} KL_FaultCode;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* API
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_App_Init(void);
|
||||||
|
void KL_App_Service(void);
|
||||||
|
void KL_App_Dispatch(const KL_Packet *cmd);
|
||||||
|
int KL_App_Busy(void);
|
||||||
|
int KL_App_Done(void);
|
||||||
|
void KL_App_ResetLocks(void);
|
||||||
|
|
||||||
|
/* DTC list management */
|
||||||
|
void KL_App_SetDTCList(const KL_FaultCode *list, size_t count);
|
||||||
|
|
||||||
|
#endif /* KL_APP_H */
|
||||||
162
Core/Kline_Libs/kl_phy.c
Normal file
162
Core/Kline_Libs/kl_phy.c
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* kl_phy.c
|
||||||
|
* Physical layer: UART driver, state-based echo suppression, RX FIFO.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kl_phy.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
extern UART_HandleTypeDef huart1;
|
||||||
|
|
||||||
|
static KL_Phy phy;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* RX FIFO (single-producer ISR, single-consumer main loop)
|
||||||
|
* ================================================================ */
|
||||||
|
static void rx_fifo_push(uint8_t b)
|
||||||
|
{
|
||||||
|
uint16_t next = (uint16_t)((phy.rx_head + 1U) % KL_RX_FIFO_SIZE);
|
||||||
|
if (next == phy.rx_tail) {
|
||||||
|
/* Overflow: drop oldest */
|
||||||
|
phy.rx_tail = (uint16_t)((phy.rx_tail + 1U) % KL_RX_FIFO_SIZE);
|
||||||
|
}
|
||||||
|
phy.rx_fifo[phy.rx_head] = b;
|
||||||
|
phy.rx_head = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KL_Phy_RxPop(uint8_t *out)
|
||||||
|
{
|
||||||
|
if (phy.rx_head == phy.rx_tail) return 0;
|
||||||
|
if (out) *out = phy.rx_fifo[phy.rx_tail];
|
||||||
|
phy.rx_tail = (uint16_t)((phy.rx_tail + 1U) % KL_RX_FIFO_SIZE);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KL_Phy_RxAvailable(void)
|
||||||
|
{
|
||||||
|
return (phy.rx_head != phy.rx_tail) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KL_Phy_RxFlush(void)
|
||||||
|
{
|
||||||
|
phy.rx_tail = phy.rx_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* GPIO pin configuration helpers
|
||||||
|
* ================================================================ */
|
||||||
|
static void phy_set_pins_uart_af(void)
|
||||||
|
{
|
||||||
|
GPIO_InitTypeDef gi = {0};
|
||||||
|
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||||
|
|
||||||
|
gi.Pin = KL_TX_PIN | KL_RX_PIN;
|
||||||
|
gi.Mode = GPIO_MODE_AF_PP;
|
||||||
|
gi.Pull = GPIO_PULLUP;
|
||||||
|
gi.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
||||||
|
gi.Alternate = GPIO_AF7_USART1;
|
||||||
|
HAL_GPIO_Init(KL_GPIO_PORT, &gi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Init / Mode switching
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Phy_Init(void)
|
||||||
|
{
|
||||||
|
memset(&phy, 0, sizeof(phy));
|
||||||
|
phy.tx_state = KL_PHY_TX_IDLE;
|
||||||
|
HAL_UART_DeInit(&huart1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KL_Phy_EnableUart9600(void)
|
||||||
|
{
|
||||||
|
HAL_UART_DeInit(&huart1);
|
||||||
|
phy_set_pins_uart_af();
|
||||||
|
|
||||||
|
huart1.Instance = USART1;
|
||||||
|
huart1.Init.BaudRate = 9600;
|
||||||
|
huart1.Init.WordLength = UART_WORDLENGTH_8B;
|
||||||
|
huart1.Init.StopBits = UART_STOPBITS_1;
|
||||||
|
huart1.Init.Parity = UART_PARITY_NONE;
|
||||||
|
huart1.Init.Mode = UART_MODE_TX_RX;
|
||||||
|
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
|
||||||
|
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
|
||||||
|
if (HAL_UART_Init(&huart1) != HAL_OK) {
|
||||||
|
Error_Handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
KL_Phy_RxFlush();
|
||||||
|
phy.tx_state = KL_PHY_TX_IDLE;
|
||||||
|
phy.echo_pending = 0;
|
||||||
|
|
||||||
|
HAL_UART_Receive_IT(&huart1, phy.rx_it_buf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KL_Phy_DisableUart(void)
|
||||||
|
{
|
||||||
|
HAL_UART_DeInit(&huart1);
|
||||||
|
phy.tx_state = KL_PHY_TX_IDLE;
|
||||||
|
phy.echo_pending = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Non-blocking single-byte TX
|
||||||
|
* ================================================================ */
|
||||||
|
int KL_Phy_TxByte(uint8_t byte)
|
||||||
|
{
|
||||||
|
if (phy.tx_state != KL_PHY_TX_IDLE) return 0;
|
||||||
|
|
||||||
|
phy.tx_byte = byte;
|
||||||
|
phy.last_tx_byte = byte;
|
||||||
|
phy.echo_pending = 1;
|
||||||
|
phy.tx_state = KL_PHY_TX_SENDING;
|
||||||
|
|
||||||
|
if (HAL_UART_Transmit_IT(&huart1, &phy.tx_byte, 1) != HAL_OK) {
|
||||||
|
phy.tx_state = KL_PHY_TX_IDLE;
|
||||||
|
phy.echo_pending = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KL_Phy_TxBusy(void)
|
||||||
|
{
|
||||||
|
return (phy.tx_state != KL_PHY_TX_IDLE) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Service: echo-timeout recovery
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Phy_Service(void)
|
||||||
|
{
|
||||||
|
if (phy.tx_state == KL_PHY_TX_ECHO_WAIT) {
|
||||||
|
if (kl_tick_diff(HAL_GetTick(), phy.tx_done_tick) >= (int32_t)KL_ECHO_TMO_MS) {
|
||||||
|
phy.tx_state = KL_PHY_TX_IDLE;
|
||||||
|
phy.echo_pending = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* ISR callbacks
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Phy_TxCpltCB(void)
|
||||||
|
{
|
||||||
|
phy.tx_done_tick = HAL_GetTick();
|
||||||
|
phy.tx_state = KL_PHY_TX_ECHO_WAIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KL_Phy_RxCpltCB(void)
|
||||||
|
{
|
||||||
|
uint8_t byte = phy.rx_it_buf[0];
|
||||||
|
|
||||||
|
if (phy.echo_pending && byte == phy.last_tx_byte) {
|
||||||
|
phy.echo_pending = 0;
|
||||||
|
phy.tx_state = KL_PHY_TX_IDLE;
|
||||||
|
} else {
|
||||||
|
rx_fifo_push(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
HAL_UART_Receive_IT(&huart1, phy.rx_it_buf, 1);
|
||||||
|
}
|
||||||
56
Core/Kline_Libs/kl_phy.h
Normal file
56
Core/Kline_Libs/kl_phy.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* kl_phy.h
|
||||||
|
* Physical layer: UART driver, state-based echo suppression, RX FIFO.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KL_PHY_H
|
||||||
|
#define KL_PHY_H
|
||||||
|
|
||||||
|
#include "kl_protocol.h"
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* TX state
|
||||||
|
* ================================================================ */
|
||||||
|
typedef enum {
|
||||||
|
KL_PHY_TX_IDLE, /* No TX in progress */
|
||||||
|
KL_PHY_TX_SENDING, /* HAL_UART_Transmit_IT issued */
|
||||||
|
KL_PHY_TX_ECHO_WAIT /* TxCplt fired, awaiting self-echo */
|
||||||
|
} KL_PhyTxState;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Physical layer context
|
||||||
|
* ================================================================ */
|
||||||
|
typedef struct {
|
||||||
|
volatile KL_PhyTxState tx_state;
|
||||||
|
uint8_t tx_byte; /* Byte buffer for Transmit_IT */
|
||||||
|
uint8_t last_tx_byte; /* For echo matching */
|
||||||
|
volatile uint32_t tx_done_tick; /* Tick when TxCplt fired */
|
||||||
|
volatile uint8_t echo_pending; /* Expecting self-echo on RX */
|
||||||
|
|
||||||
|
uint8_t rx_fifo[KL_RX_FIFO_SIZE];
|
||||||
|
volatile uint16_t rx_head; /* Written by ISR */
|
||||||
|
volatile uint16_t rx_tail; /* Read by main loop */
|
||||||
|
|
||||||
|
uint8_t rx_it_buf[1]; /* HAL_UART_Receive_IT buffer */
|
||||||
|
} KL_Phy;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* API
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Phy_Init(void);
|
||||||
|
void KL_Phy_Service(void); /* Echo-timeout recovery */
|
||||||
|
void KL_Phy_EnableUart9600(void);
|
||||||
|
void KL_Phy_DisableUart(void);
|
||||||
|
|
||||||
|
int KL_Phy_TxByte(uint8_t byte); /* Returns 1 accepted, 0 busy */
|
||||||
|
int KL_Phy_TxBusy(void); /* Includes echo wait */
|
||||||
|
|
||||||
|
int KL_Phy_RxPop(uint8_t *out); /* Returns 1 if byte available */
|
||||||
|
int KL_Phy_RxAvailable(void);
|
||||||
|
void KL_Phy_RxFlush(void);
|
||||||
|
|
||||||
|
/* ISR callbacks -- call from HAL callbacks */
|
||||||
|
void KL_Phy_TxCpltCB(void);
|
||||||
|
void KL_Phy_RxCpltCB(void);
|
||||||
|
|
||||||
|
#endif /* KL_PHY_H */
|
||||||
118
Core/Kline_Libs/kl_protocol.h
Normal file
118
Core/Kline_Libs/kl_protocol.h
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* kl_protocol.h
|
||||||
|
* Shared constants, enums, and types for the K-Line protocol stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KL_PROTOCOL_H
|
||||||
|
#define KL_PROTOCOL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Hardware pin mapping (USART1 on PA9/PA10)
|
||||||
|
* ================================================================ */
|
||||||
|
#define KL_RX_PIN GPIO_PIN_10
|
||||||
|
#define KL_TX_PIN GPIO_PIN_9
|
||||||
|
#define KL_GPIO_PORT GPIOA
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Protocol constants
|
||||||
|
* ================================================================ */
|
||||||
|
#define KL_SLAVE_ADDR 0xF1
|
||||||
|
#define KL_SYNC_BYTE 0x55
|
||||||
|
#define KL_KEYWORD_LSB 0x8C
|
||||||
|
#define KL_KEYWORD_MSB 0x51
|
||||||
|
#define KL_PACKET_END 0x03
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Timing (milliseconds)
|
||||||
|
* ================================================================ */
|
||||||
|
#define KL_5BAUD_BIT_MS 200U /* 5-baud bit period */
|
||||||
|
#define KL_5BAUD_FIRST_MS 300U /* 1.5 bit times for first sample */
|
||||||
|
#define KL_W1_MS 200U /* Post-5baud delay before sync */
|
||||||
|
#define KL_W2_MS 11U /* Sync to keyword LSB */
|
||||||
|
#define KL_W3_MS 11U /* Keyword LSB to MSB */
|
||||||
|
#define KL_HS_RETRY_MS 43U /* Between handshake retries */
|
||||||
|
#define KL_P1_GAP_MS 5U /* ECU inter-byte gap */
|
||||||
|
#define KL_SESSION_TMO_MS 1000U /* Session keepalive timeout */
|
||||||
|
#define KL_BYTE_TMO_MS 50U /* Per-byte RX timeout */
|
||||||
|
#define KL_PKT_TMO_MS 800U /* Overall packet RX timeout */
|
||||||
|
#define KL_COMPL_TMO_MS 25U /* Complement wait timeout */
|
||||||
|
#define KL_ECHO_TMO_MS 3U /* Max time to wait for echo */
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Buffer sizing
|
||||||
|
* ================================================================ */
|
||||||
|
#define KL_MAX_PACKET_PAYLOAD 16 /* KWP1281 max block data */
|
||||||
|
#define KL_MAX_PACKET_RAW 20 /* LEN+CNT+CMD+16+0x03+margin */
|
||||||
|
#define KL_RX_FIFO_SIZE 64
|
||||||
|
#define KL_IDENT_BUF_SIZE 64
|
||||||
|
#define KL_STREAM_BUF_SIZE 64
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Handshake
|
||||||
|
* ================================================================ */
|
||||||
|
#define KL_HANDSHAKE_MAX_RETRIES 5
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* ASCII / DTC chunk sizes
|
||||||
|
* ================================================================ */
|
||||||
|
#define KL_ASCII_CHUNK_SIZE 13 /* Data bytes per ASCII packet */
|
||||||
|
#define KL_DTC_CHUNK_SIZE 15 /* Data bytes per DTC packet */
|
||||||
|
#define KL_DTC_RECORD_SIZE 8
|
||||||
|
#define KL_DTC_TOTAL_RECORDS 8
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Packet command enum
|
||||||
|
* ================================================================ */
|
||||||
|
typedef enum {
|
||||||
|
KL_CMD_ReadIdent = 0x00,
|
||||||
|
KL_CMD_ReadRam = 0x01,
|
||||||
|
KL_CMD_WriteRam = 0x02,
|
||||||
|
KL_CMD_ReadRomEeprom = 0x03,
|
||||||
|
KL_CMD_ActuatorTest = 0x04,
|
||||||
|
KL_CMD_FaultCodesDelete = 0x05,
|
||||||
|
KL_CMD_End = 0x06,
|
||||||
|
KL_CMD_FaultCodesRead = 0x07,
|
||||||
|
KL_CMD_SingleRead = 0x08,
|
||||||
|
KL_CMD_ACK = 0x09,
|
||||||
|
KL_CMD_NAK = 0x0A,
|
||||||
|
KL_CMD_SoftwareCoding = 0x10,
|
||||||
|
KL_CMD_LoginEeprom = 0x18,
|
||||||
|
KL_CMD_ReadEeprom = 0x19,
|
||||||
|
KL_CMD_WriteEeprom = 0x1A,
|
||||||
|
KL_CMD_Custom = 0x1B,
|
||||||
|
KL_CMD_GroupReading = 0x29,
|
||||||
|
KL_CMD_Login = 0x2B,
|
||||||
|
KL_CMD_GroupReadingResponse = 0xE7,
|
||||||
|
KL_CMD_ReadEepromResponse = 0xEF,
|
||||||
|
KL_CMD_EepromLoginResponse = 0xF0,
|
||||||
|
KL_CMD_ActuatorTestResponse = 0xF5,
|
||||||
|
KL_CMD_AsciiData = 0xF6,
|
||||||
|
KL_CMD_WriteEepromResponse = 0xF9,
|
||||||
|
KL_CMD_SingleReadResponse = 0xFB,
|
||||||
|
KL_CMD_FaultCodesResponse = 0xFC,
|
||||||
|
KL_CMD_ReadRomResponse = 0xFD
|
||||||
|
} KL_PacketCmd;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Packet structure (parsed)
|
||||||
|
* ================================================================ */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t length; /* LENGTH field from wire */
|
||||||
|
uint8_t counter; /* COUNTER field */
|
||||||
|
uint8_t command; /* COMMAND field */
|
||||||
|
uint8_t data[KL_MAX_PACKET_PAYLOAD]; /* Data bytes after COMMAND */
|
||||||
|
uint8_t data_len; /* Number of data bytes */
|
||||||
|
} KL_Packet;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Inline helper
|
||||||
|
* ================================================================ */
|
||||||
|
static inline int32_t kl_tick_diff(uint32_t now, uint32_t ref)
|
||||||
|
{
|
||||||
|
return (int32_t)(now - ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* KL_PROTOCOL_H */
|
||||||
501
Core/Kline_Libs/kl_session.c
Normal file
501
Core/Kline_Libs/kl_session.c
Normal file
@@ -0,0 +1,501 @@
|
|||||||
|
/*
|
||||||
|
* kl_session.c
|
||||||
|
* Session layer: 5-baud init, handshake, ident stream, session lifecycle.
|
||||||
|
*
|
||||||
|
* All operations are non-blocking state machines.
|
||||||
|
* The 5-baud sampling is ISR-driven via TIM17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kl_session.h"
|
||||||
|
#include "kl_phy.h"
|
||||||
|
#include "kl_transport.h"
|
||||||
|
#include "kl_app.h"
|
||||||
|
#include "psg_prop.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
extern TIM_HandleTypeDef htim17;
|
||||||
|
|
||||||
|
static KL_Session sess;
|
||||||
|
|
||||||
|
/* Forward declarations for BuildCombined (ident data) */
|
||||||
|
static size_t sess_build_ident(uint8_t *out, size_t maxlen);
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* TIM17 configuration (0.1ms tick for 5-baud sampling)
|
||||||
|
* ================================================================ */
|
||||||
|
static void tim17_config(void)
|
||||||
|
{
|
||||||
|
__HAL_TIM_DISABLE(&htim17);
|
||||||
|
__HAL_TIM_DISABLE_IT(&htim17, TIM_IT_UPDATE);
|
||||||
|
__HAL_TIM_CLEAR_FLAG(&htim17, TIM_FLAG_UPDATE);
|
||||||
|
|
||||||
|
uint32_t timclk = HAL_RCC_GetPCLK2Freq();
|
||||||
|
RCC_ClkInitTypeDef clk;
|
||||||
|
uint32_t fl;
|
||||||
|
HAL_RCC_GetClockConfig(&clk, &fl);
|
||||||
|
if (clk.APB2CLKDivider != RCC_HCLK_DIV1) timclk *= 2U;
|
||||||
|
|
||||||
|
const uint32_t target_hz = 10000U;
|
||||||
|
uint32_t psc = (timclk + target_hz - 1U) / target_hz - 1U;
|
||||||
|
if (psc > 0xFFFFU) psc = 0xFFFFU;
|
||||||
|
|
||||||
|
__HAL_TIM_SET_PRESCALER(&htim17, psc);
|
||||||
|
__HAL_TIM_SET_AUTORELOAD(&htim17, 9U);
|
||||||
|
__HAL_TIM_SET_COUNTER(&htim17, 0U);
|
||||||
|
|
||||||
|
htim17.Instance->CR1 |= (TIM_CR1_OPM | TIM_CR1_URS);
|
||||||
|
htim17.Instance->EGR = TIM_EGR_UG;
|
||||||
|
__HAL_TIM_CLEAR_FLAG(&htim17, TIM_FLAG_UPDATE);
|
||||||
|
|
||||||
|
HAL_NVIC_SetPriority(TIM1_TRG_COM_TIM17_IRQn, 6, 0);
|
||||||
|
HAL_NVIC_EnableIRQ(TIM1_TRG_COM_TIM17_IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tim17_schedule(uint32_t delay_ms)
|
||||||
|
{
|
||||||
|
uint32_t ticks = delay_ms * 10U;
|
||||||
|
if (ticks < 1U) ticks = 1U;
|
||||||
|
if (ticks > 0x10000U) ticks = 0x10000U;
|
||||||
|
|
||||||
|
__HAL_TIM_DISABLE(&htim17);
|
||||||
|
__HAL_TIM_DISABLE_IT(&htim17, TIM_IT_UPDATE);
|
||||||
|
__HAL_TIM_CLEAR_FLAG(&htim17, TIM_FLAG_UPDATE);
|
||||||
|
|
||||||
|
__HAL_TIM_SET_AUTORELOAD(&htim17, (uint16_t)(ticks - 1U));
|
||||||
|
__HAL_TIM_SET_COUNTER(&htim17, 0U);
|
||||||
|
|
||||||
|
htim17.Instance->EGR = TIM_EGR_UG;
|
||||||
|
__HAL_TIM_CLEAR_FLAG(&htim17, TIM_FLAG_UPDATE);
|
||||||
|
|
||||||
|
htim17.Instance->CR1 |= (TIM_CR1_OPM | TIM_CR1_URS);
|
||||||
|
|
||||||
|
__HAL_TIM_ENABLE_IT(&htim17, TIM_IT_UPDATE);
|
||||||
|
__HAL_TIM_ENABLE(&htim17);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* EXTI / 5-baud arming
|
||||||
|
* ================================================================ */
|
||||||
|
static void sess_rearm_5baud(void)
|
||||||
|
{
|
||||||
|
KL_Phy_DisableUart();
|
||||||
|
|
||||||
|
__HAL_RCC_SYSCFG_CLK_ENABLE();
|
||||||
|
|
||||||
|
/* TX pin idle high (input with pull-up) */
|
||||||
|
GPIO_InitTypeDef giTx = {0};
|
||||||
|
giTx.Pin = KL_TX_PIN;
|
||||||
|
giTx.Mode = GPIO_MODE_INPUT;
|
||||||
|
giTx.Pull = GPIO_PULLUP;
|
||||||
|
giTx.Speed = GPIO_SPEED_FREQ_LOW;
|
||||||
|
HAL_GPIO_Init(KL_GPIO_PORT, &giTx);
|
||||||
|
HAL_GPIO_WritePin(KL_GPIO_PORT, KL_TX_PIN, GPIO_PIN_SET);
|
||||||
|
|
||||||
|
/* RX pin: EXTI falling edge */
|
||||||
|
GPIO_InitTypeDef giRx = {0};
|
||||||
|
giRx.Pin = KL_RX_PIN;
|
||||||
|
giRx.Mode = GPIO_MODE_IT_FALLING;
|
||||||
|
giRx.Pull = GPIO_PULLUP;
|
||||||
|
HAL_GPIO_Init(KL_GPIO_PORT, &giRx);
|
||||||
|
|
||||||
|
__HAL_GPIO_EXTI_CLEAR_IT(KL_RX_PIN);
|
||||||
|
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 5, 0);
|
||||||
|
HAL_NVIC_ClearPendingIRQ(EXTI15_10_IRQn);
|
||||||
|
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sess_reset_to_idle(void)
|
||||||
|
{
|
||||||
|
sess.state = KL_SESS_IDLE;
|
||||||
|
sess.five_bit_index = 0;
|
||||||
|
sess.five_byte = 0;
|
||||||
|
sess.five_active = 0;
|
||||||
|
sess.connection_status = 0;
|
||||||
|
sess.end_requested = 0;
|
||||||
|
|
||||||
|
KL_Transport_ResetCounter();
|
||||||
|
|
||||||
|
HAL_TIM_Base_Stop_IT(&htim17);
|
||||||
|
__HAL_TIM_DISABLE_IT(&htim17, TIM_IT_UPDATE);
|
||||||
|
|
||||||
|
__HAL_GPIO_EXTI_CLEAR_IT(KL_RX_PIN);
|
||||||
|
HAL_NVIC_ClearPendingIRQ(EXTI15_10_IRQn);
|
||||||
|
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Init
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Session_Init(void)
|
||||||
|
{
|
||||||
|
memset(&sess, 0, sizeof(sess));
|
||||||
|
KL_Phy_Init();
|
||||||
|
tim17_config();
|
||||||
|
sess_rearm_5baud();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* ISR: GPIO EXTI falling edge on RX pin (start of 5-baud)
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Session_OnExtiRxFalling(void)
|
||||||
|
{
|
||||||
|
if (sess.state == KL_SESS_IDLE) {
|
||||||
|
HAL_NVIC_DisableIRQ(EXTI15_10_IRQn);
|
||||||
|
sess.five_active = 1;
|
||||||
|
sess.five_bit_index = 0;
|
||||||
|
sess.five_byte = 0;
|
||||||
|
sess.state = KL_SESS_5BAUD_SAMPLING;
|
||||||
|
tim17_schedule(KL_5BAUD_FIRST_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* ISR: TIM17 period elapsed (5-baud bit sampling)
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Session_OnTim17Elapsed(void)
|
||||||
|
{
|
||||||
|
if (!sess.five_active) {
|
||||||
|
HAL_TIM_Base_Stop_IT(&htim17);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sess.state == KL_SESS_5BAUD_SAMPLING) {
|
||||||
|
GPIO_PinState pin = HAL_GPIO_ReadPin(KL_GPIO_PORT, KL_RX_PIN);
|
||||||
|
uint8_t bit = (pin == GPIO_PIN_SET) ? 1U : 0U;
|
||||||
|
|
||||||
|
sess.five_byte |= (bit << sess.five_bit_index);
|
||||||
|
sess.five_bit_index++;
|
||||||
|
|
||||||
|
if (sess.five_bit_index >= 8) {
|
||||||
|
sess.five_active = 0;
|
||||||
|
HAL_TIM_Base_Stop_IT(&htim17);
|
||||||
|
|
||||||
|
if (sess.five_byte != KL_SLAVE_ADDR) {
|
||||||
|
sess_reset_to_idle();
|
||||||
|
sess_rearm_5baud();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sess.connection_status = 1;
|
||||||
|
sess.hs_next_tick = HAL_GetTick() + KL_W1_MS;
|
||||||
|
sess.state = KL_SESS_5BAUD_DONE;
|
||||||
|
} else {
|
||||||
|
tim17_schedule(KL_5BAUD_BIT_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Session kick (extend deadline)
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Session_Kick(void)
|
||||||
|
{
|
||||||
|
sess.session_deadline = HAL_GetTick() + KL_SESSION_TMO_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KL_Session_RequestEnd(void)
|
||||||
|
{
|
||||||
|
sess.end_requested = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t KL_Session_GetStatus(void)
|
||||||
|
{
|
||||||
|
return sess.connection_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Build identification data
|
||||||
|
* ================================================================ */
|
||||||
|
static size_t append_str(const char *s, size_t n, uint8_t *out, size_t w, size_t maxlen)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < n && s[i] != '\0' && w < maxlen; i++) {
|
||||||
|
out[w++] = (uint8_t)s[i];
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t sess_build_ident(uint8_t *out, size_t maxlen)
|
||||||
|
{
|
||||||
|
size_t w = 0;
|
||||||
|
w = append_str(PSG_KUNDENNUMMER_STR, 12, out, w, maxlen);
|
||||||
|
w = append_str(PSG_DATENSATZ_STR, 10, out, w, maxlen);
|
||||||
|
w = append_str(PSG_SOFTWARE_VER_STR, 10, out, w, maxlen);
|
||||||
|
w = append_str(PSG_SOFTWARE_VER2_STR, 10, out, w, maxlen);
|
||||||
|
w = append_str(PSG_STEUERGERAET_STR, 10, out, w, maxlen);
|
||||||
|
w = append_str(PSG_UNKNOWN_STR, 6, out, w, maxlen);
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Terminate session and rearm 5-baud
|
||||||
|
* ================================================================ */
|
||||||
|
static void sess_terminate(void)
|
||||||
|
{
|
||||||
|
sess.connection_status = 0;
|
||||||
|
sess.session_deadline = 0;
|
||||||
|
sess.end_requested = 0;
|
||||||
|
|
||||||
|
KL_App_ResetLocks();
|
||||||
|
KL_Transport_ResetCounter();
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
KL_Transport_RxReset();
|
||||||
|
KL_Phy_DisableUart();
|
||||||
|
sess_rearm_5baud();
|
||||||
|
|
||||||
|
sess.state = KL_SESS_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Main session service (called from KLine_Service)
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Session_Service(void)
|
||||||
|
{
|
||||||
|
uint32_t now = HAL_GetTick();
|
||||||
|
|
||||||
|
switch (sess.state) {
|
||||||
|
|
||||||
|
case KL_SESS_IDLE:
|
||||||
|
case KL_SESS_5BAUD_SAMPLING:
|
||||||
|
/* Driven by ISR */
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---- 5-baud done: wait W1 then enter UART mode ---- */
|
||||||
|
case KL_SESS_5BAUD_DONE:
|
||||||
|
if (kl_tick_diff(now, sess.hs_next_tick) >= 0) {
|
||||||
|
KL_Phy_EnableUart9600();
|
||||||
|
KL_Transport_Init();
|
||||||
|
KL_Phy_RxFlush();
|
||||||
|
|
||||||
|
sess.hs_retry_count = 0;
|
||||||
|
sess.hs_next_tick = now;
|
||||||
|
sess.connection_status = 1;
|
||||||
|
sess.state = KL_SESS_HS_SYNC;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---- Handshake: send sync byte ---- */
|
||||||
|
case KL_SESS_HS_SYNC:
|
||||||
|
if (kl_tick_diff(now, sess.hs_next_tick) >= 0) {
|
||||||
|
if (KL_Transport_SendRawByte(KL_SYNC_BYTE)) {
|
||||||
|
sess.state = KL_SESS_HS_SYNC_WAIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_SESS_HS_SYNC_WAIT:
|
||||||
|
if (KL_Transport_RawTxDone()) {
|
||||||
|
sess.hs_next_tick = HAL_GetTick() + KL_W2_MS;
|
||||||
|
KL_Transport_RawTxReset();
|
||||||
|
sess.state = KL_SESS_HS_KW_LSB;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---- Handshake: send keyword LSB ---- */
|
||||||
|
case KL_SESS_HS_KW_LSB:
|
||||||
|
if (kl_tick_diff(now, sess.hs_next_tick) >= 0) {
|
||||||
|
if (KL_Transport_SendRawByte(KL_KEYWORD_LSB)) {
|
||||||
|
sess.state = KL_SESS_HS_KW_LSB_WAIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_SESS_HS_KW_LSB_WAIT:
|
||||||
|
if (KL_Transport_RawTxDone()) {
|
||||||
|
sess.hs_next_tick = HAL_GetTick() + KL_W3_MS;
|
||||||
|
KL_Transport_RawTxReset();
|
||||||
|
sess.state = KL_SESS_HS_KW_MSB;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---- Handshake: send keyword MSB ---- */
|
||||||
|
case KL_SESS_HS_KW_MSB:
|
||||||
|
if (kl_tick_diff(now, sess.hs_next_tick) >= 0) {
|
||||||
|
if (KL_Transport_SendRawByte(KL_KEYWORD_MSB)) {
|
||||||
|
sess.state = KL_SESS_HS_KW_MSB_WAIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_SESS_HS_KW_MSB_WAIT:
|
||||||
|
if (KL_Transport_RawTxDone()) {
|
||||||
|
KL_Transport_RawTxReset();
|
||||||
|
/* Set deadline for master ACK */
|
||||||
|
sess.hs_next_tick = HAL_GetTick() + KL_HS_RETRY_MS;
|
||||||
|
sess.state = KL_SESS_HS_WAIT_ACK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---- Handshake: wait for master ~KW_MSB ---- */
|
||||||
|
case KL_SESS_HS_WAIT_ACK:
|
||||||
|
{
|
||||||
|
uint8_t rx_byte;
|
||||||
|
if (KL_Phy_RxPop(&rx_byte)) {
|
||||||
|
if (rx_byte == (uint8_t)~KL_KEYWORD_MSB) {
|
||||||
|
/* Handshake succeeded -- prepare identification */
|
||||||
|
sess.ident_len = (uint8_t)sess_build_ident(sess.ident_buf,
|
||||||
|
sizeof(sess.ident_buf));
|
||||||
|
sess.ident_offset = 0;
|
||||||
|
KL_Phy_RxFlush();
|
||||||
|
sess.state = KL_SESS_IDENT_SEND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Unexpected byte, fall through to retry */
|
||||||
|
}
|
||||||
|
if (kl_tick_diff(now, sess.hs_next_tick) >= 0) {
|
||||||
|
sess.hs_retry_count++;
|
||||||
|
if (sess.hs_retry_count >= KL_HANDSHAKE_MAX_RETRIES) {
|
||||||
|
sess_terminate();
|
||||||
|
} else {
|
||||||
|
sess.hs_next_tick = now;
|
||||||
|
sess.state = KL_SESS_HS_SYNC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Identification: send ASCII chunks ---- */
|
||||||
|
case KL_SESS_IDENT_SEND:
|
||||||
|
{
|
||||||
|
uint8_t remaining = sess.ident_len - sess.ident_offset;
|
||||||
|
sess.ident_chunk_len = (remaining > KL_ASCII_CHUNK_SIZE)
|
||||||
|
? KL_ASCII_CHUNK_SIZE : remaining;
|
||||||
|
|
||||||
|
uint8_t payload[1 + KL_ASCII_CHUNK_SIZE];
|
||||||
|
payload[0] = (uint8_t)KL_CMD_AsciiData;
|
||||||
|
memcpy(&payload[1], &sess.ident_buf[sess.ident_offset], sess.ident_chunk_len);
|
||||||
|
|
||||||
|
if (KL_Transport_SendPacket(payload, (uint8_t)(1 + sess.ident_chunk_len))) {
|
||||||
|
sess.state = KL_SESS_IDENT_WAIT_TX;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case KL_SESS_IDENT_WAIT_TX:
|
||||||
|
if (KL_Transport_TxDone()) {
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
sess.ident_offset += sess.ident_chunk_len;
|
||||||
|
if (sess.ident_offset >= sess.ident_len) {
|
||||||
|
/* Last chunk sent -- go straight to ready.
|
||||||
|
* No ACK expected after the final chunk. */
|
||||||
|
KL_Session_Kick();
|
||||||
|
sess.connection_status = 2;
|
||||||
|
sess.state = KL_SESS_READY;
|
||||||
|
} else {
|
||||||
|
/* Intermediate chunk: wait for ACK before next */
|
||||||
|
KL_Transport_StartReceive(400);
|
||||||
|
sess.state = KL_SESS_IDENT_WAIT_ACK;
|
||||||
|
}
|
||||||
|
} else if (KL_Transport_TxError()) {
|
||||||
|
sess_terminate();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_SESS_IDENT_WAIT_ACK:
|
||||||
|
if (KL_Transport_RxDone()) {
|
||||||
|
const KL_Packet *pkt = KL_Transport_GetRxPacket();
|
||||||
|
KL_Transport_RxReset();
|
||||||
|
if (pkt->command == (uint8_t)KL_CMD_ACK) {
|
||||||
|
sess.state = KL_SESS_IDENT_SEND;
|
||||||
|
} else {
|
||||||
|
sess_terminate();
|
||||||
|
}
|
||||||
|
} else if (KL_Transport_RxError()) {
|
||||||
|
sess_terminate();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_SESS_IDENT_FINAL_WAIT:
|
||||||
|
/* No longer used, kept for enum completeness */
|
||||||
|
if (KL_Transport_TxDone()) {
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
KL_Session_Kick();
|
||||||
|
sess.connection_status = 2;
|
||||||
|
sess.state = KL_SESS_READY;
|
||||||
|
} else if (KL_Transport_TxError()) {
|
||||||
|
sess_terminate();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---- Active session: ready to receive commands ---- */
|
||||||
|
case KL_SESS_READY:
|
||||||
|
if (kl_tick_diff(now, sess.session_deadline) >= 0) {
|
||||||
|
sess_terminate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Use the session deadline as the RX timeout so we don't
|
||||||
|
* artificially cut short a receive that started late.
|
||||||
|
* Compute remaining time until session deadline. */
|
||||||
|
{
|
||||||
|
int32_t remaining = (int32_t)(sess.session_deadline - now);
|
||||||
|
if (remaining <= 0) { sess_terminate(); break; }
|
||||||
|
if (KL_Transport_StartReceive((uint32_t)remaining)) {
|
||||||
|
sess.state = KL_SESS_CMD_RX;
|
||||||
|
}
|
||||||
|
/* If StartReceive fails (rx not idle), stay in READY
|
||||||
|
* and retry next cycle rather than advancing to CMD_RX
|
||||||
|
* with no active receive. */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_SESS_CMD_RX:
|
||||||
|
if (kl_tick_diff(now, sess.session_deadline) >= 0) {
|
||||||
|
KL_Transport_RxReset();
|
||||||
|
sess_terminate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (KL_Transport_RxDone()) {
|
||||||
|
KL_Session_Kick();
|
||||||
|
sess.state = KL_SESS_CMD_DISPATCH;
|
||||||
|
} else if (KL_Transport_RxError()) {
|
||||||
|
KL_Transport_RxReset();
|
||||||
|
/* Only send NAK if we actually received partial data
|
||||||
|
* (i.e., at least the length byte was consumed).
|
||||||
|
* A plain timeout with no data is just "no packet yet"
|
||||||
|
* and should silently retry. */
|
||||||
|
if (KL_Transport_RxHadData()) {
|
||||||
|
KL_Transport_SendNak();
|
||||||
|
sess.state = KL_SESS_CMD_NAK_WAIT;
|
||||||
|
} else {
|
||||||
|
sess.state = KL_SESS_READY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_SESS_CMD_NAK_WAIT:
|
||||||
|
if (kl_tick_diff(now, sess.session_deadline) >= 0) {
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
sess_terminate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (KL_Transport_TxDone() || KL_Transport_TxError()) {
|
||||||
|
KL_Transport_TxReset();
|
||||||
|
sess.state = KL_SESS_READY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_SESS_CMD_DISPATCH:
|
||||||
|
{
|
||||||
|
const KL_Packet *pkt = KL_Transport_GetRxPacket();
|
||||||
|
KL_Transport_RxReset();
|
||||||
|
KL_App_Dispatch(pkt);
|
||||||
|
sess.state = KL_SESS_CMD_RESPONSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case KL_SESS_CMD_RESPONSE:
|
||||||
|
if (sess.end_requested) {
|
||||||
|
sess_terminate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (KL_App_Done()) {
|
||||||
|
KL_Session_Kick();
|
||||||
|
sess.state = KL_SESS_READY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_SESS_TERMINATE:
|
||||||
|
sess_terminate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
92
Core/Kline_Libs/kl_session.h
Normal file
92
Core/Kline_Libs/kl_session.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* kl_session.h
|
||||||
|
* Session layer: 5-baud init, handshake, ident stream, session lifecycle.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KL_SESSION_H
|
||||||
|
#define KL_SESSION_H
|
||||||
|
|
||||||
|
#include "kl_protocol.h"
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Session states
|
||||||
|
* ================================================================ */
|
||||||
|
typedef enum {
|
||||||
|
KL_SESS_IDLE,
|
||||||
|
|
||||||
|
/* 5-baud address reception */
|
||||||
|
KL_SESS_5BAUD_SAMPLING,
|
||||||
|
KL_SESS_5BAUD_DONE,
|
||||||
|
|
||||||
|
/* Handshake: sync + keywords */
|
||||||
|
KL_SESS_HS_SYNC,
|
||||||
|
KL_SESS_HS_SYNC_WAIT,
|
||||||
|
KL_SESS_HS_KW_LSB,
|
||||||
|
KL_SESS_HS_KW_LSB_WAIT,
|
||||||
|
KL_SESS_HS_KW_MSB,
|
||||||
|
KL_SESS_HS_KW_MSB_WAIT,
|
||||||
|
KL_SESS_HS_WAIT_ACK,
|
||||||
|
|
||||||
|
/* Identification stream */
|
||||||
|
KL_SESS_IDENT_SEND,
|
||||||
|
KL_SESS_IDENT_WAIT_TX,
|
||||||
|
KL_SESS_IDENT_WAIT_ACK,
|
||||||
|
KL_SESS_IDENT_FINAL_ACK,
|
||||||
|
KL_SESS_IDENT_FINAL_WAIT,
|
||||||
|
|
||||||
|
/* Active session */
|
||||||
|
KL_SESS_READY,
|
||||||
|
KL_SESS_CMD_RX,
|
||||||
|
KL_SESS_CMD_DISPATCH,
|
||||||
|
KL_SESS_CMD_RESPONSE,
|
||||||
|
KL_SESS_CMD_NAK_WAIT, /* Sending NAK after invalid packet */
|
||||||
|
|
||||||
|
/* Teardown */
|
||||||
|
KL_SESS_TERMINATE
|
||||||
|
} KL_SessionState;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Session context
|
||||||
|
* ================================================================ */
|
||||||
|
typedef struct {
|
||||||
|
KL_SessionState state;
|
||||||
|
|
||||||
|
/* 5-baud sampling */
|
||||||
|
uint8_t five_bit_index;
|
||||||
|
uint8_t five_byte;
|
||||||
|
uint8_t five_active;
|
||||||
|
|
||||||
|
/* Handshake */
|
||||||
|
uint8_t hs_retry_count;
|
||||||
|
uint32_t hs_next_tick;
|
||||||
|
|
||||||
|
/* Identification stream */
|
||||||
|
uint8_t ident_buf[KL_IDENT_BUF_SIZE];
|
||||||
|
uint8_t ident_len;
|
||||||
|
uint8_t ident_offset;
|
||||||
|
uint8_t ident_chunk_len; /* Current chunk size being sent */
|
||||||
|
|
||||||
|
/* Session keepalive */
|
||||||
|
uint32_t session_deadline;
|
||||||
|
|
||||||
|
/* Connection status: 0=idle, 1=connecting, 2=active */
|
||||||
|
volatile uint8_t connection_status;
|
||||||
|
|
||||||
|
/* Signal from app layer that End command was handled */
|
||||||
|
uint8_t end_requested;
|
||||||
|
} KL_Session;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* API
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Session_Init(void);
|
||||||
|
void KL_Session_Service(void);
|
||||||
|
uint8_t KL_Session_GetStatus(void);
|
||||||
|
void KL_Session_RequestEnd(void); /* Called by app on CMD_End */
|
||||||
|
void KL_Session_Kick(void); /* Extend session deadline */
|
||||||
|
|
||||||
|
/* ISR callbacks */
|
||||||
|
void KL_Session_OnExtiRxFalling(void);
|
||||||
|
void KL_Session_OnTim17Elapsed(void);
|
||||||
|
|
||||||
|
#endif /* KL_SESSION_H */
|
||||||
418
Core/Kline_Libs/kl_transport.c
Normal file
418
Core/Kline_Libs/kl_transport.c
Normal file
@@ -0,0 +1,418 @@
|
|||||||
|
/*
|
||||||
|
* kl_transport.c
|
||||||
|
* Transport layer: packet framing, byte-by-byte complement ack, counter.
|
||||||
|
*
|
||||||
|
* All operations are non-blocking state machines driven by
|
||||||
|
* KL_Transport_Service() called from the main loop.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kl_transport.h"
|
||||||
|
#include "kl_phy.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static KL_Transport tl;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Init / Reset
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Transport_Init(void)
|
||||||
|
{
|
||||||
|
memset(&tl, 0, sizeof(tl));
|
||||||
|
tl.tx_state = KL_TL_TX_IDLE;
|
||||||
|
tl.rx_state = KL_TL_RX_IDLE;
|
||||||
|
tl.raw_state = KL_TL_RAW_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KL_Transport_ResetCounter(void)
|
||||||
|
{
|
||||||
|
tl.pkt_counter = 0;
|
||||||
|
tl.counter_initialized = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Packet TX
|
||||||
|
* ================================================================ */
|
||||||
|
int KL_Transport_SendPacket(const uint8_t *payload, uint8_t payload_len)
|
||||||
|
{
|
||||||
|
if (tl.tx_state != KL_TL_TX_IDLE) return 0;
|
||||||
|
if (payload_len == 0 || payload_len > KL_MAX_PACKET_PAYLOAD) return 0;
|
||||||
|
|
||||||
|
/* Serialize: [LENGTH][COUNTER][payload...] */
|
||||||
|
uint8_t length_field = payload_len + 2; /* +2 for COUNTER and LENGTH convention */
|
||||||
|
uint8_t idx = 0;
|
||||||
|
tl.tx_buf[idx++] = length_field;
|
||||||
|
tl.tx_buf[idx++] = tl.pkt_counter++;
|
||||||
|
memcpy(&tl.tx_buf[idx], payload, payload_len);
|
||||||
|
idx += payload_len;
|
||||||
|
|
||||||
|
tl.tx_len = idx;
|
||||||
|
tl.tx_idx = 0;
|
||||||
|
tl.tx_deadline = HAL_GetTick() + KL_PKT_TMO_MS;
|
||||||
|
tl.tx_next_tick = HAL_GetTick();
|
||||||
|
tl.tx_state = KL_TL_TX_SEND_BYTE;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KL_Transport_SendAck(void)
|
||||||
|
{
|
||||||
|
uint8_t payload = (uint8_t)KL_CMD_ACK;
|
||||||
|
KL_Transport_SendPacket(&payload, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KL_Transport_SendNak(void)
|
||||||
|
{
|
||||||
|
uint8_t payload = (uint8_t)KL_CMD_NAK;
|
||||||
|
KL_Transport_SendPacket(&payload, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int KL_Transport_TxBusy(void)
|
||||||
|
{
|
||||||
|
return (tl.tx_state != KL_TL_TX_IDLE &&
|
||||||
|
tl.tx_state != KL_TL_TX_DONE &&
|
||||||
|
tl.tx_state != KL_TL_TX_ERROR) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KL_Transport_TxDone(void) { return (tl.tx_state == KL_TL_TX_DONE) ? 1 : 0; }
|
||||||
|
int KL_Transport_TxError(void) { return (tl.tx_state == KL_TL_TX_ERROR) ? 1 : 0; }
|
||||||
|
|
||||||
|
void KL_Transport_TxReset(void)
|
||||||
|
{
|
||||||
|
tl.tx_state = KL_TL_TX_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* TX state machine
|
||||||
|
* ================================================================ */
|
||||||
|
static void tl_tx_service(void)
|
||||||
|
{
|
||||||
|
uint32_t now = HAL_GetTick();
|
||||||
|
uint8_t byte_val;
|
||||||
|
|
||||||
|
switch (tl.tx_state) {
|
||||||
|
|
||||||
|
case KL_TL_TX_SEND_BYTE:
|
||||||
|
if (kl_tick_diff(now, tl.tx_next_tick) < 0) break;
|
||||||
|
if (tl.tx_idx >= tl.tx_len) {
|
||||||
|
/* All data bytes sent, send terminator */
|
||||||
|
tl.tx_state = KL_TL_TX_SEND_TERM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
byte_val = tl.tx_buf[tl.tx_idx];
|
||||||
|
if (KL_Phy_TxByte(byte_val)) {
|
||||||
|
tl.tx_last_byte = byte_val;
|
||||||
|
tl.tx_idx++;
|
||||||
|
tl.tx_state = KL_TL_TX_WAIT_PHY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_TX_WAIT_PHY:
|
||||||
|
if (kl_tick_diff(now, tl.tx_deadline) >= 0) {
|
||||||
|
tl.tx_state = KL_TL_TX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!KL_Phy_TxBusy()) {
|
||||||
|
/* Echo consumed by PHY, now wait for master's complement */
|
||||||
|
tl.tx_state = KL_TL_TX_WAIT_COMPLEMENT;
|
||||||
|
tl.tx_next_tick = now; /* Record when we started waiting */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_TX_WAIT_COMPLEMENT:
|
||||||
|
if (kl_tick_diff(now, tl.tx_deadline) >= 0) {
|
||||||
|
tl.tx_state = KL_TL_TX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint8_t rx_byte;
|
||||||
|
if (KL_Phy_RxPop(&rx_byte)) {
|
||||||
|
if (rx_byte == (uint8_t)~tl.tx_last_byte) {
|
||||||
|
/* Valid complement received, advance to next byte */
|
||||||
|
tl.tx_next_tick = HAL_GetTick() + KL_P1_GAP_MS;
|
||||||
|
tl.tx_state = KL_TL_TX_SEND_BYTE;
|
||||||
|
} else {
|
||||||
|
/* Unexpected byte -- protocol error */
|
||||||
|
tl.tx_state = KL_TL_TX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_TX_SEND_TERM:
|
||||||
|
if (KL_Phy_TxByte(KL_PACKET_END)) {
|
||||||
|
tl.tx_state = KL_TL_TX_WAIT_TERM_PHY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_TX_WAIT_TERM_PHY:
|
||||||
|
if (kl_tick_diff(now, tl.tx_deadline) >= 0) {
|
||||||
|
tl.tx_state = KL_TL_TX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!KL_Phy_TxBusy()) {
|
||||||
|
tl.tx_state = KL_TL_TX_DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_TX_IDLE:
|
||||||
|
case KL_TL_TX_DONE:
|
||||||
|
case KL_TL_TX_ERROR:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Packet RX
|
||||||
|
* ================================================================ */
|
||||||
|
int KL_Transport_StartReceive(uint32_t timeout_ms)
|
||||||
|
{
|
||||||
|
if (tl.rx_state != KL_TL_RX_IDLE) return 0;
|
||||||
|
|
||||||
|
memset(&tl.rx_packet, 0, sizeof(tl.rx_packet));
|
||||||
|
tl.rx_idx = 0;
|
||||||
|
tl.rx_pkt_len = 0;
|
||||||
|
tl.rx_expected_total = 0;
|
||||||
|
tl.rx_deadline = HAL_GetTick() + timeout_ms;
|
||||||
|
tl.rx_byte_deadline = HAL_GetTick() + timeout_ms;
|
||||||
|
tl.rx_state = KL_TL_RX_WAIT_LEN;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KL_Transport_RxBusy(void)
|
||||||
|
{
|
||||||
|
return (tl.rx_state != KL_TL_RX_IDLE &&
|
||||||
|
tl.rx_state != KL_TL_RX_DONE &&
|
||||||
|
tl.rx_state != KL_TL_RX_ERROR) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KL_Transport_RxDone(void) { return (tl.rx_state == KL_TL_RX_DONE) ? 1 : 0; }
|
||||||
|
int KL_Transport_RxError(void) { return (tl.rx_state == KL_TL_RX_ERROR) ? 1 : 0; }
|
||||||
|
|
||||||
|
const KL_Packet* KL_Transport_GetRxPacket(void) { return &tl.rx_packet; }
|
||||||
|
|
||||||
|
void KL_Transport_RxReset(void)
|
||||||
|
{
|
||||||
|
tl.rx_state = KL_TL_RX_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KL_Transport_RxHadData(void)
|
||||||
|
{
|
||||||
|
return (tl.rx_idx > 0) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* RX helper: send complement and advance to wait-for-PHY state
|
||||||
|
* ================================================================ */
|
||||||
|
static int rx_send_complement(uint8_t byte, KL_TlRxState next_after_phy)
|
||||||
|
{
|
||||||
|
uint8_t compl = (uint8_t)~byte;
|
||||||
|
if (KL_Phy_TxByte(compl)) {
|
||||||
|
tl.rx_last_byte = byte;
|
||||||
|
tl.rx_state = KL_TL_RX_WAIT_COMPL_PHY;
|
||||||
|
/* Store the state to transition to after PHY completes.
|
||||||
|
* We encode the "next state after complement" in rx_expected_total
|
||||||
|
* as a side channel -- but cleaner to use a dedicated field.
|
||||||
|
* Instead, we use a convention: after complement PHY done,
|
||||||
|
* we look at rx_idx to determine what to do next. */
|
||||||
|
(void)next_after_phy;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* RX state machine
|
||||||
|
* ================================================================ */
|
||||||
|
static void tl_rx_service(void)
|
||||||
|
{
|
||||||
|
uint32_t now = HAL_GetTick();
|
||||||
|
uint8_t byte_val;
|
||||||
|
|
||||||
|
switch (tl.rx_state) {
|
||||||
|
|
||||||
|
case KL_TL_RX_WAIT_LEN:
|
||||||
|
if (kl_tick_diff(now, tl.rx_deadline) >= 0) {
|
||||||
|
tl.rx_state = KL_TL_RX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (KL_Phy_RxPop(&byte_val)) {
|
||||||
|
tl.rx_buf[tl.rx_idx++] = byte_val;
|
||||||
|
tl.rx_pkt_len = byte_val;
|
||||||
|
tl.rx_packet.length = byte_val;
|
||||||
|
tl.rx_byte_deadline = now + KL_BYTE_TMO_MS;
|
||||||
|
/* Send complement for LENGTH */
|
||||||
|
tl.rx_state = KL_TL_RX_COMPL_LEN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_RX_COMPL_LEN:
|
||||||
|
if (kl_tick_diff(now, tl.rx_deadline) >= 0) {
|
||||||
|
tl.rx_state = KL_TL_RX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint8_t compl = (uint8_t)~tl.rx_buf[0];
|
||||||
|
if (KL_Phy_TxByte(compl)) {
|
||||||
|
tl.rx_state = KL_TL_RX_WAIT_COMPL_PHY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_RX_WAIT_CNT:
|
||||||
|
if (kl_tick_diff(now, tl.rx_deadline) >= 0) {
|
||||||
|
tl.rx_state = KL_TL_RX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (KL_Phy_RxPop(&byte_val)) {
|
||||||
|
tl.rx_buf[tl.rx_idx++] = byte_val;
|
||||||
|
tl.rx_byte_deadline = now + KL_BYTE_TMO_MS;
|
||||||
|
|
||||||
|
/* Validate/sync packet counter */
|
||||||
|
if (!tl.counter_initialized) {
|
||||||
|
tl.pkt_counter = byte_val;
|
||||||
|
tl.counter_initialized = 1;
|
||||||
|
}
|
||||||
|
tl.rx_packet.counter = byte_val;
|
||||||
|
tl.pkt_counter = byte_val + 1;
|
||||||
|
|
||||||
|
/* Send complement for COUNTER */
|
||||||
|
tl.rx_state = KL_TL_RX_COMPL_CNT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_RX_COMPL_CNT:
|
||||||
|
if (kl_tick_diff(now, tl.rx_deadline) >= 0) {
|
||||||
|
tl.rx_state = KL_TL_RX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint8_t compl = (uint8_t)~tl.rx_buf[1];
|
||||||
|
if (KL_Phy_TxByte(compl)) {
|
||||||
|
tl.rx_state = KL_TL_RX_WAIT_COMPL_PHY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_RX_WAIT_BYTE:
|
||||||
|
if (kl_tick_diff(now, tl.rx_deadline) >= 0) {
|
||||||
|
tl.rx_state = KL_TL_RX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (KL_Phy_RxPop(&byte_val)) {
|
||||||
|
tl.rx_buf[tl.rx_idx++] = byte_val;
|
||||||
|
tl.rx_byte_deadline = now + KL_BYTE_TMO_MS;
|
||||||
|
/* Always send complement for every non-terminator byte.
|
||||||
|
* WAIT_COMPL_PHY will decide whether to go to WAIT_TERM
|
||||||
|
* or back to WAIT_BYTE based on rx_idx vs rx_pkt_len. */
|
||||||
|
tl.rx_state = KL_TL_RX_COMPL_BYTE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_RX_COMPL_BYTE:
|
||||||
|
if (kl_tick_diff(now, tl.rx_deadline) >= 0) {
|
||||||
|
tl.rx_state = KL_TL_RX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint8_t compl = (uint8_t)~tl.rx_buf[tl.rx_idx - 1];
|
||||||
|
if (KL_Phy_TxByte(compl)) {
|
||||||
|
tl.rx_state = KL_TL_RX_WAIT_COMPL_PHY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_RX_WAIT_COMPL_PHY:
|
||||||
|
if (kl_tick_diff(now, tl.rx_deadline) >= 0) {
|
||||||
|
tl.rx_state = KL_TL_RX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!KL_Phy_TxBusy()) {
|
||||||
|
/* Complement echo done. Determine next state based on progress.
|
||||||
|
* rx_idx tells us how many bytes we've stored so far:
|
||||||
|
* 1 = just finished LEN complement -> wait for CNT
|
||||||
|
* 2 = just finished CNT complement -> wait for CMD/data
|
||||||
|
* >=3 = CMD or data complement done -> wait for next byte or terminator
|
||||||
|
*/
|
||||||
|
if (tl.rx_idx == 1) {
|
||||||
|
tl.rx_state = KL_TL_RX_WAIT_CNT;
|
||||||
|
} else if (tl.rx_idx == 2) {
|
||||||
|
tl.rx_state = KL_TL_RX_WAIT_BYTE;
|
||||||
|
} else {
|
||||||
|
/* Check if all non-terminator bytes are collected */
|
||||||
|
if (tl.rx_idx >= tl.rx_pkt_len) {
|
||||||
|
tl.rx_state = KL_TL_RX_WAIT_TERM;
|
||||||
|
} else {
|
||||||
|
tl.rx_state = KL_TL_RX_WAIT_BYTE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_RX_WAIT_TERM:
|
||||||
|
|
||||||
|
if (kl_tick_diff(now, tl.rx_deadline) >= 0) {
|
||||||
|
tl.rx_state = KL_TL_RX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (KL_Phy_RxPop(&byte_val)) {
|
||||||
|
if (byte_val != KL_PACKET_END) {
|
||||||
|
tl.rx_state = KL_TL_RX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Parse into KL_Packet structure */
|
||||||
|
tl.rx_packet.command = tl.rx_buf[2]; /* CMD at index 2 */
|
||||||
|
tl.rx_packet.data_len = tl.rx_pkt_len - 3; /* pkt_len - 3 = data count */
|
||||||
|
if (tl.rx_packet.data_len > KL_MAX_PACKET_PAYLOAD) {
|
||||||
|
tl.rx_packet.data_len = KL_MAX_PACKET_PAYLOAD;
|
||||||
|
}
|
||||||
|
memcpy(tl.rx_packet.data, &tl.rx_buf[3], tl.rx_packet.data_len);
|
||||||
|
tl.rx_state = KL_TL_RX_DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KL_TL_RX_IDLE:
|
||||||
|
case KL_TL_RX_DONE:
|
||||||
|
case KL_TL_RX_ERROR:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Raw byte TX (for handshake -- no framing, no complement)
|
||||||
|
* ================================================================ */
|
||||||
|
int KL_Transport_SendRawByte(uint8_t byte)
|
||||||
|
{
|
||||||
|
if (tl.raw_state != KL_TL_RAW_IDLE) return 0;
|
||||||
|
if (!KL_Phy_TxByte(byte)) return 0;
|
||||||
|
tl.raw_state = KL_TL_RAW_WAIT_PHY;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KL_Transport_RawTxDone(void)
|
||||||
|
{
|
||||||
|
return (tl.raw_state == KL_TL_RAW_DONE) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KL_Transport_RawTxReset(void)
|
||||||
|
{
|
||||||
|
tl.raw_state = KL_TL_RAW_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tl_raw_service(void)
|
||||||
|
{
|
||||||
|
if (tl.raw_state == KL_TL_RAW_WAIT_PHY) {
|
||||||
|
if (!KL_Phy_TxBusy()) {
|
||||||
|
tl.raw_state = KL_TL_RAW_DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Combined service
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Transport_Service(void)
|
||||||
|
{
|
||||||
|
tl_tx_service();
|
||||||
|
tl_rx_service();
|
||||||
|
tl_raw_service();
|
||||||
|
}
|
||||||
116
Core/Kline_Libs/kl_transport.h
Normal file
116
Core/Kline_Libs/kl_transport.h
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* kl_transport.h
|
||||||
|
* Transport layer: packet framing, byte-by-byte complement ack, counter.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KL_TRANSPORT_H
|
||||||
|
#define KL_TRANSPORT_H
|
||||||
|
|
||||||
|
#include "kl_protocol.h"
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* TX state machine
|
||||||
|
* ================================================================ */
|
||||||
|
typedef enum {
|
||||||
|
KL_TL_TX_IDLE,
|
||||||
|
KL_TL_TX_SEND_BYTE, /* Submitting data byte to PHY */
|
||||||
|
KL_TL_TX_WAIT_PHY, /* Waiting for PHY TX+echo done */
|
||||||
|
KL_TL_TX_WAIT_COMPLEMENT, /* Waiting for master's ~byte */
|
||||||
|
KL_TL_TX_SEND_TERM, /* Sending 0x03 terminator */
|
||||||
|
KL_TL_TX_WAIT_TERM_PHY, /* Waiting for terminator echo */
|
||||||
|
KL_TL_TX_DONE,
|
||||||
|
KL_TL_TX_ERROR
|
||||||
|
} KL_TlTxState;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* RX state machine
|
||||||
|
* ================================================================ */
|
||||||
|
typedef enum {
|
||||||
|
KL_TL_RX_IDLE,
|
||||||
|
KL_TL_RX_WAIT_LEN, /* Waiting for LENGTH byte */
|
||||||
|
KL_TL_RX_COMPL_LEN, /* Sending complement for LENGTH */
|
||||||
|
KL_TL_RX_WAIT_CNT, /* Waiting for COUNTER byte */
|
||||||
|
KL_TL_RX_COMPL_CNT, /* Sending complement for COUNTER */
|
||||||
|
KL_TL_RX_WAIT_BYTE, /* Waiting for CMD/DATA byte */
|
||||||
|
KL_TL_RX_COMPL_BYTE, /* Sending complement for CMD/DATA */
|
||||||
|
KL_TL_RX_WAIT_COMPL_PHY, /* Waiting for complement echo */
|
||||||
|
KL_TL_RX_WAIT_TERM, /* Waiting for 0x03 terminator */
|
||||||
|
KL_TL_RX_DONE,
|
||||||
|
KL_TL_RX_ERROR
|
||||||
|
} KL_TlRxState;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Raw-byte TX state (for handshake bytes, no framing)
|
||||||
|
* ================================================================ */
|
||||||
|
typedef enum {
|
||||||
|
KL_TL_RAW_IDLE,
|
||||||
|
KL_TL_RAW_WAIT_PHY,
|
||||||
|
KL_TL_RAW_DONE
|
||||||
|
} KL_TlRawState;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Transport context
|
||||||
|
* ================================================================ */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t pkt_counter;
|
||||||
|
uint8_t counter_initialized;
|
||||||
|
|
||||||
|
/* TX engine */
|
||||||
|
KL_TlTxState tx_state;
|
||||||
|
uint8_t tx_buf[KL_MAX_PACKET_RAW];
|
||||||
|
uint8_t tx_len; /* Bytes in tx_buf (excl. 0x03) */
|
||||||
|
uint8_t tx_idx;
|
||||||
|
uint8_t tx_last_byte;
|
||||||
|
uint32_t tx_deadline;
|
||||||
|
uint32_t tx_next_tick; /* Inter-byte gap timing */
|
||||||
|
|
||||||
|
/* RX engine */
|
||||||
|
KL_TlRxState rx_state;
|
||||||
|
uint8_t rx_buf[KL_MAX_PACKET_RAW];
|
||||||
|
uint8_t rx_idx;
|
||||||
|
uint8_t rx_expected_total; /* LENGTH field value + 1 (for 0x03) */
|
||||||
|
uint8_t rx_pkt_len; /* LENGTH field value */
|
||||||
|
uint8_t rx_last_byte; /* Last received byte (for complement send) */
|
||||||
|
uint32_t rx_deadline;
|
||||||
|
uint32_t rx_byte_deadline;
|
||||||
|
KL_Packet rx_packet;
|
||||||
|
|
||||||
|
/* Raw byte TX */
|
||||||
|
KL_TlRawState raw_state;
|
||||||
|
} KL_Transport;
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* API
|
||||||
|
* ================================================================ */
|
||||||
|
void KL_Transport_Init(void);
|
||||||
|
void KL_Transport_Service(void);
|
||||||
|
void KL_Transport_ResetCounter(void);
|
||||||
|
|
||||||
|
/* Packet TX (non-blocking) */
|
||||||
|
int KL_Transport_SendPacket(const uint8_t *payload, uint8_t payload_len);
|
||||||
|
int KL_Transport_TxBusy(void);
|
||||||
|
int KL_Transport_TxDone(void);
|
||||||
|
int KL_Transport_TxError(void);
|
||||||
|
void KL_Transport_TxReset(void);
|
||||||
|
|
||||||
|
/* Packet RX (non-blocking) */
|
||||||
|
int KL_Transport_StartReceive(uint32_t timeout_ms);
|
||||||
|
int KL_Transport_RxBusy(void);
|
||||||
|
int KL_Transport_RxDone(void);
|
||||||
|
int KL_Transport_RxError(void);
|
||||||
|
const KL_Packet* KL_Transport_GetRxPacket(void);
|
||||||
|
void KL_Transport_RxReset(void);
|
||||||
|
|
||||||
|
/* Diagnostics */
|
||||||
|
int KL_Transport_RxHadData(void); /* True if RX consumed any bytes before error */
|
||||||
|
|
||||||
|
/* Convenience */
|
||||||
|
void KL_Transport_SendAck(void);
|
||||||
|
void KL_Transport_SendNak(void);
|
||||||
|
|
||||||
|
/* Raw byte TX for handshake (no framing, no complement) */
|
||||||
|
int KL_Transport_SendRawByte(uint8_t byte);
|
||||||
|
int KL_Transport_RawTxDone(void);
|
||||||
|
void KL_Transport_RawTxReset(void);
|
||||||
|
|
||||||
|
#endif /* KL_TRANSPORT_H */
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,38 +0,0 @@
|
|||||||
/* kline.h — cleaned up, matches new connection layer */
|
|
||||||
|
|
||||||
#ifndef INC_KLINE_H_
|
|
||||||
#define INC_KLINE_H_
|
|
||||||
|
|
||||||
#include "IKW1281Connection.h"
|
|
||||||
|
|
||||||
#define ECU_INIT_ADDRESS 0xF1
|
|
||||||
|
|
||||||
#define KLINE_RX_PIN GPIO_PIN_10
|
|
||||||
#define KLINE_TX_PIN GPIO_PIN_9
|
|
||||||
#define KLINE_GPIO_PORT GPIOA
|
|
||||||
|
|
||||||
#define SLAVE_ADDR 0xF1
|
|
||||||
#define SYNC_BYTE 0x55
|
|
||||||
#define POST_INIT_BAUD 9600
|
|
||||||
#define KEYWORD_LSB 0x8C
|
|
||||||
#define KEYWORD_MSB 0x51
|
|
||||||
#define REQUIRE_KEY_ACK 0 // no complements expected during 0x55/keywords
|
|
||||||
|
|
||||||
extern volatile uint8_t kline_connection_status;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t dtc;
|
|
||||||
uint8_t status;
|
|
||||||
uint8_t extra;
|
|
||||||
} FaultCode;
|
|
||||||
|
|
||||||
size_t BuildCombinedFromInfo(const ControllerInfo *ci, uint8_t *out, size_t maxlen);
|
|
||||||
|
|
||||||
void KLine_Slave_Init(void);
|
|
||||||
void KLine_Rearm5Baud(void);
|
|
||||||
void KLine_Service(void);
|
|
||||||
void KLine_ServiceCommands(void);
|
|
||||||
void KLine_Slave_Poll(void);
|
|
||||||
void KLine_KeepAlivePoll(void);
|
|
||||||
|
|
||||||
#endif /* INC_KLINE_H_ */
|
|
||||||
222
Core/Kline_Libs/psg_prop.c
Normal file
222
Core/Kline_Libs/psg_prop.c
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
/*
|
||||||
|
* psg_prop.c
|
||||||
|
* PSG K-Line data dictionary implementation.
|
||||||
|
*
|
||||||
|
* Provides lookup functions for all ROM and EEPROM read responses.
|
||||||
|
* Static constant tables are derived from protocol captures.
|
||||||
|
* Dynamic values (DFI, calibration) use runtime storage.
|
||||||
|
*
|
||||||
|
* Created on: 2025
|
||||||
|
* Author: herli
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "psg_prop.h"
|
||||||
|
#include "ee_manager.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// ROM lookup tables
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
/* Binary ROM entries: fixed-length raw data blobs */
|
||||||
|
typedef struct {
|
||||||
|
uint16_t addr;
|
||||||
|
uint8_t len;
|
||||||
|
uint8_t data[10];
|
||||||
|
} RomEntry_t;
|
||||||
|
|
||||||
|
static const RomEntry_t s_rom_bin_table[] = {
|
||||||
|
/* addr len data bytes (from protocol capture) description */
|
||||||
|
{ 0x9FCC, 10, { 0xA2,0x93,0x61,0x9F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF } }, /* fixed block */
|
||||||
|
{ 0x9FD6, 10, { 0x4B,0x9F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x10,0x9C } }, /* fixed block */
|
||||||
|
{ 0x9FE0, 10, { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF } }, /* fixed block */
|
||||||
|
{ 0x9FEA, 10, { 0xFF,0xFF,0xFF,0xFF,0xDD,0x95,0xFF,0xFF,0xFF,0xFF } }, /* fixed block */
|
||||||
|
{ 0x9FF4, 10, { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF } }, /* fixed block */
|
||||||
|
{ 0x9FB6, 2, { 0x44,0x00 } }, /* ptr -> DFI EEPROM addr */
|
||||||
|
{ 0x9FBE, 2, { 0xC6,0x00 } }, /* unknown ptr */
|
||||||
|
{ 0x9FBC, 2, { 0xA4,0x00 } }, /* unknown ptr */
|
||||||
|
{ 0x9FBA, 2, { 0xED,0x00 } }, /* unknown ptr */
|
||||||
|
{ 0x9FA6, 2, { 0x06,0x04 } }, /* unknown */
|
||||||
|
{ 0x9FA8, 2, { 0x42,0x00 } }, /* dT-Hybrid ref */
|
||||||
|
{ 0x9F74, 2, { 0x70,0x00 } }, /* DeltaBlockierwinkel */
|
||||||
|
{ 0x9F72, 2, { 0x74,0x00 } }, /* DeltaPhi-Offset */
|
||||||
|
{ 0x9F70, 2, { 0x48,0x00 } }, /* DynFDKorrekturt */
|
||||||
|
{ 0x93AD, 3, { 0x30,0x30,0x30 } }, /* Änderungsindex PV "000" */
|
||||||
|
{ 0x9F9E, 3, { 0xFF,0xFF,0xFF } }, /* PSG Fertigungsdatum (unset) */
|
||||||
|
{ 0x9FA1, 3, { 0xFF,0xFF,0xFF } }, /* PSG Chargen-Nr. (unset) */
|
||||||
|
{ 0x9C10, 10, PSG_KUNDENNUMMER1_STR },/* Kundennummer 1 "1234567891" */
|
||||||
|
{ 0x9C1A, 2, PSG_KUNDENNUMMER2_STR }, /* Kundennummer 2 "23" */
|
||||||
|
{ PSG_CUST_CHANGE_ADDR, 3, PSG_FERTIGUNGSWERK_STR }, /* Fertigungswerk */
|
||||||
|
{ PSG_CUST_CHANGE_ADDR + 3, 6, PSG_MOD_INDEX_STR }, /* Mod Index */
|
||||||
|
};
|
||||||
|
#define ROM_BIN_COUNT (sizeof(s_rom_bin_table) / sizeof(s_rom_bin_table[0]))
|
||||||
|
|
||||||
|
/* String ROM entries: 10-byte ASCII identifiers */
|
||||||
|
typedef struct {
|
||||||
|
uint16_t addr;
|
||||||
|
const char *str; /* exactly 10 ASCII bytes, no NUL */
|
||||||
|
} RomStrEntry_t;
|
||||||
|
|
||||||
|
static const RomStrEntry_t s_rom_str_table[] = {
|
||||||
|
{ 0x93BA, PSG_IDENT_STR }, /* ident (at PSG_IDENT_ADDR - 10) */
|
||||||
|
{ 0x9F80, PSG_SOFTWARE_VER_STR }, /* software version */
|
||||||
|
{ 0x9F94, PSG_STEUERGERAET_STR }, /* Steuergeraet */
|
||||||
|
{ 0x9F76, PSG_FAHRSOFTWARE_STR }, /* Fahrsoftware */
|
||||||
|
{ 0x9C1C, PSG_DATENSATZ_STR }, /* Datensatz */
|
||||||
|
{ 0x93B0, PSG_REFERENZ_STR }, /* Referenz-Kennfeld */
|
||||||
|
|
||||||
|
};
|
||||||
|
#define ROM_STR_COUNT (sizeof(s_rom_str_table) / sizeof(s_rom_str_table[0]))
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// EEPROM static data blocks
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DTC storage region (0x0000-0x003F, 64 bytes).
|
||||||
|
* Stored as a flat array so any-length chunk reads reconstruct correctly.
|
||||||
|
* Byte values derived from protocol capture.
|
||||||
|
*/
|
||||||
|
static const uint8_t s_dtc_eeprom_block[64] = {
|
||||||
|
/* 0x0000-0x000C (13 bytes, DTC MSG 1) */
|
||||||
|
0x50, 0x20, 0xAB, 0x0A, 0x0E, 0x09, 0x01, 0xED, 0x5A, 0x20, 0xDF, 0x2D, 0x05,
|
||||||
|
/* 0x000D-0x0019 (13 bytes, DTC MSG 2) */
|
||||||
|
0x00, 0x02, 0x33, 0x5B, 0x20, 0xDF, 0x2D, 0x05, 0x00, 0x03, 0x34, 0x52, 0x20,
|
||||||
|
/* 0x001A-0x0026 (13 bytes, DTC MSG 3) */
|
||||||
|
0xDF, 0x2D, 0x00, 0x00, 0x04, 0x30, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
/* 0x0027-0x0033 (13 bytes, DTC MSG 4) */
|
||||||
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
|
||||||
|
/* 0x0034-0x003F (12 bytes, DTC MSG 5) */
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeltaPhi calibration array (0x009A-0x00BD, 36 bytes = 18 x int16 LE).
|
||||||
|
* 0x8000 entries are "not calibrated" placeholders.
|
||||||
|
*/
|
||||||
|
static const uint8_t s_deltaphi_block[36] = {
|
||||||
|
/* 0x009A-0x00A6 (13 bytes, AD1) */
|
||||||
|
0xDE, 0xFF, 0xE8, 0xFF, 0x0C, 0x00, 0x08, 0x00, 0xFE, 0xFF, 0x00, 0x80, 0x00,
|
||||||
|
/* 0x00A7-0x00B3 (13 bytes, AD2) */
|
||||||
|
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80,
|
||||||
|
/* 0x00B4-0x00BD (10 bytes, AD3) */
|
||||||
|
0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Runtime-writable EEPROM values.
|
||||||
|
* Initialized to sniffed defaults; updated by WriteEeprom handlers.
|
||||||
|
* ================================================================ */
|
||||||
|
static uint8_t s_dt_hybrid[2] = { 0xDE, 0x22 }; /* 0x0042, dT-Hybrid, default 1958.5 K */
|
||||||
|
static uint8_t s_dynfd_korr[2] = { 0x00, 0x00 }; /* 0x0048, DynFDKorrekturt */
|
||||||
|
static uint8_t s_delta_blockierwinkel[4] = { 0x00, 0x00, 0x00, 0x00 }; /* 0x0070 */
|
||||||
|
static uint8_t s_deltaphi_offset[4] = { 0x00, 0x00, 0x00, 0x00 }; /* 0x0074 */
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// PSG_ROM_GetData
|
||||||
|
// ================================================================
|
||||||
|
uint8_t PSG_ROM_GetData(uint16_t addr, uint8_t len, uint8_t *out)
|
||||||
|
{
|
||||||
|
/* customerChangeAddress (0x9FFE) — 2 bytes little-endian */
|
||||||
|
if (addr == 0x9FFE && len == 2) {
|
||||||
|
out[0] = (uint8_t)(PSG_CUST_CHANGE_ADDR & 0xFF);
|
||||||
|
out[1] = (uint8_t)((PSG_CUST_CHANGE_ADDR >> 8) & 0xFF);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Binary data blocks */
|
||||||
|
for (int i = 0; i < (int)ROM_BIN_COUNT; i++) {
|
||||||
|
if (s_rom_bin_table[i].addr == addr && s_rom_bin_table[i].len == len) {
|
||||||
|
memcpy(out, s_rom_bin_table[i].data, len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 10-byte ASCII string entries */
|
||||||
|
for (int i = 0; i < (int)ROM_STR_COUNT; i++) {
|
||||||
|
if (s_rom_str_table[i].addr == addr && len == 10) {
|
||||||
|
memcpy(out, s_rom_str_table[i].str, 10);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// PSG_EEPROM_GetData
|
||||||
|
// ================================================================
|
||||||
|
uint8_t PSG_EEPROM_GetData(uint16_t addr, uint8_t len, uint8_t *out)
|
||||||
|
{
|
||||||
|
/* DFI angle correction (0x0044, 2 bytes) — dynamic, caller must check lock */
|
||||||
|
if (addr == 0x0044 && len == 2) {
|
||||||
|
int8_t code = GetDfiValue();
|
||||||
|
out[0] = (uint8_t)code;
|
||||||
|
out[1] = (uint8_t)(-(int8_t)code); /* two's-complement checksum byte */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Serial number (0x0080, 6 bytes) */
|
||||||
|
if (addr == 0x0080 && len == 6) {
|
||||||
|
memcpy(out, PSG_SERIAL_STR, 6);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fertigungsdatum / manufacturing date (0x0086, 3 bytes) */
|
||||||
|
if (addr == 0x0086 && len == 3) {
|
||||||
|
memcpy(out, FERTIGUNGSDATUM_STR, 3);
|
||||||
|
//memcpy(out + 3, FERTIGUNGSDATUM_STR, 3); si length = 6
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unknown/reserved (0x0094, 2 bytes) */
|
||||||
|
if (addr == 0x0094 && len == 2) {
|
||||||
|
out[0] = 0x00; out[1] = 0x00;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dT-Hybrid (0x0042, 2 bytes) */
|
||||||
|
if (addr == 0x0042 && len == 2) {
|
||||||
|
memcpy(out, s_dt_hybrid, 2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DynFDKorrekturt (0x0048, 2 bytes) */
|
||||||
|
if (addr == 0x0048 && len == 2) {
|
||||||
|
memcpy(out, s_dynfd_korr, 2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DeltaBlockierwinkel (0x0070, 4 bytes) */
|
||||||
|
if (addr == 0x0070 && len == 4) {
|
||||||
|
memcpy(out, s_delta_blockierwinkel, 4);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DeltaPhi-Offset (0x0074, 4 bytes) */
|
||||||
|
if (addr == 0x0074 && len == 4) {
|
||||||
|
memcpy(out, s_deltaphi_offset, 4);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DTC EEPROM block (0x0000-0x003F, 64 bytes total) */
|
||||||
|
if (addr < 0x0040U) {
|
||||||
|
if ((uint16_t)(addr + len) <= 0x0040U) {
|
||||||
|
memcpy(out, &s_dtc_eeprom_block[addr], len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DeltaPhi calibration array (0x009A-0x00BD, 36 bytes) */
|
||||||
|
if (addr >= 0x009AU && addr < (0x009AU + 36U)) {
|
||||||
|
uint16_t off = addr - 0x009AU;
|
||||||
|
uint16_t avail = (uint16_t)(36U - off);
|
||||||
|
uint8_t n = (len <= (uint8_t)avail) ? len : (uint8_t)avail;
|
||||||
|
memcpy(out, &s_deltaphi_block[off], n);
|
||||||
|
if (n < len) memset(out + n, 0xFF, len - n);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* psg_prop.h
|
* psg_prop.h
|
||||||
|
* PSG device properties dictionary.
|
||||||
|
*
|
||||||
|
* All K-Line ROM and EEPROM read responses are defined here.
|
||||||
|
* Edit the values in this file to configure what the master sees.
|
||||||
*
|
*
|
||||||
* Created on: Aug 19, 2025
|
* Created on: Aug 19, 2025
|
||||||
* Author: herli
|
* Author: herli
|
||||||
@@ -8,18 +12,95 @@
|
|||||||
#ifndef INC_PSG_PROP_H_
|
#ifndef INC_PSG_PROP_H_
|
||||||
#define INC_PSG_PROP_H_
|
#define INC_PSG_PROP_H_
|
||||||
|
|
||||||
typedef struct {
|
#include <stdint.h>
|
||||||
char client_ident[13]; // 12 + null
|
#include <stddef.h>
|
||||||
char unk_ident1[11];
|
|
||||||
char soft_info[11];
|
|
||||||
char unk_ident2[11];
|
|
||||||
char unk_ident3[11];
|
|
||||||
} PSGControllerInfo;
|
|
||||||
|
|
||||||
#define i_Ident "0470504005"
|
// ================================================================
|
||||||
#define i_SerialN "225832"
|
// Device identity strings (used in ASCII init stream and ROM reads)
|
||||||
#define i_modIndex "000004"
|
// ================================================================
|
||||||
|
|
||||||
|
/* Part number / ECU identifier — 10 ASCII chars, ROM 0x93BA */
|
||||||
|
#define PSG_IDENT_STR "0470004004"
|
||||||
|
//#define PSG_IDENT_STR "0470504009"
|
||||||
|
|
||||||
|
/* Serial number — 6 ASCII chars, EEPROM 0x0080 */
|
||||||
|
#define PSG_SERIAL_STR "297523"
|
||||||
|
|
||||||
|
/* Modification index — 6 ASCII chars, ROM 0x93A7 */
|
||||||
|
#define PSG_MOD_INDEX_STR "000000"
|
||||||
|
|
||||||
|
/* Software version — 10 ASCII chars, ROM 0x9F80 */
|
||||||
|
//#define PSG_SOFTWARE_VER_STR "C063_2.V61"
|
||||||
|
#define PSG_SOFTWARE_VER_STR "HCPS_G2.04"
|
||||||
|
|
||||||
|
|
||||||
|
#define PSG_SOFTWARE_VER2_STR "C062_0.P64" //no la lee directamente, solo al principio
|
||||||
|
|
||||||
|
/* Steuergeraet (controller unit number) — 10 ASCII chars, ROM 0x9F94 */
|
||||||
|
#define PSG_STEUERGERAET_STR "0281010888"
|
||||||
|
|
||||||
|
/* Fahrsoftware (driving software code) — 10 ASCII chars, ROM 0x9F76 */
|
||||||
|
#define PSG_FAHRSOFTWARE_STR "1469946013"
|
||||||
|
|
||||||
|
/* Datensatz (dataset code) — 10 ASCII chars, ROM 0x9C1C */
|
||||||
|
#define PSG_DATENSATZ_STR "2469947023"
|
||||||
|
|
||||||
|
/* Referenz-Kennfeld (reference map code) — 10 ASCII chars, ROM 0x93B0 */
|
||||||
|
#define PSG_REFERENZ_STR "2469948008"
|
||||||
|
|
||||||
|
/* Fertigungsdatum (manufacturing date) — 3 ASCII chars, EEPROM 0x0086 */
|
||||||
|
//#define FERTIGUNGSDATUM_STR "489"
|
||||||
|
#define FERTIGUNGSDATUM_STR "420"
|
||||||
|
|
||||||
|
/* Fertigungswerk (manufacturing plant) — 3 ASCII chars, ROM 0x93A4 */
|
||||||
|
//#define PSG_FERTIGUNGSWERK_STR "015"
|
||||||
|
#define PSG_FERTIGUNGSWERK_STR "069"
|
||||||
|
|
||||||
|
#define PSG_ANDERUNGSINDEX_STR "000"
|
||||||
|
|
||||||
|
#define PSG_KUNDENNUMMER_STR "YC1Q9A543EF "
|
||||||
|
#define PSG_KUNDENNUMMER1_STR "YC1Q9A543E"
|
||||||
|
#define PSG_KUNDENNUMMER2_STR "F "
|
||||||
|
|
||||||
|
#define PSG_UNKNOWN_STR "\xFF\xFF\xFF\xFF\xFF\xFF"
|
||||||
|
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// Key address constants
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Address returned by the ReadIdentAdress (0x01) command.
|
||||||
|
* The ROM ident string lives at (PSG_IDENT_ADDR - 10) = 0x93BA.
|
||||||
|
*/
|
||||||
|
#define PSG_IDENT_ADDR 0x93C4U
|
||||||
|
|
||||||
|
#define PSG_FAHRSOFTWARE_ADDR 0x9C28U
|
||||||
|
|
||||||
|
#define PSG_START55_ADDR 0x60U
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Customer change address — returned at ROM 0x9FFE (little-endian).
|
||||||
|
* The mod-index string lives at (PSG_CUST_CHANGE_ADDR + 3) = 0x93A7.
|
||||||
|
*/
|
||||||
|
#define PSG_CUST_CHANGE_ADDR 0x93A4U
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// ROM lookup interface (implemented in psg_prop.c)
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PSG_ROM_GetData — fill 'out' with 'len' bytes for the given ROM address.
|
||||||
|
* Returns 1 if the address is known, 0 if unhandled.
|
||||||
|
* Access-control (lock checks) is the caller's responsibility.
|
||||||
|
*/
|
||||||
|
uint8_t PSG_ROM_GetData(uint16_t addr, uint8_t len, uint8_t *out);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PSG_EEPROM_GetData — fill 'out' with 'len' bytes for the given EEPROM address.
|
||||||
|
* Returns 1 if the address is known, 0 if unhandled.
|
||||||
|
* Access-control (lock checks) is the caller's responsibility.
|
||||||
|
*/
|
||||||
|
uint8_t PSG_EEPROM_GetData(uint16_t addr, uint8_t len, uint8_t *out);
|
||||||
|
|
||||||
#endif /* INC_PSG_PROP_H_ */
|
#endif /* INC_PSG_PROP_H_ */
|
||||||
|
|||||||
@@ -375,9 +375,11 @@ float BoostMultiplier(uint8_t mode, float RPM, float ME){
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define _004006
|
//#define _004006
|
||||||
//#define _004004
|
#define _004004
|
||||||
//#define _004002
|
//#define _004002
|
||||||
|
//#define _504003
|
||||||
|
//#define _504009
|
||||||
|
|
||||||
#if defined(_004006)
|
#if defined(_004006)
|
||||||
struct AlphaStruct fuelmap_m12 = { //probar este mapa
|
struct AlphaStruct fuelmap_m12 = { //probar este mapa
|
||||||
@@ -471,7 +473,7 @@ struct AlphaStruct fuelmap_m12 = { //probar este mapa
|
|||||||
{ -2.273, 1.383, 2.438, 2.848, 4.008, 5.145, 12.363, 15.117, 16.523, 17.941, 24.598, 24.598}, //RPM = 2199
|
{ -2.273, 1.383, 2.438, 2.848, 4.008, 5.145, 12.363, 15.117, 16.523, 17.941, 24.598, 24.598}, //RPM = 2199
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct AlphaStruct fuelmap_10 = { //probar este mapa
|
struct AlphaStruct fuelmap_m5 = { //probar este mapa
|
||||||
{
|
{
|
||||||
{ 5.109, 7.020, 7.137, 7.230, 7.500, 8.156, 10.711, 11.520, 11.742, 11.965, 12.961, 13.781}, //RPM = 99
|
{ 5.109, 7.020, 7.137, 7.230, 7.500, 8.156, 10.711, 11.520, 11.742, 11.965, 12.961, 13.781}, //RPM = 99
|
||||||
{ 5.367, 7.559, 7.688, 7.793, 8.121, 8.859, 11.379, 12.141, 12.316, 12.504, 13.465, 14.227}, //RPM = 199
|
{ 5.367, 7.559, 7.688, 7.793, 8.121, 8.859, 11.379, 12.141, 12.316, 12.504, 13.465, 14.227}, //RPM = 199
|
||||||
@@ -486,7 +488,7 @@ struct AlphaStruct fuelmap_10 = { //probar este mapa
|
|||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct AlphaStruct fuelmap_25 = { //probar este mapa
|
struct AlphaStruct fuelmap_10 = { //probar este mapa
|
||||||
{
|
{
|
||||||
{ 5.320, 7.289, 7.406, 7.500, 7.781, 8.355, 10.898, 11.566, 11.871, 12.188, 13.184, 14.004}, //RPM = 99
|
{ 5.320, 7.289, 7.406, 7.500, 7.781, 8.355, 10.898, 11.566, 11.871, 12.188, 13.184, 14.004}, //RPM = 99
|
||||||
{ 5.484, 7.711, 7.852, 7.957, 8.285, 9.094, 11.531, 12.176, 12.445, 12.738, 13.699, 14.461}, //RPM = 199
|
{ 5.484, 7.711, 7.852, 7.957, 8.285, 9.094, 11.531, 12.176, 12.445, 12.738, 13.699, 14.461}, //RPM = 199
|
||||||
@@ -500,7 +502,7 @@ struct AlphaStruct fuelmap_25 = { //probar este mapa
|
|||||||
{ -2.027, 1.711, 2.836, 3.199, 4.254, 5.391, 13.254, 15.938, 17.801, 19.676, 24.598, 24.598}, //RPM = 2199
|
{ -2.027, 1.711, 2.836, 3.199, 4.254, 5.391, 13.254, 15.938, 17.801, 19.676, 24.598, 24.598}, //RPM = 2199
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct AlphaStruct fuelmap_60 = { //probar este mapa
|
struct AlphaStruct fuelmap_25 = { //probar este mapa
|
||||||
{
|
{
|
||||||
{ 5.590, 7.652, 7.781, 7.863, 8.145, 8.613, 11.133, 11.625, 12.047, 12.457, 13.477, 14.297}, //RPM = 99
|
{ 5.590, 7.652, 7.781, 7.863, 8.145, 8.613, 11.133, 11.625, 12.047, 12.457, 13.477, 14.297}, //RPM = 99
|
||||||
{ 5.637, 7.922, 8.074, 8.168, 8.496, 9.375, 11.707, 12.223, 12.609, 13.020, 13.969, 14.730}, //RPM = 199
|
{ 5.637, 7.922, 8.074, 8.168, 8.496, 9.375, 11.707, 12.223, 12.609, 13.020, 13.969, 14.730}, //RPM = 199
|
||||||
@@ -514,7 +516,7 @@ struct AlphaStruct fuelmap_60 = { //probar este mapa
|
|||||||
{ -1.875, 1.945, 3.070, 3.445, 4.430, 5.578, 13.793, 16.465, 18.668, 20.824, 24.598, 24.598}, //RPM = 2199664, 2.180, 3.398, 3.715, 4.711, 5.777, 14.461, 17.086, 19.605, 22.125, 24.598, 24.598}, //RPM = 2199
|
{ -1.875, 1.945, 3.070, 3.445, 4.430, 5.578, 13.793, 16.465, 18.668, 20.824, 24.598, 24.598}, //RPM = 2199664, 2.180, 3.398, 3.715, 4.711, 5.777, 14.461, 17.086, 19.605, 22.125, 24.598, 24.598}, //RPM = 2199
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct AlphaStruct fuelmap_80 = { //probar este mapa
|
struct AlphaStruct fuelmap_60 = { //probar este mapa
|
||||||
{
|
{
|
||||||
{ 5.918, 8.109, 8.227, 8.320, 8.590, 8.930, 11.426, 11.695, 12.246, 12.820, 13.816, 14.625}, //RPM = 99
|
{ 5.918, 8.109, 8.227, 8.320, 8.590, 8.930, 11.426, 11.695, 12.246, 12.820, 13.816, 14.625}, //RPM = 99
|
||||||
{ 5.824, 8.180, 8.309, 8.426, 8.742, 9.727, 11.953, 12.281, 12.809, 13.336, 14.320, 15.070}, //RPM = 199
|
{ 5.824, 8.180, 8.309, 8.426, 8.742, 9.727, 11.953, 12.281, 12.809, 13.336, 14.320, 15.070}, //RPM = 199
|
||||||
@@ -626,6 +628,135 @@ struct fuelMapIndexes fuelMapI = { //probar este mapa
|
|||||||
{ 0.375, 0.500, 1.625, 2.500, 5.000, 10.000, 30.000, 35.000, 40.000, 45.000, 60.000, 72.000},
|
{ 0.375, 0.500, 1.625, 2.500, 5.000, 10.000, 30.000, 35.000, 40.000, 45.000, 60.000, 72.000},
|
||||||
{ -15.5, -5.5 ,6.3, 27.5, 47.8, 75}
|
{ -15.5, -5.5 ,6.3, 27.5, 47.8, 75}
|
||||||
};
|
};
|
||||||
|
#elif defined(_504003)
|
||||||
|
struct AlphaStruct fuelmap_m12 = { //probar este mapa
|
||||||
|
{
|
||||||
|
{ 0.563, 10.008, 10.113, 10.172, 10.676, 11.895, 12.691, 13.406, 14.273, 21.059}, //RPM = 99.75
|
||||||
|
{ 0.609, 10.324, 10.559, 10.688, 11.531, 12.668, 13.535, 13.910, 14.730, 20.730}, //RPM = 199.75
|
||||||
|
{ 0.527, 11.098, 11.438, 11.672, 13.078, 14.602, 15.680, 16.289, 17.742, 18.680}, //RPM = 399.75
|
||||||
|
{ 1.031, 14.977, 15.410, 15.727, 17.215, 20.238, 22.348, 23.801, 26.484, 28.441}, //RPM = 999.75
|
||||||
|
{ 0.984, 19.594, 20.016, 20.262, 22.066, 27.398, 30.633, 33.188, 33.188, 33.188}, //RPM = 1749.75
|
||||||
|
{ 1.066, 22.266, 22.863, 23.074, 25.090, 30.961, 34.816, 38.145, 38.109, 38.121}, //RPM = 2149.50
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AlphaStruct fuelmap_m5 = { //probar este mapa
|
||||||
|
{
|
||||||
|
{ 0.656, 10.102, 10.207, 10.266, 10.758, 12.023, 12.867, 13.570, 14.438, 21.223}, //RPM = 99.75
|
||||||
|
{ 0.691, 10.406, 10.641, 10.758, 11.613, 12.949, 13.781, 14.168, 14.965, 20.977}, //RPM = 199.75
|
||||||
|
{ 0.656, 11.227, 11.578, 11.801, 13.219, 14.801, 15.926, 16.629, 18.082, 19.031}, //RPM = 399.75
|
||||||
|
{ 1.078, 15.023, 15.457, 15.773, 17.262, 20.402, 22.711, 24.246, 26.930, 28.887}, //RPM = 999.75
|
||||||
|
{ 1.172, 19.781, 20.215, 20.461, 22.266, 27.703, 31.125, 33.926, 33.961, 33.949}, //RPM = 1749.75
|
||||||
|
{ 1.313, 22.500, 23.098, 23.309, 25.336, 31.313, 35.449, 39.105, 39.070, 39.094}, //RPM = 2149.75
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AlphaStruct fuelmap_10 = { //probar este mapa
|
||||||
|
{
|
||||||
|
{ 0.762, 10.207, 10.313, 10.371, 10.863, 12.164, 13.078, 13.770, 14.637, 21.410}, //RPM = 99.75
|
||||||
|
{ 0.773, 10.500, 10.723, 10.863, 11.695, 13.301, 14.086, 14.461, 15.270, 21.281}, //RPM = 199.75
|
||||||
|
{ 0.832, 11.402, 11.754, 11.977, 13.395, 15.047, 16.242, 17.039, 18.492, 19.441}, //RPM = 399.75
|
||||||
|
{ 1.137, 15.082, 15.516, 15.832, 17.320, 20.602, 23.133, 24.785, 27.492, 29.461}, //RPM = 999.75
|
||||||
|
{ 1.430, 20.039, 20.438, 20.707, 22.488, 28.078, 31.734, 34.898, 34.922, 34.793}, //RPM = 1749.75
|
||||||
|
{ 1.605, 22.793, 23.391, 23.602, 25.629, 31.781, 36.211, 40.277, 40.289, 40.289}, //RPM = 2149.75
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AlphaStruct fuelmap_25 = { //probar este mapa
|
||||||
|
{
|
||||||
|
{ 0.844, 10.289, 10.395, 10.453, 10.945, 12.293, 13.242, 13.922, 14.789, 21.574}, //RPM = 99.75
|
||||||
|
{ 0.855, 10.570, 10.816, 10.934, 11.777, 13.559, 14.309, 14.695, 15.504, 21.516}, //RPM = 199.75
|
||||||
|
{ 0.961, 11.531, 11.883, 12.105, 13.512, 15.234, 16.500, 17.367, 18.820, 19.770}, //RPM = 399.75
|
||||||
|
{ 1.172, 15.117, 15.563, 15.867, 17.355, 20.754, 23.496, 25.219, 27.902, 29.859}, //RPM = 999.75
|
||||||
|
{ 1.617, 20.215, 20.648, 20.883, 22.688, 28.348, 32.203, 35.555, 35.555, 35.566}, //RPM = 1749.75
|
||||||
|
{ 1.828, 23.004, 23.613, 23.824, 25.852, 32.133, 36.773, 41.203, 41.227, 41.215}, //RPM = 2149.75
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AlphaStruct fuelmap_60 = { //probar este mapa
|
||||||
|
{
|
||||||
|
{ 0.938, 10.383, 10.488, 10.547, 11.039, 12.410, 13.418, 14.086, 14.953, 21.738}, //RPM = 99.75
|
||||||
|
{ 0.949, 10.664, 10.898, 11.027, 11.871, 13.863, 14.578, 14.965, 15.773, 21.773}, //RPM = 199.75
|
||||||
|
{ 1.113, 11.684, 12.035, 12.258, 13.664, 15.457, 16.770, 17.730, 19.195, 20.145}, //RPM = 399.75
|
||||||
|
{ 1.230, 15.176, 15.609, 15.926, 17.414, 20.930, 23.895, 25.711, 28.395, 30.352}, //RPM = 999.75
|
||||||
|
{ 1.828, 20.426, 20.848, 21.094, 22.910, 28.699, 32.754, 36.363, 36.387, 36.398}, //RPM = 1749.75
|
||||||
|
{ 2.098, 23.273, 23.883, 24.094, 26.121, 32.543, 37.512, 42.293, 42.305, 42.316}, //RPM = 2149.75
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/*struct AlphaStruct fuelmap_80 = { //probar este mapa
|
||||||
|
{
|
||||||
|
{ 6.234, 8.531, 8.602, 8.613, 8.672, 9.234, 11.320, 11.508, 12.176, 12.844, 14.285, 15.445}, //RPM = 99
|
||||||
|
{ 5.625, 7.910, 8.098, 8.238, 8.684, 9.996, 11.801, 11.918, 12.516, 13.102, 14.332, 15.316}, //RPM = 199
|
||||||
|
{ 4.582, 6.949, 7.266, 7.664, 8.859, 9.891, 12.188, 12.844, 13.348, 13.840, 15.387, 16.594}, //RPM = 424
|
||||||
|
{ 4.160, 6.527, 6.973, 7.441, 8.801, 9.668, 12.234, 12.879, 13.523, 14.203, 15.973, 17.367}, //RPM = 499
|
||||||
|
{ 2.496, 5.145, 5.988, 6.434, 7.676, 8.520, 12.832, 13.840, 14.859, 15.891, 18.855, 21.188}, //RPM = 895
|
||||||
|
{ 2.109, 4.805, 5.742, 6.141, 7.313, 8.238, 12.984, 14.109, 15.223, 16.336, 19.582, 22.172}, //RPM = 999
|
||||||
|
{ 0.914, 4.031, 4.945, 5.320, 6.387, 7.594, 13.488, 15.117, 16.723, 18.328, 22.465, 24.598}, //RPM = 1399
|
||||||
|
{ 0.551, 3.727, 4.652, 5.074, 6.281, 7.488, 13.816, 15.598, 17.203, 18.820, 23.145, 24.598}, //RPM = 1499
|
||||||
|
{ -1.383, 2.156, 3.023, 3.621, 5.320, 6.773, 14.754, 16.922, 19.066, 21.223, 24.598, 24.598}, //RPM = 1999
|
||||||
|
{ -2.191, 1.477, 2.262, 2.906, 4.746, 6.363, 14.766, 16.898, 19.523, 22.160, 24.598, 24.598}, //RPM = 2199
|
||||||
|
}
|
||||||
|
};*/
|
||||||
|
|
||||||
|
struct fuelMapIndexes fuelMapI = { //probar este mapa
|
||||||
|
{ 100, 200, 400, 1000, 1750, 2150}, //N_RPM = 17
|
||||||
|
{ 0.031, 0.406, 1.656, 2.406, 8.313, 24.906, 37.313, 45.688, 60.000, 72.000},
|
||||||
|
{ -15.0, 5.1 ,30, 50, 74.5}
|
||||||
|
};
|
||||||
|
#elif defined(_504009)
|
||||||
|
struct AlphaStruct fuelmap_m12 = { //probar este mapa
|
||||||
|
{
|
||||||
|
{ 0.563, 9.387, 9.680, 9.785, 10.406, 11.742, 12.434, 12.844, 13.898, 20.602}, //RPM = 99.75
|
||||||
|
{ 0.598, 9.785, 9.820, 9.996, 11.063, 12.762, 13.020, 13.242, 14.180, 20.145}, //RPM = 199.75
|
||||||
|
{ 0.680, 10.910, 11.227, 11.426, 12.867, 14.355, 14.988, 15.516, 16.758, 17.613}, //RPM = 399.75
|
||||||
|
{ 0.949, 14.777, 15.164, 15.410, 16.605, 19.207, 21.059, 22.219, 24.609, 26.367}, //RPM = 999.50
|
||||||
|
{ 1.137, 19.277, 19.629, 19.875, 21.234, 25.582, 28.324, 30.281, 34.922, 37.922}, //RPM = 1749.75
|
||||||
|
{ 1.336, 22.617, 23.496, 23.801, 25.523, 29.719, 32.742, 34.676, 40.770, 42.176}, //RPM = 2149.75
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AlphaStruct fuelmap_m5 = { //probar este mapa
|
||||||
|
{
|
||||||
|
{ 0.633, 9.457, 9.750, 9.855, 10.477, 11.848, 12.645, 13.148, 14.203, 20.906}, //RPM = 99.75
|
||||||
|
{ 0.668, 9.855, 9.891, 10.066, 11.133, 12.855, 13.230, 13.547, 14.484, 20.438}, //RPM = 199.75
|
||||||
|
{ 0.738, 10.969, 11.285, 11.484, 12.926, 14.461, 15.199, 15.809, 17.051, 17.906}, //RPM = 399.75
|
||||||
|
{ 1.020, 14.848, 15.234, 15.480, 16.676, 19.406, 21.352, 22.641, 25.031, 26.801}, //RPM = 999.75
|
||||||
|
{ 1.266, 19.406, 19.758, 20.004, 21.363, 25.840, 28.734, 30.902, 35.543, 38.543}, //RPM = 1749.75
|
||||||
|
{ 1.488, 22.770, 23.672, 23.953, 25.652, 29.988, 33.117, 35.473, 41.555, 42.984}, //RPM = 2149.75
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AlphaStruct fuelmap_10 = { //probar este mapa
|
||||||
|
{
|
||||||
|
{ 0.715, 9.539, 9.832, 9.938, 10.559, 11.965, 12.902, 13.512, 14.566, 21.270}, //RPM = 99.75
|
||||||
|
{ 0.750, 9.938, 9.961, 10.148, 11.203, 12.984, 13.488, 13.898, 14.836, 20.801}, //RPM = 199.75
|
||||||
|
{ 0.820, 11.051, 11.367, 11.566, 13.008, 14.578, 15.457, 16.172, 17.414, 18.270}, //RPM = 399.75
|
||||||
|
{ 1.113, 14.941, 15.328, 15.574, 16.770, 19.641, 21.703, 23.156, 25.547, 27.316}, //RPM = 999.75
|
||||||
|
{ 1.430, 19.570, 19.922, 20.168, 21.527, 26.168, 29.238, 31.688, 36.316, 39.316}, //RPM = 1749.75
|
||||||
|
{ 1.676, 22.934, 23.859, 24.117, 25.840, 30.328, 33.609, 36.457, 42.563, 43.969}, //RPM = 2149.75
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AlphaStruct fuelmap_25 = { //probar este mapa
|
||||||
|
{
|
||||||
|
{ 0.773, 9.598, 9.891, 9.996, 10.617, 12.059, 13.102, 13.793, 14.836, 21.539}, //RPM = 99.75
|
||||||
|
{ 0.809, 9.996, 10.031, 10.207, 11.273, 13.078, 13.676, 14.180, 15.129, 21.082}, //RPM = 199.75
|
||||||
|
{ 0.879, 11.109, 11.426, 11.625, 13.066, 14.672, 15.656, 16.453, 17.695, 18.551}, //RPM = 399.75
|
||||||
|
{ 1.184, 15.012, 15.398, 15.645, 16.840, 19.828, 21.984, 23.555, 25.945, 27.715}, //RPM = 999.75
|
||||||
|
{ 1.547, 19.688, 20.039, 20.285, 21.645, 26.414, 29.613, 32.262, 36.914, 39.902}, //RPM = 1749.75
|
||||||
|
{ 1.805, 23.086, 23.988, 24.328, 25.980, 30.598, 33.973, 37.207, 43.301, 44.707}, //RPM = 2149.75
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AlphaStruct fuelmap_60 = { //probar este mapa
|
||||||
|
{
|
||||||
|
{ 0.855, 9.680, 9.973, 10.078, 10.699, 12.176, 13.348, 14.145, 15.188, 21.891}, //RPM = 99.75
|
||||||
|
{ 0.879, 10.066, 10.102, 10.277, 11.344, 13.195, 13.934, 14.531, 15.469, 21.434}, //RPM = 199.75
|
||||||
|
{ 0.949, 11.191, 11.508, 11.707, 13.148, 14.789, 15.902, 16.781, 18.047, 18.902}, //RPM = 399.50
|
||||||
|
{ 1.277, 15.105, 15.492, 15.738, 16.934, 20.063, 22.324, 24.047, 26.438, 28.207}, //RPM = 999.75
|
||||||
|
{ 1.699, 19.840, 20.191, 20.438, 21.797, 26.730, 30.094, 33.000, 37.664, 40.652}, //RPM = 1749.75
|
||||||
|
{ 1.992, 23.262, 24.164, 24.457, 26.145, 30.914, 34.418, 38.227, 44.238, 45.680}, //RPM = 2150.00
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct fuelMapIndexes fuelMapI = { //probar este mapa
|
||||||
|
{ 100, 200, 400, 1000, 1750, 2150}, //N_RPM = 17
|
||||||
|
{ 0.031, 0.406, 1.656, 2.406, 8.313, 24.906, 37.313, 45.688, 60.000, 72.000},
|
||||||
|
{ -15.0, 5.1 ,30, 50, 74.5}
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,12 @@
|
|||||||
#include "ee_manager.h"
|
#include "ee_manager.h"
|
||||||
#include "toothed_wheel.h"
|
#include "toothed_wheel.h"
|
||||||
#include "can_read_pump_data.h"
|
#include "can_read_pump_data.h"
|
||||||
#include "kline.h"
|
/*#include "kline.h"
|
||||||
#include "IKW1281Connection.h"
|
#include "IKW1281Connection.h"*/
|
||||||
|
#include "kl_api.h"
|
||||||
|
#include "kl_protocol.h"
|
||||||
|
#include "kl_session.h"
|
||||||
|
|
||||||
/* USER CODE END Includes */
|
/* USER CODE END Includes */
|
||||||
|
|
||||||
/* Private typedef -----------------------------------------------------------*/
|
/* Private typedef -----------------------------------------------------------*/
|
||||||
@@ -245,6 +249,27 @@ void CAN_AppInit(void)
|
|||||||
can_read_pump_data_register(); // wraps 0x502 and sends EMPF2 for immobiliser pattern
|
can_read_pump_data_register(); // wraps 0x502 and sends EMPF2 for immobiliser pattern
|
||||||
}
|
}
|
||||||
uint32_t canfails = 0;
|
uint32_t canfails = 0;
|
||||||
|
|
||||||
|
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
|
||||||
|
if (huart->Instance == USART1) KL_Phy_TxCpltCB();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
|
||||||
|
if (huart->Instance == USART1) KL_Phy_RxCpltCB();
|
||||||
|
}
|
||||||
|
void EXTI15_10_IRQHandler(void)
|
||||||
|
{
|
||||||
|
HAL_GPIO_EXTI_IRQHandler(KL_RX_PIN);
|
||||||
|
}
|
||||||
|
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
|
||||||
|
if (GPIO_Pin == KL_RX_PIN) {
|
||||||
|
KL_Session_OnExtiRxFalling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
|
||||||
|
if (htim->Instance == TIM17) KL_Session_OnTim17Elapsed();
|
||||||
|
}
|
||||||
/* USER CODE END 0 */
|
/* USER CODE END 0 */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -348,7 +373,7 @@ int main(void)
|
|||||||
SYNC_Pulse_EnableGPIO();
|
SYNC_Pulse_EnableGPIO();
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
KLine_Slave_Init();
|
KLine_Init();
|
||||||
|
|
||||||
/* USER CODE END 2 */
|
/* USER CODE END 2 */
|
||||||
|
|
||||||
@@ -360,7 +385,7 @@ int main(void)
|
|||||||
|
|
||||||
/* USER CODE BEGIN 3 */
|
/* USER CODE BEGIN 3 */
|
||||||
KLine_Service();
|
KLine_Service();
|
||||||
KLine_ServiceCommands(); // <-- NEW: react to ISR kick, process full packet
|
//KLine_ServiceCommands(); // <-- NEW: react to ISR kick, process full packet
|
||||||
|
|
||||||
TW_Service();
|
TW_Service();
|
||||||
CAN_Service(); // drain queued frames whenever HW has room
|
CAN_Service(); // drain queued frames whenever HW has room
|
||||||
|
|||||||
@@ -1039,7 +1039,14 @@ void HAL_UART_MspInit(UART_HandleTypeDef* huart)
|
|||||||
PA9 ------> USART1_TX
|
PA9 ------> USART1_TX
|
||||||
PA10 ------> USART1_RX
|
PA10 ------> USART1_RX
|
||||||
*/
|
*/
|
||||||
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
|
GPIO_InitStruct.Pin = GPIO_PIN_9;
|
||||||
|
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
||||||
|
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||||
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
|
||||||
|
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
|
||||||
|
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||||||
|
|
||||||
|
GPIO_InitStruct.Pin = GPIO_PIN_10;
|
||||||
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
||||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
||||||
|
|||||||
@@ -184,6 +184,10 @@ NVIC.TIM6_DAC_IRQn=true\:4\:0\:true\:false\:true\:true\:true\:true
|
|||||||
NVIC.USART1_IRQn=true\:5\:0\:true\:false\:true\:true\:true\:true
|
NVIC.USART1_IRQn=true\:5\:0\:true\:false\:true\:true\:true\:true
|
||||||
NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
||||||
PA0.Signal=S_TIM2_CH1
|
PA0.Signal=S_TIM2_CH1
|
||||||
|
PA10.GPIOParameters=GPIO_ModeDefaultPP,GPIO_Speed,GPIO_PuPd
|
||||||
|
PA10.GPIO_ModeDefaultPP=GPIO_MODE_AF_PP
|
||||||
|
PA10.GPIO_PuPd=GPIO_NOPULL
|
||||||
|
PA10.GPIO_Speed=GPIO_SPEED_FREQ_LOW
|
||||||
PA10.Mode=Asynchronous
|
PA10.Mode=Asynchronous
|
||||||
PA10.Signal=USART1_RX
|
PA10.Signal=USART1_RX
|
||||||
PA11.Mode=FDCAN_Activate
|
PA11.Mode=FDCAN_Activate
|
||||||
@@ -216,6 +220,10 @@ PA6.Signal=OPAMP2_VOUT
|
|||||||
PA7.Locked=true
|
PA7.Locked=true
|
||||||
PA7.Mode=Follower
|
PA7.Mode=Follower
|
||||||
PA7.Signal=OPAMP2_VINP
|
PA7.Signal=OPAMP2_VINP
|
||||||
|
PA9.GPIOParameters=GPIO_ModeDefaultPP,GPIO_Speed,GPIO_PuPd
|
||||||
|
PA9.GPIO_ModeDefaultPP=GPIO_MODE_AF_PP
|
||||||
|
PA9.GPIO_PuPd=GPIO_NOPULL
|
||||||
|
PA9.GPIO_Speed=GPIO_SPEED_FREQ_HIGH
|
||||||
PA9.Mode=Asynchronous
|
PA9.Mode=Asynchronous
|
||||||
PA9.Signal=USART1_TX
|
PA9.Signal=USART1_TX
|
||||||
PB0.GPIOParameters=GPIO_Label
|
PB0.GPIOParameters=GPIO_Label
|
||||||
|
|||||||
Reference in New Issue
Block a user