diff --git a/.cproject b/.cproject index e4b7d6f..00c1f43 100644 --- a/.cproject +++ b/.cproject @@ -35,6 +35,7 @@ @@ -54,6 +55,7 @@ + @@ -118,6 +120,7 @@ @@ -136,6 +139,7 @@ + diff --git a/Core/Inc/stm32g4xx_it.h b/Core/Inc/stm32g4xx_it.h index a1f0aea..b8a1a2f 100644 --- a/Core/Inc/stm32g4xx_it.h +++ b/Core/Inc/stm32g4xx_it.h @@ -60,9 +60,11 @@ void DMA1_Channel1_IRQHandler(void); void DMA1_Channel2_IRQHandler(void); void FDCAN1_IT0_IRQHandler(void); void TIM1_BRK_TIM15_IRQHandler(void); +void TIM1_TRG_COM_TIM17_IRQHandler(void); void TIM1_CC_IRQHandler(void); void TIM2_IRQHandler(void); void TIM3_IRQHandler(void); +void USART1_IRQHandler(void); void TIM6_DAC_IRQHandler(void); /* USER CODE BEGIN EFP */ diff --git a/Core/Kline_Libs/IKW1281Connection.c b/Core/Kline_Libs/IKW1281Connection.c new file mode 100644 index 0000000..c880848 --- /dev/null +++ b/Core/Kline_Libs/IKW1281Connection.c @@ -0,0 +1,274 @@ +/* IKW1281Connection.c — byte pump, RX FIFO, no HAL_Delay() in hot path */ + +#include +#include +#include +#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= 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 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 +#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_ReadIdentAdress = 0x01, + PACKET_CMD_ReadRomEeprom = 0x03, + PACKET_CMD_ActuatorTest = 0x04, + PACKET_CMD_FaultCodesDelete = 0x05, + PACKET_CMD_End = 0x06, + PACKET_CMD_FaultCodesRead = 0x07, + 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_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 32 // 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 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_ */ diff --git a/Core/Kline_Libs/kline.c b/Core/Kline_Libs/kline.c new file mode 100644 index 0000000..e8b04d9 --- /dev/null +++ b/Core/Kline_Libs/kline.c @@ -0,0 +1,1281 @@ +/* + * kline.c + * + * Created on: Aug 18, 2025 + * Author: herli + */ + +#include "kline.h" +#include "IKW1281Connection.h" +#include "ee_manager.h" + + +#include "stdint.h" +#include "main.h" +#include +#include // for roundf + +static int ReceivePacket_Tmo(ParsedPacket *out, uint32_t overall_ms); +static void KLine_HandleWriteEeprom(const uint8_t *body, uint8_t body_len); +static void KLine_HandleReadRomEeprom(const uint8_t *body, uint8_t body_len); +static void KLine_HandleReadIdentAddress(const uint8_t *body, uint8_t body_len); +static void KLine_HandleReadEeprom(const uint8_t *body, uint8_t body_len); +static void KLine_HandlePassword(const uint8_t *body, uint8_t body_len); +static void KLine_HandleEnd(void); +static void KLine_HandleCustomPacket(const uint8_t *body, uint8_t body_len); +static void KLine_TerminateAndRearm(void); + +extern float dFi; + +int BitBang = 0; +uint8_t K_RxData[1]; + +volatile uint8_t kline_connection_status = 0; // 0 = idle, 1 = busy, 2 = hold + +// --- config you can change --- + +extern UART_HandleTypeDef huart1; +extern TIM_HandleTypeDef htim17; + +typedef enum { + FIVE_IDLE = 0, + FIVE_WAIT_FIRST_SAMPLE, + FIVE_SAMPLING +} five_state_t; + +volatile five_state_t five_state = FIVE_IDLE; +volatile uint8_t five_bit_index = 0; // 0..7 +volatile uint8_t five_byte = 0; // assembled address byte +volatile uint32_t next_interval_ms = 0; // for dynamic ARR updates +volatile uint8_t five_active = 0; + +#define K5_BIT_MS 200U +#define K5_FIRST_SAMPLE_MS 300U // 1.5 * 200ms + +ControllerInfo info = { + "0002246 826E", + "1469947023", + "C062_2.V60", + "C062_0.P64", + "0281001827", + "971057" +}; + +static void KLine5_ResetToIdle(void); +//void KLine_Rearm5Baud(void); +static void KLine5_StartSampling(void); +static void KLine5_ScheduleTimer(uint32_t delay_ms); +static void KLine_EnterUartMode_9600(void); +static void KLine_SendSyncAndKeywords(void); + +static void TIM17_ConfigTick100us(void) +{ + // Stop & clean + __HAL_TIM_DISABLE(&htim17); + __HAL_TIM_DISABLE_IT(&htim17, TIM_IT_UPDATE); + __HAL_TIM_CLEAR_FLAG(&htim17, TIM_FLAG_UPDATE); + + // Compute timer clock (APB2 x2 if prescaler != 1) + 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; + + // 10 kHz tick => 0.1 ms per tick + const uint32_t target_hz = 10000U; + uint32_t psc = (timclk + target_hz - 1U) / target_hz - 1U; // round up + if (psc > 0xFFFFU) psc = 0xFFFFU; + + __HAL_TIM_SET_PRESCALER(&htim17, psc); + __HAL_TIM_SET_AUTORELOAD(&htim17, 9U); // dummy non-zero ARR + __HAL_TIM_SET_COUNTER(&htim17, 0U); + + // One-pulse; URS so only overflow triggers update IRQ (UG won't) + htim17.Instance->CR1 |= (TIM_CR1_OPM | TIM_CR1_URS); + + // Load PSC/ARR now without causing an IRQ + htim17.Instance->EGR = TIM_EGR_UG; + __HAL_TIM_CLEAR_FLAG(&htim17, TIM_FLAG_UPDATE); + + // NVIC + HAL_NVIC_SetPriority(TIM1_TRG_COM_TIM17_IRQn, 6, 0); + HAL_NVIC_EnableIRQ(TIM1_TRG_COM_TIM17_IRQn); +} + +// Schedule an interrupt after delay_ms using TIM17 periodic update +static void KLine5_ScheduleTimer(uint32_t delay_ms) +{ + // 0.1 ms per tick + uint32_t ticks = delay_ms * 10U; + if (ticks < 1U) ticks = 1U; + if (ticks > 0x10000U) ticks = 0x10000U; // clamp (ARR is 16-bit) + + // Stop, clear, program ARR/CNT + __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)); // ARR = ticks-1 + __HAL_TIM_SET_COUNTER(&htim17, 0U); + + // Apply registers, then clear UIF so we don't fire instantly + htim17.Instance->EGR = TIM_EGR_UG; + __HAL_TIM_CLEAR_FLAG(&htim17, TIM_FLAG_UPDATE); + + // Ensure one-pulse remains set + htim17.Instance->CR1 |= (TIM_CR1_OPM | TIM_CR1_URS); + + // Arm and start + __HAL_TIM_ENABLE_IT(&htim17, TIM_IT_UPDATE); + __HAL_TIM_ENABLE(&htim17); // CEN +} + +// ===== Initialization entry (call once in main after MX_* inits) ===== +void KLine_Slave_Init(void) +{ + // We want to START in GPIO/EXTI mode on PA10 (falling edge). + // Ensure UART is not owning PA10: + HAL_UART_DeInit(&huart1); + + // PA10 should already be configured by CubeMX as GPIO Input + EXTI (falling) with pull-up. + // If not, configure here manually. + + TIM17_ConfigTick100us(); + KLine_Rearm5Baud(); // <— call it here + + //KLine5_ResetToIdle(); + KLine_SetDFI_Value(dFi); + +} + +// ===== Return to IDLE (re-arm EXTI) ===== +static void KLine5_ResetToIdle(void) +{ + five_state = FIVE_IDLE; + five_bit_index = 0; + five_byte = 0; + five_active = 0; + + kline_connection_status = 0; // <-- Disconnected + + ResetPacketCounter(); + + // Stop timer and disable its IRQ + HAL_TIM_Base_Stop_IT(&htim17); + __HAL_TIM_DISABLE_IT(&htim17, TIM_IT_UPDATE); + + // Clear EXTI and re-arm falling edge on PA10 + __HAL_GPIO_EXTI_CLEAR_IT(KLINE_RX_PIN); + + HAL_NVIC_ClearPendingIRQ(EXTI15_10_IRQn); + HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); +} + +// ===== Start 5-baud sampling sequence ===== +static void KLine5_StartSampling(void) +{ + five_active = 1; + five_state = FIVE_WAIT_FIRST_SAMPLE; + five_bit_index = 0; + five_byte = 0; + + // First sample at 1.5 bit times (300 ms) + KLine5_ScheduleTimer(K5_FIRST_SAMPLE_MS); +} + +// ===== GPIO EXTI ISR hook ===== +void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) +{ + if (GPIO_Pin == KLINE_RX_PIN) // PA10 (K-Line RX) + { + // Falling edge = start bit candidate + if (five_state == FIVE_IDLE) + { + // Debounce / sanity: ensure line is still low ~a bit later if you want (optional) + // Disable EXTI so we don't retrigger during the 5-baud window + HAL_NVIC_DisableIRQ(EXTI15_10_IRQn); + KLine5_StartSampling(); + } + } +} +void EXTI15_10_IRQHandler(void) +{ + HAL_GPIO_EXTI_IRQHandler(KLINE_RX_PIN); +} +volatile uint8_t k5_done = 0; +volatile uint32_t k5_ready_at = 0; // tick when we can switch to UART + +volatile uint8_t kline_cmd_pending = 0; // 0 = none, 1 = a command start is pending +volatile uint8_t kline_first_len = 0; // first byte (length) captured by ISR + +#define KEEPALIVE_TIMEOUT_MS 1000U +static volatile uint32_t kl_session_deadline = 0; + +// === W-time handshake state === +static uint8_t hs_state = 0; +static uint32_t hs_next = 0; + +static void KLine_SessionKick(void); // forward + +static void KLine_SessionKick(void) +{ + kl_session_deadline = HAL_GetTick() + KEEPALIVE_TIMEOUT_MS; +} + +void KLine_Service(void) +{ + // Always progress the TX byte pump + KLine_BytePump_Service(); + + // After 5-baud address done: switch to UART and schedule handshake + if (k5_done && (int32_t)(HAL_GetTick() - k5_ready_at) >= 0) { + k5_done = 0; + KLine_EnterUartMode_9600(); + KLine_BytePump_Init(); + + // Arm RX + HAL_UART_Receive_IT(&huart1, K_RxData, 1); + + // Schedule W1 -> 0x55 -> W2 -> KW LSB -> W3 -> KW MSB + hs_state = 1; + hs_next = HAL_GetTick() + KWP_W1_MS; + kline_connection_status = 1; + return; + } + + // Handshake sequencer + if (hs_state != 0) { + if ((int32_t)(HAL_GetTick() - hs_next) >= 0) { + if (hs_state == 1) { + uint8_t b = SYNC_BYTE; // 0x55 + KLine_TxStart(&b, 1, 0, 0); // no complement + hs_state = 2; hs_next = HAL_GetTick() + KWP_W2_MS; + } else if (hs_state == 2) { + uint8_t b = KEYWORD_LSB; + KLine_TxStart(&b, 1, 0, 0); + hs_state = 3; hs_next = HAL_GetTick() + KWP_W3_MS; + } else if (hs_state == 3) { + uint8_t b = KEYWORD_MSB; + KLine_TxStart(&b, 1, 0, 0); + hs_state = 4; // done + } + } + if (hs_state != 4) return; + + // After keywords, send your initial ASCII ident stream + if (!KLine_TxBusy()) { + uint8_t combined[64] = {0}; + size_t n = BuildCombinedFromInfo(&info, combined, sizeof(combined)); + (void)Slave_SendAsciiStream_WithAcks(combined, n); + KLine_SessionKick(); + kline_connection_status = 2; + hs_state = 0; + } + return; + } + + if(kline_connection_status == 2){ + ParsedPacket packet = {0}; + if (ReceivePacket_Tmo(&packet, 800)) { //waiting for an ack packet of stay alive + // any packet seen → extend session + KLine_SessionKick(); + // keep-alive: ACK → ACK + switch (packet.title) { + case PACKET_CMD_ReadIdent: + uint8_t combined[64] = {0}; + size_t n = BuildCombinedFromInfo(&info, combined, sizeof(combined)); + HAL_Delay(20); + Slave_SendAsciiStream_WithAcks(combined, n); + KLine_SessionKick(); + break; + case PACKET_CMD_ReadIdentAdress: // readIdentAddressCMD + KLine_HandleReadIdentAddress(&packet.raw[3], (uint8_t)(packet.length - 4)); + packet.type = PACKET_TYPE_UNKNOWN; + break; + case PACKET_CMD_FaultCodesRead: + KLine_DTCResponse(); // <-- respond + break; + case PACKET_CMD_ACK: + SendAckPacket(); + KLine_SessionKick(); + break; + case PACKET_CMD_LoginEeprom: // password/login for EEPROM + KLine_HandlePassword(&packet.raw[3], (uint8_t)(packet.length - 4)); + packet.type = PACKET_TYPE_UNKNOWN; + break; + case PACKET_CMD_WriteEeprom: // 0x1A + KLine_HandleWriteEeprom(&packet.raw[3], (uint8_t)(packet.length - 4)); + packet.type = PACKET_TYPE_UNKNOWN; // or PACKET_TYPE_WRITE_EEPROM_RESPONSE if you add it + break; + case PACKET_CMD_ReadEeprom: // 0x19 + KLine_HandleReadEeprom(&packet.raw[3], (uint8_t)(packet.length - 4)); + packet.type = PACKET_TYPE_READ_EEPROM_RESPONSE; // we just sent EF + break; + case PACKET_CMD_ReadRomEeprom: // 0x03 + KLine_HandleReadRomEeprom(&packet.raw[3], (uint8_t)(packet.length - 4)); + packet.type = PACKET_TYPE_READ_ROM_EEPROM_RESPONSE; + break; + case PACKET_CMD_End: + KLine_HandleEnd(); + packet.type = PACKET_TYPE_UNKNOWN; // we’re done; session is torn down + break; // early return is fine + + default: + KLine_HandleCustomPacket(&packet.raw[2], (uint8_t)(packet.length - 4)); + break; + } + } + if (tick_diff(HAL_GetTick(), kl_session_deadline) >= 0) { + //KLine_HandleEnd(); + KLine_TerminateAndRearm(); + return; + } + } + + +} + +// ===== TIM17 ISR hook: sampling scheduler ===== +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) +{ + if (htim->Instance != TIM17) return; + + if (!five_active) { + HAL_TIM_Base_Stop_IT(&htim17); + return; + } + + if (five_state == FIVE_WAIT_FIRST_SAMPLE || five_state == FIVE_SAMPLING) + { + // Sample the data bit in the middle of its window + GPIO_PinState pin = HAL_GPIO_ReadPin(KLINE_GPIO_PORT, KLINE_RX_PIN); + uint8_t bit = (pin == GPIO_PIN_SET) ? 1U : 0U; + + // LSB first + five_byte |= (bit << five_bit_index); + five_bit_index++; + + if (five_bit_index >= 8) + { + // Done. We can ignore stop bit timing and switch to UART mode. + five_active = 0; + HAL_TIM_Base_Stop_IT(&htim17); + + // Optional: check address value if you only respond to specific ones + if (five_byte != 0xF1) { KLine5_ResetToIdle(); return; } + + kline_connection_status = 1; // <-- Connected + + + //HAL_Delay(25); + kline_connection_status = 1; // connected + k5_ready_at = HAL_GetTick() + 200; // W1 ~20..300ms; choose ~200ms margin + k5_done = 1; // signal main loop + // From now on you're in USART mode; if you ever want to + // go back to "listen for 5-baud", DeInit UART and call KLine5_ResetToIdle(). + } + else + { + // Schedule next bit sample in 200 ms + five_state = FIVE_SAMPLING; + KLine5_ScheduleTimer(K5_BIT_MS); + } + } +} + +static void KLine_SetPinsToUartAF(void) +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + __HAL_RCC_GPIOA_CLK_ENABLE(); + + // PA9 = TX, PA10 = RX for USART1, AF7 + GPIO_InitStruct.Pin = KLINE_TX_PIN | KLINE_RX_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_PULLUP; // K-line transceiver RX usually idle-high + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF7_USART1; + HAL_GPIO_Init(KLINE_GPIO_PORT, &GPIO_InitStruct); +} + +// ===== Switch PA9/PA10 into USART1 9600-8N1 mode ===== +static void KLine_EnterUartMode_9600(void) +{ + // Make sure peripheral is clean + HAL_UART_DeInit(&huart1); + + // Restore pins to AF7 + KLine_SetPinsToUartAF(); + + // Re-init only with HAL (no MX_ call needed) + 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(); + } + + static uint8_t rx; + HAL_UART_Receive_IT(&huart1, &rx, 1); +} + +// ===== Send sync + keywords ===== +static void KLine_SendSyncAndKeywords(void) +{ + uint8_t buf[3] = {0x55, KEYWORD_LSB, KEYWORD_MSB}; + //uint8_t buf[2] = {KEYWORD_LSB, KEYWORD_MSB}; + //WriteByteRaw(SYNC_BYTE); + //WriteByteRaw(KEYWORD_LSB); + //WriteByteAndReadAck(KEYWORD_MSB); + HAL_UART_Transmit(&huart1, buf, sizeof(buf), 200); +} +void KLine_Rearm5Baud(void) +{ + // 1. DeInit UART1 so PA9/PA10 return to GPIO state + HAL_UART_DeInit(&huart1); + + // Make sure SYSCFG is clocked (EXTI routing) + __HAL_RCC_SYSCFG_CLK_ENABLE(); + + GPIO_InitTypeDef giTX = {0}; + giTX.Pin = KLINE_TX_PIN; // PA9 + //giTX.Mode = GPIO_MODE_OUTPUT_PP; // deterministic high + //giTX.Pull = GPIO_NOPULL; + giTX.Mode = GPIO_MODE_INPUT; // deterministic high + giTX.Pull = GPIO_PULLUP; + giTX.Speed = GPIO_SPEED_FREQ_LOW; + HAL_GPIO_Init(KLINE_GPIO_PORT, &giTX); + HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_TX_PIN, GPIO_PIN_SET); // idle high + + // 2. Reconfigure PA10 back as GPIO input with EXTI (falling edge) + GPIO_InitTypeDef GPIO_InitStruct = {0}; + GPIO_InitStruct.Pin = KLINE_RX_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; + GPIO_InitStruct.Pull = GPIO_PULLUP; + HAL_GPIO_Init(KLINE_GPIO_PORT, &GPIO_InitStruct); + + // 3. Clear pending EXTI interrupt for PA10 + //__HAL_GPIO_EXTI_CLEAR_FLAG(KLINE_RX_PIN); //TODO PROBAR SI FUNCIONA PORQUE ANTES ESTABA CON FLAG Y IBA + __HAL_GPIO_EXTI_CLEAR_IT(KLINE_RX_PIN); // not _CLEAR_FLAG + + //HAL_NVIC_ClearPendingIRQ(EXTI15_10_IRQn); + HAL_NVIC_SetPriority(EXTI15_10_IRQn, 5, 0); // pick a prio that fits your app + + HAL_NVIC_ClearPendingIRQ(EXTI15_10_IRQn); + + + HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); + + // 4. Reset state machine variables (your existing helper) + KLine5_ResetToIdle(); +} + + +// ===== Optional: USART1 IRQ callback ===== +void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) +{ + if (huart->Instance != USART1) return; + + uint8_t byte = K_RxData[0]; + KLine_OnByteReceived(byte); // <-- NEW: central RX handler + + HAL_UART_Receive_IT(&huart1, K_RxData, 1); // re-arm RX +} + +void KLine_ServiceCommands(void) +{ + if (!kline_cmd_pending) return; // nothing queued + + // Clear the event first to avoid reentrancy/races + kline_cmd_pending = 0; + + // Process exactly one full packet now (uses your timeout reads for remaining bytes) + ParsedPacket pkt; + if (KLine_ReceiveCMD(&pkt)) { + // Optionally: extend session on any well-formed packet + KLine_SessionKick(); + + // If you prefer handling keep-alive here: + if (pkt.title == PACKET_CMD_ACK) { + SendAckPacket(); // ACK → ACK + KLine_SessionKick(); + } + // For FaultCodesRead you already call KLine_DTCResponse() inside KLine_ReceiveCMD() + // so nothing more to do here. + } +} + +int KLine_ReceiveCMD(ParsedPacket *out){ + ParsedPacket packet = (ParsedPacket){0}; + + uint8_t index = 0, v; + + uint8_t packetLength = K_RxData[0]; + packet.raw[index++] = packetLength; + + if (!ReadPacketCounter_Tmo(100, &v)) return 0; // or call ReadPacketCounter() if you need lock-in + uint8_t packetCounter = v; + packet.raw[index++] = packetCounter; + + if (!ReadAndAckByte_Tmo(20, &v)) return 0; + uint8_t packetCommand = v; + packet.raw[index++] = packetCommand; + + for (int i = 0; i < packetLength - 3; i++) { + if (!ReadAndAckByte_Tmo(20, &v)) return 0; + packet.raw[index++] = v; + } + + if (!ReadByte_Tmo(20, &v)) return 0; + if (v != PACKET_END_EXPECTED) return 0; + packet.raw[index++] = v; + packet.length = index; + + packet.title = packetCommand; + switch (packetCommand) { + case PACKET_CMD_FaultCodesRead: + KLine_DTCResponse(); // <-- respond + packet.type = PACKET_TYPE_UNKNOWN; // (we don’t really use the parsed packet after responding) + break; + 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; + } + + *out = packet; + return 1; +} + +#define DTC_CHUNK_MAX 0x10U // payload bytes including 0xFC +#define DTC_CMD_SIZE 3U // 0xFC +#define DTC_BODY_MAX_PER_PKT (DTC_CHUNK_MAX - DTC_CMD_SIZE) // 0x0F = 15 +#define DTC_RECORD_SIZE 8U +#define DTC_TOTAL_RECORDS 8U // 8*8 = 64 body bytes (your master's buffer) + +// ---- storage set by app ---- +static FaultCode s_dtc_list[DTC_TOTAL_RECORDS]; +static size_t s_dtc_count = 0; + +FaultCode dtcs[] = { + { 0x5B, 0x20, 0x55 }, + { 0x50, 0x20, 0x03 }, + { 0x56, 0x20, 0x56 }, + { 0x5E, 0x30, 0x60 }, +}; + +void KLine_SetDTCList(const FaultCode *list, size_t count) +{ + if (!list) { s_dtc_count = 0; return; } + if (count > DTC_TOTAL_RECORDS) count = DTC_TOTAL_RECORDS; + for (size_t i = 0; i < count; ++i) s_dtc_list[i] = list[i]; + s_dtc_count = count; +} + +// one 8-byte record: first 3 meaningful, rest 0xFF +static void KLine_FillRecord(uint8_t *dst, const FaultCode *fc) +{ + dst[0] = fc->dtc; + dst[1] = fc->status; + dst[2] = fc->extra; + dst[3] = 0xFF; dst[4] = 0xFF; dst[5] = 0xFF; dst[6] = 0xFF; dst[7] = 0xFF; +} + +// Build the full 64-byte stream (8 records). Pads with "no-fault" records. +static size_t KLine_BuildDTCStream(uint8_t *out /*>=64*/) +{ + KLine_SetDTCList(dtcs, 4); //custom test errors + + KLine_SetDFI_Value(dFi); // whatever current DFI value is; this encodes s_dfi_code + + size_t w = 0; + for (size_t i = 0; i < DTC_TOTAL_RECORDS; ++i) { + FaultCode fc; + if (i < s_dtc_count) { + fc = s_dtc_list[i]; + } else { + fc.dtc = 0x00; fc.status = 0xFF; fc.extra = 0xFF; // no-fault filler + } + KLine_FillRecord(&out[w], &fc); + w += DTC_RECORD_SIZE; + } + return w; // 64 +} +// Send one FC packet (0xFC + <=15 body bytes), wait for tester ACK +static int KLine_SendFCChunk_AndWaitAck(const uint8_t *body, uint8_t body_len) +{ + uint8_t payload[DTC_CMD_SIZE + DTC_BODY_MAX_PER_PKT]; // 1 + 15 = 16 + payload[0] = (uint8_t)PACKET_CMD_FaultCodesResponse; // 0xFC + memcpy(&payload[1], body, body_len); + + SendPacket(payload, (uint8_t)(1 + body_len)); + + ParsedPacket ack = (ParsedPacket){0}; + if (!ReceivePacket_Tmo(&ack, 200)) return 0; + if (ack.title != PACKET_CMD_ACK) return 0; + return 1; +} + +// Respond to PACKET_CMD_FaultCodesRead using <=0x10 chunk rule (includes 0xFC) +void KLine_DTCResponse(void) +{ + uint8_t stream[DTC_TOTAL_RECORDS * DTC_RECORD_SIZE]; // 64 + size_t total = KLine_BuildDTCStream(stream); + size_t off = 0; + + while (off < total) { + uint8_t body_len = (uint8_t)((total - off) > DTC_BODY_MAX_PER_PKT + ? DTC_BODY_MAX_PER_PKT + : (total - off)); + if (!KLine_SendFCChunk_AndWaitAck(&stream[off], body_len)) { + // Optional: KLine_EndSession(); + return; + } + off += body_len; + } + + // Tell the master the list is complete + SendAckPacket(); + + // Keep the session alive for immediate keep-alive exchange + KLine_SessionKick(); +} + +#define ASCII_CHUNK 13 // 13 info bytes + 1 f6 ascii title +static size_t append_n(const char *s, size_t n, uint8_t *out, size_t w, size_t maxlen) +{ + size_t i = 0; + while (i < n && s[i] != '\0' && w < maxlen) { + out[w++] = (uint8_t)s[i++]; + } + return w; +} + +size_t BuildCombinedFromInfo(const ControllerInfo *ci, uint8_t *out, size_t maxlen) +{ + size_t w = 0; + w = append_n(ci->client_ident, 12, out, w, maxlen); + w = append_n(ci->unk_ident1, 10, out, w, maxlen); + w = append_n(ci->soft_info, 10, out, w, maxlen); + w = append_n(ci->unk_ident2, 10, out, w, maxlen); + w = append_n(ci->unk_ident3, 10, out, w, maxlen); + w = append_n(ci->unk_ident4, 6, out, w, maxlen); + return w; +} +int Slave_SendAsciiStream_WithAcks(const uint8_t *data, size_t len) +{ + uint8_t payload[1 + ASCII_CHUNK]; // [CMD][chunk...] + size_t off = 0; + + while (off < len) { + uint8_t chunk = (uint8_t)((len - off) > ASCII_CHUNK ? ASCII_CHUNK : (len - off)); + payload[0] = (uint8_t)PACKET_CMD_AsciiData; + for (uint8_t i = 0; i < chunk; ++i) payload[1 + i] = data[off + i]; + + // Send AsciiData packet using your SendPacket() + SendPacket(payload, (uint8_t)(chunk + 1)); + + // Expect a packet-level ACK from tester + ParsedPacket ack = (ParsedPacket){0}; + if (ReceivePacket_Tmo(&ack, 400)) { + if (ack.title == PACKET_CMD_ACK) { + //SendAckPacket(); // mirror your style: ACK←→ACK + KLine_SessionKick(); + } else { + return 0; + } + } else { + return 0; + } + + // Optional strict check: + // if (ack.title != PACKET_CMD_ACK) { /* handle retry/error if you want */ } + + off += chunk; + } + + // Terminal ACK so master breaks out + SendAckPacket(); + KLine_SessionKick(); + + return 1; + + /*ParsedPacket rx = {0}; + if (ReceivePacket_Tmo(&rx, 200)) { //waiting for an ack packet of stay alive + // any packet seen → extend session + // keep-alive: ACK → ACK + KLine_SessionKick(); + + if (rx.title == PACKET_CMD_ACK) { + SendAckPacket(); + KLine_SessionKick(); + return; + } + }*/ + +} + +// Handle ReadIdent request by sending ControllerInfo as AsciiData stream +/*static void KLine_Slave_HandleReadIdent(const ControllerInfo *ci) +{ + uint8_t combined[128] = {0}; + size_t n = BuildCombinedFromInfo(ci, combined, sizeof(combined)); + if (n > 0) { + Slave_SendAsciiStream_WithAcks(combined, n); + } else { + uint8_t nak_payload[1] = { (uint8_t)PACKET_CMD_NAK }; + SendPacket(nak_payload, 1); + } +}*/ + + + + + +// Receive ONE packet with overall timeout (ms). Returns 1=ok, 0=timeout/error. +static int ReceivePacket_Tmo(ParsedPacket *out, uint32_t overall_ms) +{ + uint32_t deadline = HAL_GetTick() + overall_ms; + + // helper: remaining time + #define REMAIN_MS() ({ \ + int32_t d__ = (int32_t)(deadline - HAL_GetTick()); \ + (d__ > 0) ? (uint32_t)d__ : 0U; \ + }) + + ParsedPacket packet = (ParsedPacket){0}; + uint8_t index = 0, v; + uint32_t ms; + + ms = REMAIN_MS(); if (!ms) return 0; + if (!ReadAndAckByte_Tmo(ms, &v)) return 0; + uint8_t packetLength = v; + packet.raw[index++] = packetLength; + + ms = REMAIN_MS(); if (!ms) return 0; + if (!ReadPacketCounter_Tmo(ms, &v)) return 0; // or call ReadPacketCounter() if you need lock-in + uint8_t packetCounter = v; + packet.raw[index++] = packetCounter; + + ms = REMAIN_MS(); if (!ms) return 0; + if (!ReadAndAckByte_Tmo(ms, &v)) return 0; + uint8_t packetCommand = v; + packet.raw[index++] = packetCommand; + + for (int i = 0; i < packetLength - 3; i++) { + ms = REMAIN_MS(); if (!ms) return 0; + if (!ReadAndAckByte_Tmo(ms, &v)) return 0; + packet.raw[index++] = v; + } + + ms = REMAIN_MS(); if (!ms) return 0; + if (!ReadByte_Tmo(ms, &v)) return 0; + if (v != PACKET_END_EXPECTED) return 0; + packet.raw[index++] = v; + packet.length = index; + + 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; + } + + *out = packet; + return 1; +} + + +void KLine_KeepAlivePoll(void) +{ + if (!kline_connection_status) return; + + if (kl_session_deadline == 0) return; + + // timeout? + if (tick_diff(HAL_GetTick(), kl_session_deadline) >= 0) { + KLine_EndSession(); + return; + } + + // try to receive one packet quickly + ParsedPacket rx = {0}; + if (!ReceivePacket_Tmo(&rx, 20)) { + return; // nothing arrived this slice + } + + // any packet seen → extend session + KLine_SessionKick(); + + // keep-alive: ACK → ACK + if (rx.title == PACKET_CMD_ACK) { + SendAckPacket(); + KLine_SessionKick(); + + return; + } + + // (optional) handle other tester commands here if needed +} + + +void KLine_Slave_Poll(void) +{ + // Optional: if you later want to respond to ReadIdent on demand, you can: + // ParsedPacket req; + // if (ReceivePacket_Tmo(&req, 20)) { if (req.title == PACKET_CMD_ReadIdent) KLine_Slave_HandleReadIdent(&info); } +} + +static volatile uint8_t s_eeprom_unlocked_dfi = 0; +int8_t s_dfi_code = 0; // encoded byte sent as first data of 0xEF +static volatile uint8_t s_eeprom_unlocked_dfi_write = 0; + + +// default value to return for address 0x9FFE +static volatile uint8_t s_rom_unlocked_cust; // from your earlier code +static uint16_t s_customerChangeAddress = 0x7E36; // example default -> FD body "36 7E" +static uint16_t s_identAddress = 0x7E56; // will be returned at 0x01 → 0xFE +static const char s_identAscii[10] = "0470504005"; // 10 bytes, no NUL +static const char s_serialNumberAscii[6] = "225832";// 6 ASCII bytes, no NUL + +static const char s_modIndexAscii[6] = "000004"; + + +// Passwords & login-result bodies +static const uint8_t kPwdBody_DFI[] = { 0x00, 0x03, 0xFF, 0xFF }; +static const uint8_t kLoginBody_DFI[] = { 0x00, 0x00, 0x00, 0xBF, 0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45 }; +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, 0x41, 0x31, 0x32, 0x50, 0x35, 0x34, 0x11, 0x02, 0x00 }; + +static const uint8_t kReadIdentAddrBody[] = { 0x02, 0x00, 0xC6 }; + +static const uint8_t kActivateSyncOut[] = { 0x02, 0x88, 0x01, 0x04, 0x06, 0x01 }; + +void KLine_SetDFI_Value(float dfi) +{ + // encode: master decodes as ((int8_t)raw[3]) * 3.0 / 256.0 + float scaled = dfi * 256.0f / 3.0f; + int val = (int)lroundf(scaled); + if (val < -128) val = -128; + if (val > 127) val = 127; + s_dfi_code = (int8_t)val; +} +float KLine_GetDFI(void) +{ + return ((float)s_dfi_code * 3.0f) / 256.0f; +} + +void KLine_SetCustomerChangeAddress(uint16_t addr) { s_customerChangeAddress = addr; } + +/*void KLine_SetModIndexAscii(const char *six_digits) +{ + if (!six_digits) return; + for (int i = 0; i < 6; ++i) { + char c = six_digits[i]; + s_modIndexAscii[i] = (c ? c : '0'); + } +}*/ + +static int KLine_SendLoginResultBody_AndAck(const uint8_t *body, size_t len) +{ + uint8_t payload[1 + 16]; // 0xF0 + up to 16 bytes (we send 13) + uint8_t idx = 0; + + payload[idx++] = 0xF0; // login result command + for (size_t i = 0; i < len; ++i) payload[idx++] = body[i]; + + SendPacket(payload, idx); + + ParsedPacket ack = (ParsedPacket){0}; + if (ReceivePacket_Tmo(&ack, 200)) { + if (ack.title == PACKET_CMD_ACK) { + SendAckPacket(); // mirror your style: ACK←→ACK + KLine_SessionKick(); + + } else { + return 0; + } + } else { + return 0; + } + KLine_SessionKick(); + return 1; +} +static void KLine_HandlePassword(const uint8_t *body, uint8_t body_len) +{ + if (body_len == sizeof(kPwdBody_DFI) && + memcmp(body, kPwdBody_DFI, sizeof(kPwdBody_DFI)) == 0) + { + s_eeprom_unlocked_dfi = 1; + (void)KLine_SendLoginResultBody_AndAck(kLoginBody_DFI, sizeof(kLoginBody_DFI)); + return; + } + + if (body_len == sizeof(kPwdBody_CUST) && + memcmp(body, kPwdBody_CUST, sizeof(kPwdBody_CUST)) == 0) + { + s_rom_unlocked_cust = 1; + (void)KLine_SendLoginResultBody_AndAck(kLoginBody_CUST, sizeof(kLoginBody_CUST)); + return; + } + if (body_len == sizeof(kPwdBody_DFI_WRITE) && + memcmp(body, kPwdBody_DFI_WRITE, sizeof(kPwdBody_DFI_WRITE)) == 0) + { + s_eeprom_unlocked_dfi_write = 1; + (void)KLine_SendLoginResultBody_AndAck(kLoginBody_DFI, sizeof(kLoginBody_DFI)); + return; + } + // wrong password: clear both + s_eeprom_unlocked_dfi = 0; + s_eeprom_unlocked_dfi_write = 0; + s_rom_unlocked_cust = 0; + // (optional) Send NAK + // uint8_t nak = PACKET_CMD_NAK; SendPacket(&nak,1); +} +static void KLine_HandleWriteEeprom(const uint8_t *body, uint8_t body_len) +{ + // Body: [len][addr_hi][addr_lo][value][csum] + if (body_len < 5U) return; + + const uint8_t len_req = body[0]; + const uint16_t addr = ((uint16_t)body[1] << 8) | body[2]; + const uint8_t value = body[3]; + const uint8_t csum = body[4]; + + // Only allow if write unlock is active + if (!s_eeprom_unlocked_dfi_write) return; + + // Only handle DFI @ 0x0044, len 0x02 + if (addr != 0x0044 || len_req != 0x02) return; + + // Simple checksum rule: complement must match + if ((uint8_t)(value + csum) != 0) { + // Optional NAK on bad checksum: + // uint8_t nak = PACKET_CMD_NAK; SendPacket(&nak, 1); + return; + } + + // Update DFI code in RAM (your GetDFI uses s_dfi_code -> float) + s_dfi_code = (int8_t)value; + memWrite = 1; + + // Send WriteEepromResponse (0xF9) — often with empty body besides the title + uint8_t payload[1] = { (uint8_t)PACKET_CMD_WriteEepromResponse }; // 0xF9 + SendPacket(payload, (uint8_t)sizeof(payload)); + + ParsedPacket ack = (ParsedPacket){0}; + if (ReceivePacket_Tmo(&ack, 200) && ack.title == PACKET_CMD_ACK) { + SendAckPacket(); + KLine_SessionKick(); + } +} + +static void KLine_HandleReadEeprom(const uint8_t *body, uint8_t body_len) +{ + // Body: [len_req][addr_hi][addr_lo] + if (body_len < 3U) return; + + const uint8_t len_req = body[0]; + const uint16_t addr = ((uint16_t)body[1] << 8) | body[2]; + + // ---- DFI @ 0x0044, len=2 (requires DFI unlock) ---- + if (addr == 0x0044 && len_req == 0x02) { + if (!s_eeprom_unlocked_dfi) return; + + uint8_t payload[3]; + payload[0] = (uint8_t)PACKET_CMD_ReadEepromResponse; // 0xEF + payload[1] = (uint8_t)s_dfi_code; // dfi1 + payload[2] = 0x00; // dfi2 (placeholder) + + SendPacket(payload, (uint8_t)sizeof(payload)); + + ParsedPacket ack = (ParsedPacket){0}; + if (ReceivePacket_Tmo(&ack, 200) && ack.title == PACKET_CMD_ACK) { + SendAckPacket(); + KLine_SessionKick(); + } + return; + } + + // ---- Serial number @ 0x0080, len=6 (no unlock) ---- + if (addr == 0x0080 && len_req == 0x06) { + uint8_t payload[1 + 6]; + payload[0] = (uint8_t)PACKET_CMD_ReadEepromResponse; // 0xEF + for (int i = 0; i < 6; ++i) payload[1 + i] = (uint8_t)s_serialNumberAscii[i]; + + SendPacket(payload, (uint8_t)sizeof(payload)); + + ParsedPacket ack = (ParsedPacket){0}; + if (ReceivePacket_Tmo(&ack, 200) && ack.title == PACKET_CMD_ACK) { + SendAckPacket(); + KLine_SessionKick(); + } + return; + } + + // Unknown/unsupported EEPROM address → ignore or NAK (uncomment to NAK) + // { uint8_t nak = PACKET_CMD_NAK; SendPacket(&nak, 1); } +} +static void KLine_HandleReadRomEeprom(const uint8_t *body, uint8_t body_len) +{ + // Body: [len_req][addr_hi][addr_lo] + if (body_len < 3U) return; + + const uint8_t len_req = body[0]; + const uint16_t addr = ((uint16_t)body[1] << 8) | body[2]; + + // --- Case 1: 0x9FFE -> return customerChangeAddress (lo, hi) + // Requires ROM unlock (password 0x00 00 82 33). + if (addr == 0x9FFE) { + if (!s_rom_unlocked_cust) return; + if (len_req != 0x02) return; // enforce length + + uint8_t payload[1 + 2]; + payload[0] = (uint8_t)PACKET_CMD_ReadRomEepromResponse; // 0xFD + payload[1] = (uint8_t)(s_customerChangeAddress & 0xFF); // lo + payload[2] = (uint8_t)((s_customerChangeAddress >> 8) & 0xFF); // hi + + SendPacket(payload, (uint8_t)sizeof(payload)); + + ParsedPacket ack = (ParsedPacket){0}; + if (ReceivePacket_Tmo(&ack, 200) && ack.title == PACKET_CMD_ACK) { + SendAckPacket(); // mirror tester's ACK with our ACK + KLine_SessionKick(); + } + return; + } + + // --- Case 2: Ident string at (s_identAddress - 10), len = 10 + // DOES NOT require ROM unlock. + if (addr == (uint16_t)(s_identAddress - 10)) { + if (len_req != 0x0A) return; + + uint8_t payload[1 + 10]; + payload[0] = (uint8_t)PACKET_CMD_ReadRomEepromResponse; // 0xFD + for (int i = 0; i < 10; ++i) payload[1 + i] = (uint8_t)s_identAscii[i]; + + SendPacket(payload, (uint8_t)sizeof(payload)); + + ParsedPacket ack = (ParsedPacket){0}; + if (ReceivePacket_Tmo(&ack, 200) && ack.title == PACKET_CMD_ACK) { + SendAckPacket(); + KLine_SessionKick(); + } + return; + } + + // --- Case 3: customerChangeAddress + 3 -> 6 ASCII bytes (mod index), len = 6 + // Requires ROM unlock. + if (addr == (uint16_t)(s_customerChangeAddress + 3)) { + if (!s_rom_unlocked_cust) return; + if (len_req != 0x06) return; + + uint8_t payload[1 + 6]; + payload[0] = (uint8_t)PACKET_CMD_ReadRomEepromResponse; // 0xFD + for (int i = 0; i < 6; ++i) payload[1 + i] = (uint8_t)s_modIndexAscii[i]; + + SendPacket(payload, (uint8_t)sizeof(payload)); + + ParsedPacket ack = (ParsedPacket){0}; + if (ReceivePacket_Tmo(&ack, 200) && ack.title == PACKET_CMD_ACK) { + SendAckPacket(); + KLine_SessionKick(); + } + return; + } + + // Unknown ROM address: ignore (or NAK if you prefer) + // uint8_t nak = PACKET_CMD_NAK; SendPacket(&nak, 1); +} + +void Kline_CCIResponse(uint8_t len_req){ + // customerChangeIndex (2 bytes) + uint8_t payload[1 + 2]; // [FD][hi][lo] + payload[0] = (uint8_t)PACKET_CMD_ReadRomEepromResponse; // 0xFD + payload[1] = (uint8_t)((s_customerChangeAddress >> 8) & 0xFF); + payload[2] = (uint8_t)( s_customerChangeAddress & 0xFF); + + // If tester asked for exactly 2 bytes; otherwise you could clamp/extend + (void)len_req; // if needed, enforce: if (len_req != 0x02) return; + + SendPacket(payload, sizeof(payload)); + + // Expect tester ACK; respond ACK; keepalive + ParsedPacket ack = (ParsedPacket){0}; + if (ReceivePacket_Tmo(&ack, 200) && ack.title == PACKET_CMD_ACK) { + SendAckPacket(); + KLine_SessionKick(); + } +} +static void KLine_HandleReadIdentAddress(const uint8_t *body, uint8_t body_len) +{ + if (body_len < sizeof(kReadIdentAddrBody)) return; + if (memcmp(body, kReadIdentAddrBody, sizeof(kReadIdentAddrBody)) != 0) return; + + // Respond: 0xFE [lo][hi] + uint8_t payload[1 + 2]; + payload[0] = 0xFE; // custom "ReadIdentAddressResponse" + payload[1] = (uint8_t)(s_identAddress & 0xFF); // lo + payload[2] = (uint8_t)((s_identAddress >> 8) & 0xFF); // hi + + SendPacket(payload, sizeof(payload)); + + ParsedPacket ack = (ParsedPacket){0}; + if (ReceivePacket_Tmo(&ack, 200) && ack.title == PACKET_CMD_ACK) { + SendAckPacket(); // mirror your ACK↔ACK style + KLine_SessionKick(); + } +} +uint8_t SYNC_PULSE_OUT; + +static void KLine_HandleCustomPacket(const uint8_t *body, uint8_t body_len) +{ + if (body_len < sizeof(kActivateSyncOut)){ + if (memcmp(body, kActivateSyncOut, sizeof(kActivateSyncOut)) == 0){ + SYNC_PULSE_OUT = 1; + SendAckPacket(); + KLine_SessionKick(); + } + } +/* + ParsedPacket ack = (ParsedPacket){0}; + if (ReceivePacket_Tmo(&ack, 200) && ack.title == PACKET_CMD_ACK) { + SendAckPacket(); // mirror your ACK↔ACK style + KLine_SessionKick(); + }*/ +} +/*static ControllerInfo info = {0}; // Zero init all strings +ControllerInfo ReadEcuInfo() { + int packet_count = 0; + ParsedPacket *packets = ReceivePackets(&packet_count); + + char combined[128] = {0}; // Temporary buffer to build full ASCII text + + if (packets != NULL) { + for (int i = 0; i < packet_count; i++) { + ParsedPacket *p = &packets[i]; + if (p->type == PACKET_TYPE_ASCII_DATA && p->length > 4) { + size_t len = p->length - 4; // Exclude first 3 and last byte + strncat(combined, (char*)(p->raw + 3), len); + } + } + free(packets); // clean up when done + } + + memset(&info, 0, sizeof(ControllerInfo)); + + // Fill ControllerInfo fields from combined text + strncpy(info.client_ident, combined, 12); + strncpy(info.unk_ident1, combined + 12, 10); + strncpy(info.soft_info, combined + 22, 10); + + if (strlen(combined) > 40) + strncpy(info.unk_ident2, combined + 32, 10); + if (strlen(combined) > 50) + strncpy(info.unk_ident3, combined + 42, 10); + + free(combined); + return info; +}*/ + +#define MAX_EEPROM_DATA 256 // adjust to your ECU’s max response +#define MAX_PACKETS 16 + +static ParsedPacket packet_buffer[MAX_PACKETS]; +static uint8_t eeprom_result[MAX_EEPROM_DATA]; + +uint8_t* ReadRomEeprom(uint16_t address, uint8_t count, int* outLength) +{ + uint8_t request[4] = { + PACKET_CMD_ReadRomEeprom, // 0x08 usually + count, + (uint8_t)(address >> 8), + (uint8_t)(address & 0xFF) + }; + + int packetCount = 0; + ParsedPacket* packets = SendCustom(request, sizeof(request), &packetCount, 0); + if (!packets || packetCount == 0) { + *outLength = 0; + return NULL; + } + + // Check for NAK + if (packetCount == 1 && packets[0].type == PACKET_TYPE_NAK) { + *outLength = 0; + return NULL; + } + + // Collect useful packets (non-Ack/Nak) + int usefulCount = 0; + for (int i = 0; i < packetCount && usefulCount < MAX_PACKETS; i++) { + if (!packets[i].isAckNak) { + packet_buffer[usefulCount++] = packets[i]; + } + } + + if (usefulCount != 1) { + *outLength = 0; + return NULL; // Expect exactly one useful packet + } + + int dataLen = packet_buffer[0].length - 4; // skip 3-byte header + 1-byte end + if (dataLen > MAX_EEPROM_DATA) { + dataLen = MAX_EEPROM_DATA; // clamp to buffer size + } + + memcpy(eeprom_result, packet_buffer[0].raw + 3, dataLen); + *outLength = dataLen; + + return eeprom_result; // static buffer pointer +} + +static void KLine_TerminateAndRearm(void) +{ + // Drop session state + kline_connection_status = 0; + kl_session_deadline = 0; + + // Clear feature unlocks (DFI/ROM) + s_eeprom_unlocked_dfi = 0; + s_rom_unlocked_cust = 0; + + // Stop any pending command processing from ISR + kline_cmd_pending = 0; + + // Reset packet counters (TX/RX sequencing) + ResetPacketCounter(); + + // Make sure RX IT isn’t mid-flight; then drop UART and rearm 5-baud + // (AbortReceive_IT only if you enabled it; otherwise DeInit is fine) + // HAL_UART_AbortReceive_IT(&huart1); + HAL_UART_DeInit(&huart1); + + // Go back to EXTI/bit-bang listen + KLine_Rearm5Baud(); +} + +// Handle tester's End command: ACK then terminate +static void KLine_HandleEnd(void) +{ + // ACK the End command as a packet-level ACK + SendAckPacket(); + + // Immediately end session and rearm + KLine_TerminateAndRearm(); +} + diff --git a/Core/Kline_Libs/kline.h b/Core/Kline_Libs/kline.h new file mode 100644 index 0000000..b5b3cf9 --- /dev/null +++ b/Core/Kline_Libs/kline.h @@ -0,0 +1,38 @@ +/* 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_ */ diff --git a/Core/Kline_Libs/psg_prop.h b/Core/Kline_Libs/psg_prop.h new file mode 100644 index 0000000..c091be3 --- /dev/null +++ b/Core/Kline_Libs/psg_prop.h @@ -0,0 +1,25 @@ +/* + * psg_prop.h + * + * Created on: Aug 19, 2025 + * Author: herli + */ + +#ifndef INC_PSG_PROP_H_ +#define INC_PSG_PROP_H_ + +typedef struct { + char client_ident[13]; // 12 + null + 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" +#define i_modIndex "000004" + + + +#endif /* INC_PSG_PROP_H_ */ diff --git a/Core/Src/main.c b/Core/Src/main.c index 3ab19f6..0f2c336 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -31,7 +31,8 @@ #include "ee_manager.h" #include "toothed_wheel.h" #include "can_read_pump_data.h" - +#include "kline.h" +#include "IKW1281Connection.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -75,6 +76,7 @@ TIM_HandleTypeDef htim7; TIM_HandleTypeDef htim8; TIM_HandleTypeDef htim15; TIM_HandleTypeDef htim16; +TIM_HandleTypeDef htim17; UART_HandleTypeDef huart1; @@ -108,6 +110,7 @@ static void MX_TIM16_Init(void); static void MX_RTC_Init(void); static void MX_TIM8_Init(void); static void MX_OPAMP2_Init(void); +static void MX_TIM17_Init(void); /* USER CODE BEGIN PFP */ void DAC_Voltaje(uint8_t isPeak); void EvaluateInjection(void); @@ -292,6 +295,7 @@ int main(void) MX_RTC_Init(); MX_TIM8_Init(); MX_OPAMP2_Init(); + MX_TIM17_Init(); /* USER CODE BEGIN 2 */ CAN_AppInit(); //ford might be too fast @@ -343,6 +347,9 @@ int main(void) }/*else{ SYNC_Pulse_EnableGPIO(); }*/ + + KLine_Slave_Init(); + /* USER CODE END 2 */ /* Infinite loop */ @@ -352,6 +359,8 @@ int main(void) /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ + KLine_Service(); + KLine_ServiceCommands(); // <-- NEW: react to ISR kick, process full packet TW_Service(); CAN_Service(); // drain queued frames whenever HW has room @@ -1304,6 +1313,42 @@ static void MX_TIM16_Init(void) } +/** + * @brief TIM17 Initialization Function + * @param None + * @retval None + */ +static void MX_TIM17_Init(void) +{ + + /* USER CODE BEGIN TIM17_Init 0 */ + + /* USER CODE END TIM17_Init 0 */ + + /* USER CODE BEGIN TIM17_Init 1 */ + + /* USER CODE END TIM17_Init 1 */ + htim17.Instance = TIM17; + htim17.Init.Prescaler = 160-1; + htim17.Init.CounterMode = TIM_COUNTERMODE_UP; + htim17.Init.Period = 65535; + htim17.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + htim17.Init.RepetitionCounter = 0; + htim17.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if (HAL_TIM_Base_Init(&htim17) != HAL_OK) + { + Error_Handler(); + } + if (HAL_TIM_OnePulse_Init(&htim17, TIM_OPMODE_SINGLE) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN TIM17_Init 2 */ + + /* USER CODE END TIM17_Init 2 */ + +} + /** * @brief USART1 Initialization Function * @param None diff --git a/Core/Src/stm32g4xx_hal_msp.c b/Core/Src/stm32g4xx_hal_msp.c index cd2b123..4dc40ee 100644 --- a/Core/Src/stm32g4xx_hal_msp.c +++ b/Core/Src/stm32g4xx_hal_msp.c @@ -639,6 +639,8 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base) /* TIM1 interrupt Init */ HAL_NVIC_SetPriority(TIM1_BRK_TIM15_IRQn, 5, 0); HAL_NVIC_EnableIRQ(TIM1_BRK_TIM15_IRQn); + HAL_NVIC_SetPriority(TIM1_TRG_COM_TIM17_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(TIM1_TRG_COM_TIM17_IRQn); HAL_NVIC_SetPriority(TIM1_CC_IRQn, 0, 0); HAL_NVIC_EnableIRQ(TIM1_CC_IRQn); /* USER CODE BEGIN TIM1_MspInit 1 */ @@ -746,6 +748,20 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base) /* USER CODE END TIM16_MspInit 1 */ } + else if(htim_base->Instance==TIM17) + { + /* USER CODE BEGIN TIM17_MspInit 0 */ + + /* USER CODE END TIM17_MspInit 0 */ + /* Peripheral clock enable */ + __HAL_RCC_TIM17_CLK_ENABLE(); + /* TIM17 interrupt Init */ + HAL_NVIC_SetPriority(TIM1_TRG_COM_TIM17_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(TIM1_TRG_COM_TIM17_IRQn); + /* USER CODE BEGIN TIM17_MspInit 1 */ + + /* USER CODE END TIM17_MspInit 1 */ + } } @@ -823,6 +839,14 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base) /* HAL_NVIC_DisableIRQ(TIM1_BRK_TIM15_IRQn); */ /* USER CODE END TIM1:TIM1_BRK_TIM15_IRQn disable */ + /* USER CODE BEGIN TIM1:TIM1_TRG_COM_TIM17_IRQn disable */ + /** + * Uncomment the line below to disable the "TIM1_TRG_COM_TIM17_IRQn" interrupt + * Be aware, disabling shared interrupt may affect other IPs + */ + /* HAL_NVIC_DisableIRQ(TIM1_TRG_COM_TIM17_IRQn); */ + /* USER CODE END TIM1:TIM1_TRG_COM_TIM17_IRQn disable */ + HAL_NVIC_DisableIRQ(TIM1_CC_IRQn); /* USER CODE BEGIN TIM1_MspDeInit 1 */ @@ -936,6 +960,27 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base) /* USER CODE END TIM16_MspDeInit 1 */ } + else if(htim_base->Instance==TIM17) + { + /* USER CODE BEGIN TIM17_MspDeInit 0 */ + + /* USER CODE END TIM17_MspDeInit 0 */ + /* Peripheral clock disable */ + __HAL_RCC_TIM17_CLK_DISABLE(); + + /* TIM17 interrupt DeInit */ + /* USER CODE BEGIN TIM17:TIM1_TRG_COM_TIM17_IRQn disable */ + /** + * Uncomment the line below to disable the "TIM1_TRG_COM_TIM17_IRQn" interrupt + * Be aware, disabling shared interrupt may affect other IPs + */ + /* HAL_NVIC_DisableIRQ(TIM1_TRG_COM_TIM17_IRQn); */ + /* USER CODE END TIM17:TIM1_TRG_COM_TIM17_IRQn disable */ + + /* USER CODE BEGIN TIM17_MspDeInit 1 */ + + /* USER CODE END TIM17_MspDeInit 1 */ + } } @@ -1001,6 +1046,9 @@ void HAL_UART_MspInit(UART_HandleTypeDef* huart) GPIO_InitStruct.Alternate = GPIO_AF7_USART1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + /* USART1 interrupt Init */ + HAL_NVIC_SetPriority(USART1_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(USART1_IRQn); /* USER CODE BEGIN USART1_MspInit 1 */ /* USER CODE END USART1_MspInit 1 */ @@ -1031,6 +1079,8 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* huart) */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10); + /* USART1 interrupt DeInit */ + HAL_NVIC_DisableIRQ(USART1_IRQn); /* USER CODE BEGIN USART1_MspDeInit 1 */ /* USER CODE END USART1_MspDeInit 1 */ diff --git a/Core/Src/stm32g4xx_it.c b/Core/Src/stm32g4xx_it.c index a972b2a..72632e2 100644 --- a/Core/Src/stm32g4xx_it.c +++ b/Core/Src/stm32g4xx_it.c @@ -69,6 +69,8 @@ extern TIM_HandleTypeDef htim2; extern TIM_HandleTypeDef htim3; extern TIM_HandleTypeDef htim6; extern TIM_HandleTypeDef htim15; +extern TIM_HandleTypeDef htim17; +extern UART_HandleTypeDef huart1; /* USER CODE BEGIN EV */ /* USER CODE END EV */ @@ -282,6 +284,21 @@ void TIM1_BRK_TIM15_IRQHandler(void) /* USER CODE END TIM1_BRK_TIM15_IRQn 1 */ } +/** + * @brief This function handles TIM1 trigger and commutation interrupts and TIM17 global interrupt. + */ +void TIM1_TRG_COM_TIM17_IRQHandler(void) +{ + /* USER CODE BEGIN TIM1_TRG_COM_TIM17_IRQn 0 */ + + /* USER CODE END TIM1_TRG_COM_TIM17_IRQn 0 */ + HAL_TIM_IRQHandler(&htim1); + HAL_TIM_IRQHandler(&htim17); + /* USER CODE BEGIN TIM1_TRG_COM_TIM17_IRQn 1 */ + + /* USER CODE END TIM1_TRG_COM_TIM17_IRQn 1 */ +} + /** * @brief This function handles TIM1 capture compare interrupt. */ @@ -357,6 +374,20 @@ void TIM3_IRQHandler(void) /* USER CODE END TIM3_IRQn 1 */ } +/** + * @brief This function handles USART1 global interrupt / USART1 wake-up interrupt through EXTI line 25. + */ +void USART1_IRQHandler(void) +{ + /* USER CODE BEGIN USART1_IRQn 0 */ + + /* USER CODE END USART1_IRQn 0 */ + HAL_UART_IRQHandler(&huart1); + /* USER CODE BEGIN USART1_IRQn 1 */ + + /* USER CODE END USART1_IRQn 1 */ +} + /** * @brief This function handles TIM6 global interrupt, DAC1 and DAC3 channel underrun error interrupts. */ diff --git a/README.md b/README.md index 831157c..bf3a69c 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ -Powered by HERLICSON 19/11/2025 +Powered by Herlic Components 30/03/2026 -forked from 1.8.7 t06301. -trying to make better general project without touching working one. +Forked from 1.8.7.T06216 -this is being made for: +This is main (general) project with git version control. +It is called v2 because of K-Line implementation. -soft v1 C062_2.V50 -soft v2 C062_0.P64 -Mod Index 000018 -0470504003 -mejore lo del syncout, la implementacion y poder cambiarlo por can con el watchdog. -El arreglo esta comprobado en el T06301 y porteado aqui, asi que habra que ver si funciona. -Last updated 15/01/2025 \ No newline at end of file + + + + + + +Last updated 30/03/2026 \ No newline at end of file diff --git a/hpsg5-controller_v2-stm32g4 Debug.launch b/hpsg5-controller_v2-stm32g4 Debug.launch new file mode 100644 index 0000000..20cd27d --- /dev/null +++ b/hpsg5-controller_v2-stm32g4 Debug.launch @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hpsg5-controller_v2-stm32g4.ioc b/hpsg5-controller_v2-stm32g4.ioc index 018b0e2..9e20296 100644 --- a/hpsg5-controller_v2-stm32g4.ioc +++ b/hpsg5-controller_v2-stm32g4.ioc @@ -99,7 +99,8 @@ Mcu.IP18=TIM8 Mcu.IP19=TIM15 Mcu.IP2=COMP4 Mcu.IP20=TIM16 -Mcu.IP21=USART1 +Mcu.IP21=TIM17 +Mcu.IP22=USART1 Mcu.IP3=DAC1 Mcu.IP4=DMA Mcu.IP5=FDCAN1 @@ -107,7 +108,7 @@ Mcu.IP6=NVIC Mcu.IP7=OPAMP1 Mcu.IP8=OPAMP2 Mcu.IP9=RCC -Mcu.IPNb=22 +Mcu.IPNb=23 Mcu.Name=STM32G431K(6-8-B)Ux Mcu.Package=UFQFPN32 Mcu.Pin0=PF0-OSC_IN @@ -147,12 +148,14 @@ Mcu.Pin39=VP_TIM8_VS_no_output1 Mcu.Pin4=PA3 Mcu.Pin40=VP_TIM15_VS_ClockSourceINT Mcu.Pin41=VP_TIM16_VS_ClockSourceINT +Mcu.Pin42=VP_TIM17_VS_ClockSourceINT +Mcu.Pin43=VP_TIM17_VS_OPM Mcu.Pin5=PA4 Mcu.Pin6=PA5 Mcu.Pin7=PA6 Mcu.Pin8=PA7 Mcu.Pin9=PB0 -Mcu.PinsNb=42 +Mcu.PinsNb=44 Mcu.ThirdPartyNb=0 Mcu.UserConstants= Mcu.UserName=STM32G431KBUx @@ -174,9 +177,11 @@ NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:false\:true\:false NVIC.TIM1_BRK_TIM15_IRQn=true\:5\:0\:true\:false\:true\:true\:true\:true NVIC.TIM1_CC_IRQn=true\:0\:0\:true\:false\:true\:true\:true\:true +NVIC.TIM1_TRG_COM_TIM17_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true NVIC.TIM2_IRQn=true\:1\:0\:true\:false\:true\:true\:true\:true NVIC.TIM3_IRQn=true\:2\:0\:true\:false\:true\:true\:true\:true 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.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false PA0.Signal=S_TIM2_CH1 PA10.Mode=Asynchronous @@ -265,8 +270,8 @@ ProjectManager.MainLocation=Core/Src ProjectManager.NoMain=false ProjectManager.PreviousToolchain=STM32CubeIDE ProjectManager.ProjectBuild=false -ProjectManager.ProjectFileName=KBU6_1.8.7.T06216.ioc -ProjectManager.ProjectName=KBU6_1.8.7.T06216 +ProjectManager.ProjectFileName=hpsg5-controller_v2-stm32g4.ioc +ProjectManager.ProjectName=hpsg5-controller_v2-stm32g4 ProjectManager.ProjectStructure= ProjectManager.RegisterCallBack= ProjectManager.StackSize=0x400 @@ -275,7 +280,7 @@ ProjectManager.ToolChainLocation= ProjectManager.UAScriptAfterPath= ProjectManager.UAScriptBeforePath= ProjectManager.UnderRoot=true -ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_ADC1_Init-ADC1-false-HAL-true,5-MX_ADC2_Init-ADC2-false-HAL-true,6-MX_COMP4_Init-COMP4-false-HAL-true,7-MX_DAC1_Init-DAC1-false-HAL-true,8-MX_FDCAN1_Init-FDCAN1-false-HAL-true,9-MX_OPAMP1_Init-OPAMP1-false-HAL-true,10-MX_TIM2_Init-TIM2-false-HAL-true,11-MX_TIM4_Init-TIM4-false-HAL-true,12-MX_USART1_UART_Init-USART1-false-HAL-true,13-MX_TIM1_Init-TIM1-false-HAL-true,14-MX_TIM3_Init-TIM3-false-HAL-true,15-MX_TIM6_Init-TIM6-false-HAL-true,16-MX_TIM7_Init-TIM7-false-HAL-true,17-MX_TIM15_Init-TIM15-false-HAL-true,18-MX_TIM16_Init-TIM16-false-HAL-true,19-MX_RTC_Init-RTC-false-HAL-true,20-MX_TIM8_Init-TIM8-false-HAL-true,21-MX_COMP1_Init-COMP1-false-HAL-true,22-MX_OPAMP2_Init-OPAMP2-false-HAL-true +ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_ADC1_Init-ADC1-false-HAL-true,5-MX_ADC2_Init-ADC2-false-HAL-true,6-MX_COMP4_Init-COMP4-false-HAL-true,7-MX_DAC1_Init-DAC1-false-HAL-true,8-MX_FDCAN1_Init-FDCAN1-false-HAL-true,9-MX_OPAMP1_Init-OPAMP1-false-HAL-true,10-MX_TIM2_Init-TIM2-false-HAL-true,11-MX_TIM4_Init-TIM4-false-HAL-true,12-MX_USART1_UART_Init-USART1-false-HAL-true,13-MX_TIM1_Init-TIM1-false-HAL-true,14-MX_TIM3_Init-TIM3-false-HAL-true,15-MX_TIM6_Init-TIM6-false-HAL-true,16-MX_TIM7_Init-TIM7-false-HAL-true,17-MX_TIM15_Init-TIM15-false-HAL-true,18-MX_TIM16_Init-TIM16-false-HAL-true,19-MX_RTC_Init-RTC-false-HAL-true,20-MX_TIM8_Init-TIM8-false-HAL-true,21-MX_OPAMP2_Init-OPAMP2-false-HAL-true,22-MX_TIM17_Init-TIM17-false-HAL-true RCC.ADC12Freq_Value=160000000 RCC.AHBFreq_Value=160000000 RCC.APB1Freq_Value=160000000 @@ -339,6 +344,9 @@ TIM15.Prescaler=160-1 TIM15.TIM_MasterOutputTrigger=TIM_TRGO_UPDATE TIM16.IPParameters=Prescaler TIM16.Prescaler=16000-1 +TIM17.IPParameters=Prescaler,PeriodNoDither +TIM17.PeriodNoDither=65535 +TIM17.Prescaler=160-1 TIM2.Channel-Input_Capture1_from_TI1=TIM_CHANNEL_1 TIM2.Channel-Input_Capture3_from_TI3_Remap_TIM2=TIM_CHANNEL_3 TIM2.ICFilter_CH1=15 @@ -386,6 +394,10 @@ VP_TIM15_VS_ClockSourceINT.Mode=Internal VP_TIM15_VS_ClockSourceINT.Signal=TIM15_VS_ClockSourceINT VP_TIM16_VS_ClockSourceINT.Mode=Enable_Timer VP_TIM16_VS_ClockSourceINT.Signal=TIM16_VS_ClockSourceINT +VP_TIM17_VS_ClockSourceINT.Mode=Enable_Timer +VP_TIM17_VS_ClockSourceINT.Signal=TIM17_VS_ClockSourceINT +VP_TIM17_VS_OPM.Mode=OPM_bit +VP_TIM17_VS_OPM.Signal=TIM17_VS_OPM VP_TIM1_VS_ClockSourceINT.Mode=Internal VP_TIM1_VS_ClockSourceINT.Signal=TIM1_VS_ClockSourceINT VP_TIM1_VS_OPM.Mode=OPM_bit