Terrible code refactor, took 4 hours to get the protocol working. Bluetooth commands are completely broken
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "IKW1281Connection.h"
|
#include "IKW1281Connection.h"
|
||||||
#include "kline.h"
|
#include "kline_fsm.h"
|
||||||
|
|
||||||
extern UART_HandleTypeDef huart3;
|
extern UART_HandleTypeDef huart3;
|
||||||
|
|
||||||
@@ -63,41 +63,40 @@ static void HC06_TxKick(void)
|
|||||||
if (s_tx_count == 0u) return;
|
if (s_tx_count == 0u) return;
|
||||||
|
|
||||||
hc06_tx_item_t *it = &s_tx_q[s_tx_head];
|
hc06_tx_item_t *it = &s_tx_q[s_tx_head];
|
||||||
|
|
||||||
// wait until due timestamp
|
|
||||||
if ((int32_t)(HAL_GetTick() - it->due_ms) < 0) return;
|
if ((int32_t)(HAL_GetTick() - it->due_ms) < 0) return;
|
||||||
|
|
||||||
if (HAL_UART_Transmit_IT(&huart3, it->buf, it->len) == HAL_OK) {
|
if (HAL_UART_Transmit_IT(&huart3, it->buf, it->len) == HAL_OK)
|
||||||
s_tx_busy = 1u;
|
s_tx_busy = 1u;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Call this from HAL_UART_TxCpltCallback() for USART2
|
|
||||||
void HC06_UART_TxCpltCallback(UART_HandleTypeDef *huart)
|
void HC06_UART_TxCpltCallback(UART_HandleTypeDef *huart)
|
||||||
{
|
{
|
||||||
if (!huart) return;
|
if (!huart) return;
|
||||||
if (huart->Instance != huart3.Instance) return;
|
if (huart->Instance != huart3.Instance) return;
|
||||||
if (!s_tx_busy) return;
|
if (!s_tx_busy) return;
|
||||||
|
|
||||||
// pop the item that just finished
|
|
||||||
if (s_tx_count > 0u) {
|
if (s_tx_count > 0u) {
|
||||||
s_tx_head = (uint8_t)((s_tx_head + 1u) % HC06_TX_QUEUE_LEN);
|
s_tx_head = (uint8_t)((s_tx_head + 1u) % HC06_TX_QUEUE_LEN);
|
||||||
s_tx_count--;
|
s_tx_count--;
|
||||||
}
|
}
|
||||||
s_tx_busy = 0u;
|
s_tx_busy = 0u;
|
||||||
|
|
||||||
// send next (if already due)
|
|
||||||
HC06_TxKick();
|
HC06_TxKick();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =========================
|
/* =========================
|
||||||
Your project functions
|
Project-specific blocking helpers
|
||||||
========================= */
|
=========================
|
||||||
extern void InitPSG5Comm(void);
|
These still call into the old kline.c functions (ReadDfi, WriteDfi, etc.)
|
||||||
|
that perform synchronous KWP transactions. They will need to be ported to
|
||||||
|
FSM async commands in a future step. For now they are preserved as-is.
|
||||||
|
DO NOT call them unless KLine_FSM_IsConnected() returns true.
|
||||||
|
*/
|
||||||
extern float ReadDfi(void);
|
extern float ReadDfi(void);
|
||||||
extern int WriteDfi(float dfi, int version);
|
extern int WriteDfi(float dfi, int version);
|
||||||
extern int ClearFaultCodes(void);
|
extern int ClearFaultCodes(void);
|
||||||
extern uint8_t ReadAudiPin(uint16_t *pin);
|
extern uint8_t ReadAudiPin(uint16_t *pin);
|
||||||
|
extern uint8_t ReadVoltage(uint16_t *volt);
|
||||||
|
|
||||||
static void HC06_ForceRearmRx(void);
|
static void HC06_ForceRearmRx(void);
|
||||||
|
|
||||||
static void BT_InitPSG5Comm(void);
|
static void BT_InitPSG5Comm(void);
|
||||||
@@ -113,14 +112,11 @@ static void BT_ReadAudiPin(void);
|
|||||||
#define HC06_DO_AT_SETUP_AT_BOOT 1
|
#define HC06_DO_AT_SETUP_AT_BOOT 1
|
||||||
#define HC06_NAME "HC - BPDT - Adapter"
|
#define HC06_NAME "HC - BPDT - Adapter"
|
||||||
|
|
||||||
// Many HC-06 firmwares do NOT support PIN change; leave disabled unless proven.
|
|
||||||
#define HC06_TRY_SET_PIN 0
|
#define HC06_TRY_SET_PIN 0
|
||||||
#define HC06_PIN_STR "1234"
|
#define HC06_PIN_STR "1234"
|
||||||
|
|
||||||
// AT command send style: you already confirmed AT\r\n works.
|
|
||||||
#define HC06_AT_USE_CRLF 1
|
#define HC06_AT_USE_CRLF 1
|
||||||
|
|
||||||
// AT wait timeouts (ms)
|
|
||||||
#define HC06_AT_WAIT_MS_AT 600
|
#define HC06_AT_WAIT_MS_AT 600
|
||||||
#define HC06_AT_WAIT_MS_NAME 800
|
#define HC06_AT_WAIT_MS_NAME 800
|
||||||
#define HC06_AT_WAIT_MS_PIN 800
|
#define HC06_AT_WAIT_MS_PIN 800
|
||||||
@@ -131,7 +127,6 @@ static void BT_ReadAudiPin(void);
|
|||||||
volatile uint8_t g_hc06_frame_ready = 0;
|
volatile uint8_t g_hc06_frame_ready = 0;
|
||||||
hc06_frame_t g_hc06_frame;
|
hc06_frame_t g_hc06_frame;
|
||||||
|
|
||||||
/* 1-byte RX for everything */
|
|
||||||
static uint8_t rx_byte;
|
static uint8_t rx_byte;
|
||||||
uint8_t commandPending = 0;
|
uint8_t commandPending = 0;
|
||||||
|
|
||||||
@@ -162,9 +157,7 @@ static void AT_Clear(void)
|
|||||||
static void AT_Wait(uint32_t timeout_ms)
|
static void AT_Wait(uint32_t timeout_ms)
|
||||||
{
|
{
|
||||||
uint32_t t0 = HAL_GetTick();
|
uint32_t t0 = HAL_GetTick();
|
||||||
while (!s_at_done && (HAL_GetTick() - t0) < timeout_ms) {
|
while (!s_at_done && (HAL_GetTick() - t0) < timeout_ms) {}
|
||||||
// idle
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =========================
|
/* =========================
|
||||||
@@ -180,7 +173,7 @@ typedef enum {
|
|||||||
} hc06_rx_state_t;
|
} hc06_rx_state_t;
|
||||||
|
|
||||||
static hc06_rx_state_t st = ST_SYNC;
|
static hc06_rx_state_t st = ST_SYNC;
|
||||||
static uint8_t payload[7]; // [0]=cmd, [1..6]=data bytes
|
static uint8_t payload[7];
|
||||||
static uint8_t pi = 0;
|
static uint8_t pi = 0;
|
||||||
static uint8_t crc = 0;
|
static uint8_t crc = 0;
|
||||||
|
|
||||||
@@ -195,13 +188,11 @@ static void u16_to_le(uint16_t v, uint8_t *lo, uint8_t *hi)
|
|||||||
*hi = (uint8_t)((v >> 8) & 0xFF);
|
*hi = (uint8_t)((v >> 8) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CRC-8 poly 0x07, init 0x00, over [cmd + 6 data bytes] */
|
|
||||||
static uint8_t crc8_update(uint8_t c, uint8_t data)
|
static uint8_t crc8_update(uint8_t c, uint8_t data)
|
||||||
{
|
{
|
||||||
c ^= data;
|
c ^= data;
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++)
|
||||||
c = (c & 0x80) ? (uint8_t)((c << 1) ^ 0x07) : (uint8_t)(c << 1);
|
c = (c & 0x80) ? (uint8_t)((c << 1) ^ 0x07) : (uint8_t)(c << 1);
|
||||||
}
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +230,7 @@ HAL_StatusTypeDef HC06_SendAsciiLn(const char *s)
|
|||||||
|
|
||||||
HAL_StatusTypeDef HC06_SendFrame(uint8_t cmd, uint16_t w1, uint16_t w2, uint16_t w3)
|
HAL_StatusTypeDef HC06_SendFrame(uint8_t cmd, uint16_t w1, uint16_t w2, uint16_t w3)
|
||||||
{
|
{
|
||||||
uint8_t buf[9]; // sync + cmd + 6 data + crc
|
uint8_t buf[9];
|
||||||
uint8_t c = 0;
|
uint8_t c = 0;
|
||||||
|
|
||||||
buf[0] = HC06_SYNC;
|
buf[0] = HC06_SYNC;
|
||||||
@@ -253,19 +244,15 @@ HAL_StatusTypeDef HC06_SendFrame(uint8_t cmd, uint16_t w1, uint16_t w2, uint16_t
|
|||||||
for (int i = 1; i <= 7; i++) c = crc8_update(c, buf[i]);
|
for (int i = 1; i <= 7; i++) c = crc8_update(c, buf[i]);
|
||||||
buf[8] = c;
|
buf[8] = c;
|
||||||
|
|
||||||
// Non-blocking + delayed TX
|
HAL_StatusTypeDef s = HC06_TxEnqueue(buf, (uint16_t)sizeof(buf));
|
||||||
HAL_StatusTypeDef st = HC06_TxEnqueue(buf, (uint16_t)sizeof(buf));
|
|
||||||
HC06_TxKick();
|
HC06_TxKick();
|
||||||
return st;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =========================
|
/* =========================
|
||||||
AT send helpers
|
AT send helpers
|
||||||
========================= */
|
========================= */
|
||||||
static void AT_SendRaw(const char *s)
|
static void AT_SendRaw(const char *s) { (void)HC06_SendAscii(s); }
|
||||||
{
|
|
||||||
(void)HC06_SendAscii(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AT_SendCmd(const char *cmd_no_prefix)
|
static void AT_SendCmd(const char *cmd_no_prefix)
|
||||||
{
|
{
|
||||||
@@ -279,71 +266,12 @@ static void AT_SendCmd(const char *cmd_no_prefix)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* =========================
|
/* =========================
|
||||||
Init
|
Variable-length data reply
|
||||||
========================= */
|
========================= */
|
||||||
void HC06_Init(void)
|
|
||||||
{
|
|
||||||
s_mode = HC06_MODE_AT;
|
|
||||||
HC06_TxReset();
|
|
||||||
g_hc06_frame_ready = 0;
|
|
||||||
HC06_ForceRearmRx();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call once at boot (optional). Module must be NOT connected (LED blinking). */
|
|
||||||
void HC06_AT_BootSetup(void)
|
|
||||||
{
|
|
||||||
#if HC06_DO_AT_SETUP_AT_BOOT
|
|
||||||
// Ensure we're in AT mode and buffer is clean
|
|
||||||
s_mode = HC06_MODE_AT;
|
|
||||||
AT_Clear();
|
|
||||||
|
|
||||||
// 1) Basic AT check
|
|
||||||
#if HC06_AT_USE_CRLF
|
|
||||||
AT_SendRaw("AT\r\n");
|
|
||||||
#else
|
|
||||||
AT_SendRaw("AT");
|
|
||||||
#endif
|
|
||||||
AT_Wait(HC06_AT_WAIT_MS_AT);
|
|
||||||
// Expect "OK" in s_at_buf (you saw OKsetname for name)
|
|
||||||
|
|
||||||
// 2) Set name
|
|
||||||
AT_Clear();
|
|
||||||
{
|
|
||||||
char cmd[64];
|
|
||||||
snprintf(cmd, sizeof(cmd), "NAME%s", HC06_NAME);
|
|
||||||
AT_SendCmd(cmd);
|
|
||||||
}
|
|
||||||
AT_Wait(HC06_AT_WAIT_MS_NAME);
|
|
||||||
// Expect "OKsetname"
|
|
||||||
|
|
||||||
#if HC06_TRY_SET_PIN
|
|
||||||
AT_Clear();
|
|
||||||
{
|
|
||||||
char cmd[32];
|
|
||||||
snprintf(cmd, sizeof(cmd), "PIN%s", HC06_PIN_STR);
|
|
||||||
AT_SendCmd(cmd);
|
|
||||||
}
|
|
||||||
AT_Wait(HC06_AT_WAIT_MS_PIN);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Switch to BIN mode after setup
|
|
||||||
s_mode = HC06_MODE_BIN;
|
|
||||||
#else
|
|
||||||
// Directly start in binary mode
|
|
||||||
s_mode = HC06_MODE_BIN;
|
|
||||||
#endif
|
|
||||||
HC06_ForceRearmRx();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variable-length reply for CMD 0x84: SYNC, CMD, STATUS, LEN(u16 LE), PAYLOAD, CRC
|
|
||||||
// CRC is over [CMD, STATUS, LENlo, LENhi, PAYLOAD]
|
|
||||||
#define HC06_MAX_PAYLOAD 64
|
#define HC06_MAX_PAYLOAD 64
|
||||||
static HAL_StatusTypeDef HC06_SendDataReply(uint8_t status, const uint8_t *payloadIn, uint16_t len)
|
static HAL_StatusTypeDef HC06_SendDataReply(uint8_t status, const uint8_t *payloadIn, uint16_t len)
|
||||||
{
|
{
|
||||||
if (status != 0x00) {
|
if (status != 0x00) { len = 0; payloadIn = NULL; }
|
||||||
len = 0;
|
|
||||||
payloadIn = NULL;
|
|
||||||
}
|
|
||||||
if (len > HC06_MAX_PAYLOAD) len = HC06_MAX_PAYLOAD;
|
if (len > HC06_MAX_PAYLOAD) len = HC06_MAX_PAYLOAD;
|
||||||
|
|
||||||
uint8_t buf[1 + 1 + 1 + 2 + HC06_MAX_PAYLOAD + 1];
|
uint8_t buf[1 + 1 + 1 + 2 + HC06_MAX_PAYLOAD + 1];
|
||||||
@@ -359,21 +287,64 @@ static HAL_StatusTypeDef HC06_SendDataReply(uint8_t status, const uint8_t *paylo
|
|||||||
buf[idx++] = lo;
|
buf[idx++] = lo;
|
||||||
buf[idx++] = hi;
|
buf[idx++] = hi;
|
||||||
|
|
||||||
for (uint16_t i = 0; i < len; i++) {
|
for (uint16_t i = 0; i < len; i++)
|
||||||
buf[idx++] = payloadIn ? payloadIn[i] : 0;
|
buf[idx++] = payloadIn ? payloadIn[i] : 0;
|
||||||
}
|
|
||||||
|
|
||||||
for (uint16_t i = 1; i < idx; i++) c = crc8_update(c, buf[i]);
|
for (uint16_t i = 1; i < idx; i++) c = crc8_update(c, buf[i]);
|
||||||
buf[idx++] = c;
|
buf[idx++] = c;
|
||||||
|
|
||||||
// Non-blocking + delayed TX
|
HAL_StatusTypeDef s = HC06_TxEnqueue(buf, idx);
|
||||||
HAL_StatusTypeDef st = HC06_TxEnqueue(buf, idx);
|
|
||||||
HC06_TxKick();
|
HC06_TxKick();
|
||||||
return st;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =========================
|
/* =========================
|
||||||
RX callback (1 byte)
|
Init
|
||||||
|
========================= */
|
||||||
|
void HC06_Init(void)
|
||||||
|
{
|
||||||
|
s_mode = HC06_MODE_AT;
|
||||||
|
HC06_TxReset();
|
||||||
|
g_hc06_frame_ready = 0;
|
||||||
|
HC06_ForceRearmRx();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HC06_AT_BootSetup(void)
|
||||||
|
{
|
||||||
|
#if HC06_DO_AT_SETUP_AT_BOOT
|
||||||
|
s_mode = HC06_MODE_AT;
|
||||||
|
AT_Clear();
|
||||||
|
|
||||||
|
AT_SendRaw("AT\r\n");
|
||||||
|
AT_Wait(HC06_AT_WAIT_MS_AT);
|
||||||
|
|
||||||
|
AT_Clear();
|
||||||
|
{
|
||||||
|
char cmd[64];
|
||||||
|
snprintf(cmd, sizeof(cmd), "NAME%s", HC06_NAME);
|
||||||
|
AT_SendCmd(cmd);
|
||||||
|
}
|
||||||
|
AT_Wait(HC06_AT_WAIT_MS_NAME);
|
||||||
|
|
||||||
|
#if HC06_TRY_SET_PIN
|
||||||
|
AT_Clear();
|
||||||
|
{
|
||||||
|
char cmd[32];
|
||||||
|
snprintf(cmd, sizeof(cmd), "PIN%s", HC06_PIN_STR);
|
||||||
|
AT_SendCmd(cmd);
|
||||||
|
}
|
||||||
|
AT_Wait(HC06_AT_WAIT_MS_PIN);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
s_mode = HC06_MODE_BIN;
|
||||||
|
#else
|
||||||
|
s_mode = HC06_MODE_BIN;
|
||||||
|
#endif
|
||||||
|
HC06_ForceRearmRx();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
RX callback (1 byte at a time)
|
||||||
========================= */
|
========================= */
|
||||||
void HC06_UART_RxByteCallback(UART_HandleTypeDef *huart)
|
void HC06_UART_RxByteCallback(UART_HandleTypeDef *huart)
|
||||||
{
|
{
|
||||||
@@ -384,47 +355,30 @@ void HC06_UART_RxByteCallback(UART_HandleTypeDef *huart)
|
|||||||
if (s_mode == HC06_MODE_AT)
|
if (s_mode == HC06_MODE_AT)
|
||||||
{
|
{
|
||||||
char c = (char)x;
|
char c = (char)x;
|
||||||
|
if (c == '\n' || s_at_len >= (sizeof(s_at_buf) - 1)) {
|
||||||
// Capture until newline or full
|
|
||||||
if (c == '\n' || s_at_len >= (sizeof(s_at_buf) - 1))
|
|
||||||
{
|
|
||||||
s_at_buf[s_at_len] = 0;
|
s_at_buf[s_at_len] = 0;
|
||||||
s_at_done = 1;
|
s_at_done = 1;
|
||||||
}
|
} else if (c != '\r') {
|
||||||
else if (c != '\r')
|
|
||||||
{
|
|
||||||
s_at_buf[s_at_len++] = c;
|
s_at_buf[s_at_len++] = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
// continue RX
|
|
||||||
HAL_UART_Receive_IT(&huart3, &rx_byte, 1);
|
HAL_UART_Receive_IT(&huart3, &rx_byte, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Binary frame parsing (robust resync + CRC)
|
|
||||||
switch (st)
|
switch (st)
|
||||||
{
|
{
|
||||||
case ST_SYNC:
|
case ST_SYNC:
|
||||||
if (x == HC06_SYNC) {
|
if (x == HC06_SYNC) { st = ST_CMD; pi = 0; crc = 0; }
|
||||||
st = ST_CMD;
|
|
||||||
pi = 0;
|
|
||||||
crc = 0;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ST_CMD:
|
case ST_CMD:
|
||||||
payload[pi++] = x; // cmd
|
payload[pi++] = x;
|
||||||
crc = crc8_update(crc, x);
|
crc = crc8_update(crc, x);
|
||||||
if (x == CMD_DATA_REQUEST) {
|
st = (x == CMD_DATA_REQUEST) ? ST_REQ_ID : ST_D0;
|
||||||
// short request: SYNC, CMD(0x04), REQ_ID, CRC
|
|
||||||
st = ST_REQ_ID;
|
|
||||||
} else {
|
|
||||||
st = ST_D0;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ST_REQ_ID:
|
case ST_REQ_ID:
|
||||||
payload[pi++] = x; // req_id
|
payload[pi++] = x;
|
||||||
crc = crc8_update(crc, x);
|
crc = crc8_update(crc, x);
|
||||||
st = ST_REQ_CRC;
|
st = ST_REQ_CRC;
|
||||||
break;
|
break;
|
||||||
@@ -432,7 +386,7 @@ void HC06_UART_RxByteCallback(UART_HandleTypeDef *huart)
|
|||||||
case ST_REQ_CRC:
|
case ST_REQ_CRC:
|
||||||
if (x == crc) {
|
if (x == crc) {
|
||||||
g_hc06_frame.cmd = payload[0];
|
g_hc06_frame.cmd = payload[0];
|
||||||
g_hc06_frame.w1 = (uint16_t)payload[1]; // REQ_ID in low byte
|
g_hc06_frame.w1 = (uint16_t)payload[1];
|
||||||
g_hc06_frame.w2 = 0;
|
g_hc06_frame.w2 = 0;
|
||||||
g_hc06_frame.w3 = 0;
|
g_hc06_frame.w3 = 0;
|
||||||
g_hc06_frame_ready = 1;
|
g_hc06_frame_ready = 1;
|
||||||
@@ -441,7 +395,7 @@ void HC06_UART_RxByteCallback(UART_HandleTypeDef *huart)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ST_D0: case ST_D1: case ST_D2: case ST_D3: case ST_D4: case ST_D5:
|
case ST_D0: case ST_D1: case ST_D2: case ST_D3: case ST_D4: case ST_D5:
|
||||||
payload[pi++] = x; // data bytes
|
payload[pi++] = x;
|
||||||
crc = crc8_update(crc, x);
|
crc = crc8_update(crc, x);
|
||||||
if (pi >= 7) st = ST_CRC;
|
if (pi >= 7) st = ST_CRC;
|
||||||
else st = (hc06_rx_state_t)((int)st + 1);
|
else st = (hc06_rx_state_t)((int)st + 1);
|
||||||
@@ -455,7 +409,7 @@ void HC06_UART_RxByteCallback(UART_HandleTypeDef *huart)
|
|||||||
g_hc06_frame.w3 = u16_le(payload[5], payload[6]);
|
g_hc06_frame.w3 = u16_le(payload[5], payload[6]);
|
||||||
g_hc06_frame_ready = 1;
|
g_hc06_frame_ready = 1;
|
||||||
}
|
}
|
||||||
st = ST_SYNC; // resync always
|
st = ST_SYNC;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -471,60 +425,72 @@ void HC06_UART_RxByteCallback(UART_HandleTypeDef *huart)
|
|||||||
========================= */
|
========================= */
|
||||||
static uint8_t reply_cmd(uint8_t cmd) { return (uint8_t)(cmd | 0x80); }
|
static uint8_t reply_cmd(uint8_t cmd) { return (uint8_t)(cmd | 0x80); }
|
||||||
|
|
||||||
// -------------------------------
|
/* --- Data request handlers --- */
|
||||||
// Data Request placeholder handlers
|
|
||||||
// -------------------------------
|
|
||||||
static uint8_t HC06_HandleReq_FwVersion(uint8_t *out, uint16_t *outLen) { (void)out; *outLen = 0; return HC06_STATUS_OK; }
|
|
||||||
|
|
||||||
// These must exist somewhere in your project (or change to match your actual symbols)
|
static uint8_t HC06_HandleReq_FwVersion(uint8_t *out, uint16_t *outLen)
|
||||||
extern char identStr[11];
|
{
|
||||||
extern uint8_t connectionAlive;
|
(void)out;
|
||||||
//extern uint8_t BitBang;
|
*outLen = 0;
|
||||||
|
return HC06_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static uint8_t HC06_HandleReq_Ident(uint8_t *out, uint16_t *outLen)
|
static uint8_t HC06_HandleReq_Ident(uint8_t *out, uint16_t *outLen)
|
||||||
{
|
{
|
||||||
// Send exactly 11 bytes (no null terminator expected/required)
|
/*
|
||||||
uint8_t empty = 1;
|
* Return the soft_info field from the ECU info captured during
|
||||||
for (uint16_t i = 0; i < 11; i++) {
|
* connection. If not yet connected, return zeros.
|
||||||
if((uint8_t)identStr[i]){
|
* Previously this called IdentifyEcu() synchronously — that blocking
|
||||||
empty = 0;
|
* call is gone. The FSM runs IdentifyEcu as part of the connection
|
||||||
break;
|
* sequence, so by the time BT asks for ident the data is ready.
|
||||||
}
|
*/
|
||||||
}
|
const ControllerInfo *info = KLine_FSM_GetEcuInfo();
|
||||||
if(empty){
|
for (uint16_t i = 0; i < 11u; i++)
|
||||||
IdentifyEcu();
|
out[i] = (uint8_t)info->soft_info[i];
|
||||||
}
|
*outLen = 11u;
|
||||||
for (uint16_t i = 0; i < 11; i++) {
|
|
||||||
out[i] = (uint8_t)identStr[i];
|
|
||||||
}
|
|
||||||
*outLen = 11;
|
|
||||||
return HC06_STATUS_OK;
|
return HC06_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t HC06_HandleReq_Status(uint8_t *out, uint16_t *outLen)
|
static uint8_t HC06_HandleReq_Status(uint8_t *out, uint16_t *outLen)
|
||||||
{
|
{
|
||||||
out[0] = connectionAlive;
|
/*
|
||||||
out[1] = BitBang;
|
* Previously returned connectionAlive + BitBang.
|
||||||
*outLen = 2;
|
* Now uses FSM status query — no global state needed.
|
||||||
|
*/
|
||||||
|
out[0] = KLine_FSM_IsConnected() ? 1u : 0u;
|
||||||
|
out[1] = (KLine_FSM_GetStatus() == KLINE_STATUS_CONNECTING) ? 1u : 0u;
|
||||||
|
*outLen = 2u;
|
||||||
return HC06_STATUS_OK;
|
return HC06_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int N_fc = 0;
|
static int s_fault_count = 0;
|
||||||
FaultCode fault_codes[16];
|
static KLine_FaultCode s_fault_cache[KLINE_MAX_FAULT_CODES];
|
||||||
|
|
||||||
static uint8_t HC06_HandleReq_Error(uint8_t *out, uint16_t *outLen)
|
static uint8_t HC06_HandleReq_Error(uint8_t *out, uint16_t *outLen)
|
||||||
{
|
{
|
||||||
for (uint16_t i = 0; i < N_fc; i++) {
|
/*
|
||||||
uint8_t ind = 2*i;
|
* Return fault codes captured by the FSM during connection.
|
||||||
out[ind] = fault_codes[i].dtc;
|
* If a fresh read was requested (BT_ReadDTC), the cache has been
|
||||||
out[ind+1] = fault_codes[i].status;
|
* updated already before this handler runs.
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < s_fault_count; i++) {
|
||||||
|
out[2 * i] = s_fault_cache[i].dtc;
|
||||||
|
out[2 * i + 1] = s_fault_cache[i].status;
|
||||||
}
|
}
|
||||||
*outLen = 2*N_fc;
|
*outLen = (uint16_t)(2 * s_fault_count);
|
||||||
return HC06_STATUS_OK;
|
return HC06_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t HC06_HandleReq_Config(uint8_t *out, uint16_t *outLen) { (void)out; *outLen = 0; return HC06_STATUS_NOT_IMPLEMENTED; }
|
static uint8_t HC06_HandleReq_Config(uint8_t *out, uint16_t *outLen)
|
||||||
static uint8_t HC06_HandleReq_Reserved(uint8_t *out, uint16_t *outLen) { (void)out; *outLen = 0; return HC06_STATUS_NOT_IMPLEMENTED; }
|
{
|
||||||
|
(void)out; *outLen = 0;
|
||||||
|
return HC06_STATUS_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t HC06_HandleReq_Reserved(uint8_t *out, uint16_t *outLen)
|
||||||
|
{
|
||||||
|
(void)out; *outLen = 0;
|
||||||
|
return HC06_STATUS_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
static uint8_t HC06_DispatchDataRequest(uint8_t reqId, uint8_t *out, uint16_t *outLen)
|
static uint8_t HC06_DispatchDataRequest(uint8_t reqId, uint8_t *out, uint16_t *outLen)
|
||||||
{
|
{
|
||||||
@@ -541,203 +507,221 @@ static uint8_t HC06_DispatchDataRequest(uint8_t reqId, uint8_t *out, uint16_t *o
|
|||||||
|
|
||||||
void HC06_Process(void)
|
void HC06_Process(void)
|
||||||
{
|
{
|
||||||
// Always allow delayed TX queue to progress
|
|
||||||
HC06_TxKick();
|
HC06_TxKick();
|
||||||
|
|
||||||
if (!g_hc06_frame_ready) return;
|
if (!g_hc06_frame_ready) return;
|
||||||
g_hc06_frame_ready = 0;
|
g_hc06_frame_ready = 0;
|
||||||
|
|
||||||
const uint8_t cmd = g_hc06_frame.cmd;
|
if (commandPending) return;
|
||||||
const uint16_t w3 = g_hc06_frame.w3;
|
|
||||||
|
|
||||||
if(commandPending){return;}
|
|
||||||
|
|
||||||
commandPending = 1;
|
commandPending = 1;
|
||||||
|
|
||||||
|
const uint8_t cmd = g_hc06_frame.cmd;
|
||||||
|
|
||||||
switch (cmd)
|
switch (cmd)
|
||||||
{
|
{
|
||||||
case CMD_INIT_COMM:
|
case CMD_INIT_COMM:
|
||||||
{
|
|
||||||
BT_InitPSG5Comm();
|
BT_InitPSG5Comm();
|
||||||
} break;
|
break;
|
||||||
|
|
||||||
case CMD_READ_DFI:
|
case CMD_READ_DFI:
|
||||||
{
|
|
||||||
//first should check if connection is alive
|
|
||||||
BT_ReadDfi();
|
BT_ReadDfi();
|
||||||
} break;
|
break;
|
||||||
|
|
||||||
case CMD_WRITE_DFI:
|
case CMD_WRITE_DFI:
|
||||||
{
|
|
||||||
BT_WriteDfi();
|
BT_WriteDfi();
|
||||||
} break;
|
break;
|
||||||
|
|
||||||
case CMD_DATA_REQUEST:
|
case CMD_DATA_REQUEST:
|
||||||
{
|
{
|
||||||
uint8_t reqId = (uint8_t)(g_hc06_frame.w1 & 0xFF);
|
uint8_t reqId = (uint8_t)(g_hc06_frame.w1 & 0xFF);
|
||||||
uint8_t out[HC06_MAX_PAYLOAD];
|
uint8_t out[HC06_MAX_PAYLOAD];
|
||||||
uint16_t outLen = 0;
|
uint16_t outLen = 0;
|
||||||
|
|
||||||
uint8_t stc = HC06_DispatchDataRequest(reqId, out, &outLen);
|
uint8_t stc = HC06_DispatchDataRequest(reqId, out, &outLen);
|
||||||
(void)HC06_SendDataReply(stc, out, outLen);
|
(void)HC06_SendDataReply(stc, out, outLen);
|
||||||
} break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case CMD_READ_DTC:
|
case CMD_READ_DTC:
|
||||||
{
|
|
||||||
BT_ReadDTC();
|
BT_ReadDTC();
|
||||||
} break;
|
break;
|
||||||
|
|
||||||
case CMD_ERASE_DTC:
|
case CMD_ERASE_DTC:
|
||||||
{
|
|
||||||
BT_ClearDTC();
|
BT_ClearDTC();
|
||||||
} break;
|
break;
|
||||||
|
|
||||||
case CMD_READ_AUDI_PIN:
|
case CMD_READ_AUDI_PIN:
|
||||||
{
|
|
||||||
BT_ReadAudiPin();
|
BT_ReadAudiPin();
|
||||||
} break;
|
break;
|
||||||
|
|
||||||
case CMD_WRITE_AUDI_PIN:
|
case CMD_WRITE_AUDI_PIN:
|
||||||
{
|
BT_ReadAudiPin(); /* placeholder — WriteAudiPin not yet implemented */
|
||||||
BT_ReadAudiPin();
|
break;
|
||||||
} break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
|
||||||
HC06_SendAsciiLn("ERR Unknown CMD");
|
HC06_SendAsciiLn("ERR Unknown CMD");
|
||||||
// Return error frame with original cmd in w3
|
|
||||||
HC06_SendFrame(0xFF, 0, 0, (uint16_t)cmd);
|
HC06_SendFrame(0xFF, 0, 0, (uint16_t)cmd);
|
||||||
} break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
commandPending = 0;
|
commandPending = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
Force RX re-arm (UART3)
|
||||||
|
========================= */
|
||||||
static void HC06_ForceRearmRx(void)
|
static void HC06_ForceRearmRx(void)
|
||||||
{
|
{
|
||||||
// Stop anything currently running on UART2
|
|
||||||
HAL_UART_AbortReceive_IT(&huart3);
|
HAL_UART_AbortReceive_IT(&huart3);
|
||||||
HAL_UART_AbortReceive(&huart3);
|
HAL_UART_AbortReceive(&huart3);
|
||||||
HAL_UART_AbortTransmit(&huart3);
|
HAL_UART_AbortTransmit(&huart3);
|
||||||
|
|
||||||
// Reset queued TX as well (avoid sending stale frames after re-arm)
|
|
||||||
HC06_TxReset();
|
HC06_TxReset();
|
||||||
|
|
||||||
// Clear UART error flags properly
|
|
||||||
__HAL_UART_CLEAR_OREFLAG(&huart3);
|
__HAL_UART_CLEAR_OREFLAG(&huart3);
|
||||||
__HAL_UART_CLEAR_NEFLAG(&huart3);
|
__HAL_UART_CLEAR_NEFLAG(&huart3);
|
||||||
__HAL_UART_CLEAR_FEFLAG(&huart3);
|
__HAL_UART_CLEAR_FEFLAG(&huart3);
|
||||||
__HAL_UART_CLEAR_PEFLAG(&huart3);
|
__HAL_UART_CLEAR_PEFLAG(&huart3);
|
||||||
|
|
||||||
// Ensure peripheral interrupt enable bits are set
|
|
||||||
__HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE);
|
__HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE);
|
||||||
__HAL_UART_ENABLE_IT(&huart3, UART_IT_ERR);
|
__HAL_UART_ENABLE_IT(&huart3, UART_IT_ERR);
|
||||||
|
|
||||||
// Also clear any pending interrupt in NVIC (rare but helps)
|
|
||||||
NVIC_ClearPendingIRQ(USART2_IRQn);
|
NVIC_ClearPendingIRQ(USART2_IRQn);
|
||||||
|
|
||||||
// Reset parser/AT state so it doesn't wait on stale state
|
|
||||||
st = ST_SYNC;
|
st = ST_SYNC;
|
||||||
pi = 0;
|
pi = 0;
|
||||||
crc = 0;
|
crc = 0;
|
||||||
AT_Clear();
|
AT_Clear();
|
||||||
|
|
||||||
// Rearm 1-byte RX no matter what mode
|
HAL_UART_Receive_IT(&huart3, &rx_byte, 1);
|
||||||
HAL_StatusTypeDef rst = HAL_UART_Receive_IT(&huart3, &rx_byte, 1);
|
|
||||||
(void)rst;
|
|
||||||
crc = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
Public KLine event callbacks
|
||||||
|
(called from kline_fsm.c via weak symbol override in main.c)
|
||||||
|
========================= */
|
||||||
|
|
||||||
/*
|
void BT_KLINE_ERROR(void)
|
||||||
*
|
{
|
||||||
* API
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
void BT_KLINE_ERROR(){
|
|
||||||
HC06_SendFrame(reply_cmd(CMD_INIT_COMM), CONN_HEAVY, 0, 0);
|
HC06_SendFrame(reply_cmd(CMD_INIT_COMM), CONN_HEAVY, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BT_ReadDfi(){
|
void BT_OnPSG5CommEstablished(void)
|
||||||
if(connectionAlive){
|
{
|
||||||
|
/*
|
||||||
|
* Previously called KeepAlive() here (blocking). The FSM has already
|
||||||
|
* verified the session is alive before firing KLine_OnConnected(), so
|
||||||
|
* we just send the success frame directly.
|
||||||
|
*/
|
||||||
|
HC06_SendFrame(reply_cmd(CMD_INIT_COMM), HC06_STATUS_OK, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BT_SendCommStatus(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* ReadVoltage() is still a synchronous KWP call (not yet ported to FSM).
|
||||||
|
* It is safe to call here only because this is invoked from
|
||||||
|
* KLine_OnKeepAliveTick() which fires from inside KLine_FSM_Run(),
|
||||||
|
* meaning the session is known-good at this instant.
|
||||||
|
* TODO: port to an async FSM command when ReadVoltage is migrated.
|
||||||
|
*/
|
||||||
|
uint16_t volt = 0;
|
||||||
|
if (KLine_FSM_IsConnected()) {
|
||||||
|
uint8_t ok = ReadVoltage(&volt);
|
||||||
|
HC06_SendFrame(reply_cmd(CMD_TOOL_COMM_STATUS),
|
||||||
|
ok ? HC06_STATUS_OK : (uint16_t)KLINE_ERROR,
|
||||||
|
0,
|
||||||
|
volt);
|
||||||
|
} else {
|
||||||
|
HC06_SendFrame(reply_cmd(CMD_TOOL_COMM_STATUS), CONN_DEAD, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
BT command implementations
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
static void BT_InitPSG5Comm(void)
|
||||||
|
{
|
||||||
|
if (KLine_FSM_IsConnected()) {
|
||||||
|
/* Already connected — tell the app */
|
||||||
|
HC06_SendFrame(reply_cmd(CMD_INIT_COMM), CONN_ALIVE, 0, 0);
|
||||||
|
} else if (KLine_FSM_GetStatus() == KLINE_STATUS_CONNECTING) {
|
||||||
|
/* Already trying — ignore duplicate request */
|
||||||
|
} else {
|
||||||
|
/* Kick off the FSM connection sequence */
|
||||||
|
KLine_FSM_RequestConnect(ECU_INIT_ADDRESS);
|
||||||
|
/* Response will come via KLine_OnConnected() / KLine_OnDisconnected() */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BT_ReadDfi(void)
|
||||||
|
{
|
||||||
|
if (KLine_FSM_IsConnected()) {
|
||||||
float dfi = ReadDfi();
|
float dfi = ReadDfi();
|
||||||
int16_t s = HC06_DfiFloatToS16(dfi);
|
int16_t s = HC06_DfiFloatToS16(dfi);
|
||||||
HC06_SendFrame(reply_cmd(CMD_READ_DFI), 0, 0, (uint16_t)(uint16_t)s);
|
HC06_SendFrame(reply_cmd(CMD_READ_DFI), HC06_STATUS_OK, 0, (uint16_t)s);
|
||||||
} else {
|
} else {
|
||||||
HC06_SendFrame(reply_cmd(CMD_READ_DFI), CONN_DEAD, 0, 0);
|
HC06_SendFrame(reply_cmd(CMD_READ_DFI), CONN_DEAD, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BT_InitPSG5Comm(){
|
static void BT_WriteDfi(void)
|
||||||
if(!connectionAlive){
|
{
|
||||||
BitBang = 1;
|
if (KLine_FSM_IsConnected()) {
|
||||||
}else{
|
|
||||||
HC06_SendFrame(reply_cmd(CMD_INIT_COMM), CONN_ALIVE, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BT_WriteDfi(){
|
|
||||||
if(connectionAlive){
|
|
||||||
int16_t s = (int16_t)g_hc06_frame.w3;
|
int16_t s = (int16_t)g_hc06_frame.w3;
|
||||||
float dfi = HC06_DfiS16ToFloat(s);
|
float dfi = HC06_DfiS16ToFloat(s);
|
||||||
|
|
||||||
int res = WriteDfi(dfi, 0);
|
int res = WriteDfi(dfi, 0);
|
||||||
HC06_SendFrame(reply_cmd(CMD_WRITE_DFI), !res * KLINE_ERROR, 0, 0);
|
HC06_SendFrame(reply_cmd(CMD_WRITE_DFI),
|
||||||
|
res ? HC06_STATUS_OK : (uint16_t)KLINE_ERROR,
|
||||||
|
0, 0);
|
||||||
} else {
|
} else {
|
||||||
HC06_SendFrame(reply_cmd(CMD_WRITE_DFI), CONN_DEAD, 0, 0);
|
HC06_SendFrame(reply_cmd(CMD_WRITE_DFI), CONN_DEAD, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BT_OnPSG5CommEstablished(){
|
static void BT_ReadDTC(void)
|
||||||
if(KeepAlive()){
|
{
|
||||||
HC06_SendFrame(reply_cmd(CMD_INIT_COMM), 0, 0, 0);
|
if (KLine_FSM_IsConnected()) {
|
||||||
|
/*
|
||||||
|
* Snapshot the FSM's fault code cache (populated during connection).
|
||||||
|
* ReadFaultCodes() is a blocking call — preserved here for now,
|
||||||
|
* to be migrated to an async FSM command in a future step.
|
||||||
|
*/
|
||||||
|
const KLine_FaultCode *fc = KLine_FSM_GetFaultCodes(&s_fault_count);
|
||||||
|
if (fc && s_fault_count > 0)
|
||||||
|
memcpy(s_fault_cache, fc,
|
||||||
|
(size_t)s_fault_count * sizeof(KLine_FaultCode));
|
||||||
|
int ok = (s_fault_count >= 0) ? 1 : 0;
|
||||||
|
HC06_SendFrame(reply_cmd(CMD_READ_DTC),
|
||||||
|
ok ? HC06_STATUS_OK : (uint16_t)KLINE_ERROR,
|
||||||
|
0,
|
||||||
|
(uint16_t)s_fault_count);
|
||||||
} else {
|
} else {
|
||||||
HC06_SendFrame(reply_cmd(CMD_INIT_COMM), CONN_HEAVY, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BT_ReadDTC(){
|
|
||||||
if(connectionAlive){
|
|
||||||
N_fc = ReadFaultCodes(fault_codes, 16);
|
|
||||||
int res = (N_fc == -1) ? 0 : 1;
|
|
||||||
HC06_SendFrame(reply_cmd(CMD_READ_DTC), !res * KLINE_ERROR, 0, (uint16_t)N_fc);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
HC06_SendFrame(reply_cmd(CMD_READ_DTC), CONN_DEAD, 0, 0);
|
HC06_SendFrame(reply_cmd(CMD_READ_DTC), CONN_DEAD, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BT_ClearDTC(){
|
static void BT_ClearDTC(void)
|
||||||
if(connectionAlive){
|
{
|
||||||
int res = ClearFaultCodes();
|
if (KLine_FSM_IsConnected()) {
|
||||||
HC06_SendFrame(reply_cmd(CMD_ERASE_DTC), !res * KLINE_ERROR, 0, 0);
|
int res = ClearFaultCodes(); /* still blocking — to be ported */
|
||||||
}
|
HC06_SendFrame(reply_cmd(CMD_ERASE_DTC),
|
||||||
else{
|
res ? HC06_STATUS_OK : (uint16_t)KLINE_ERROR,
|
||||||
|
0, 0);
|
||||||
|
} else {
|
||||||
HC06_SendFrame(reply_cmd(CMD_ERASE_DTC), CONN_DEAD, 0, 0);
|
HC06_SendFrame(reply_cmd(CMD_ERASE_DTC), CONN_DEAD, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void BT_ReadAudiPin(){
|
|
||||||
uint16_t pin = 0;
|
|
||||||
if(connectionAlive){
|
|
||||||
HC06_SendFrame(reply_cmd(CMD_READ_AUDI_PIN), !ReadAudiPin(&pin) * KLINE_ERROR, 0, (uint16_t)pin);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
HC06_SendFrame(reply_cmd(CMD_READ_AUDI_PIN), CONN_DEAD, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void BT_WriteAudiPin(){
|
|
||||||
/*if(connectionAlive){
|
|
||||||
uint16_t pin = (uint16_t)g_hc06_frame.w3;
|
|
||||||
HC06_SendFrame(reply_cmd(CMD_READ_AUDI_PIN), !WriteAudiPin(pin) * KLINE_ERROR, 0, 0);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
HC06_SendFrame(reply_cmd(CMD_READ_AUDI_PIN), CONN_DEAD, 0, 0);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void BT_SendCommStatus(){
|
static void BT_ReadAudiPin(void)
|
||||||
uint16_t volt = 0;
|
{
|
||||||
if(connectionAlive){
|
uint16_t pin = 0;
|
||||||
HC06_SendFrame(reply_cmd(CMD_TOOL_COMM_STATUS), !ReadVoltage(&volt) * KLINE_ERROR, 0, (uint16_t)volt);
|
if (KLine_FSM_IsConnected()) {
|
||||||
}
|
uint8_t ok = ReadAudiPin(&pin);
|
||||||
else{
|
HC06_SendFrame(reply_cmd(CMD_READ_AUDI_PIN),
|
||||||
HC06_SendFrame(reply_cmd(CMD_TOOL_COMM_STATUS), CONN_DEAD, 0, 0);
|
ok ? HC06_STATUS_OK : (uint16_t)KLINE_ERROR,
|
||||||
|
0,
|
||||||
|
pin);
|
||||||
|
} else {
|
||||||
|
HC06_SendFrame(reply_cmd(CMD_READ_AUDI_PIN), CONN_DEAD, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,296 +0,0 @@
|
|||||||
/*
|
|
||||||
* IKW1281Connection.c
|
|
||||||
*
|
|
||||||
* Created on: Apr 9, 2025
|
|
||||||
* Author: herli
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "IKW1281Connection.h"
|
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
|
|
||||||
static void SendAckPacket(void);
|
|
||||||
|
|
||||||
static void WriteComplement(uint8_t b);
|
|
||||||
static void WriteByteAndDiscardEcho(uint8_t b);
|
|
||||||
static void WriteByteAndReadAck(uint8_t b);
|
|
||||||
static void WriteByteRaw(uint8_t b);
|
|
||||||
|
|
||||||
uint8_t timeoutdata[] = "TimeoutNigga\r\n";
|
|
||||||
//uint8_t RxData[1];
|
|
||||||
uint8_t TxData[KLINE_BUFFER_SIZE];
|
|
||||||
uint8_t KlineData[KLINE_BUFFER_SIZE];
|
|
||||||
extern volatile uint8_t rx_done_flag;
|
|
||||||
uint8_t connectionAlive = 0;
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t ReadByte(void)
|
|
||||||
{
|
|
||||||
if (!connectionAlive) return 0;
|
|
||||||
|
|
||||||
rx_done_flag = 0;
|
|
||||||
|
|
||||||
// Re-arm UART RX interrupt for next byte
|
|
||||||
HAL_UART_Receive_IT(&huart1, (uint8_t*)&k_rx_byte, 1);
|
|
||||||
|
|
||||||
uint32_t timeout = HAL_GetTick() + 1000;
|
|
||||||
while (!rx_done_flag)
|
|
||||||
{
|
|
||||||
if ((int32_t)(HAL_GetTick() - timeout) >= 0)
|
|
||||||
{
|
|
||||||
HAL_UART_AbortReceive(&huart1); // cancel the pending IT
|
|
||||||
connectionAlive = 0;
|
|
||||||
return 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safely capture the byte (atomic copy)
|
|
||||||
uint8_t b = k_rx_byte;
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t ReadAndAckByte(void){
|
|
||||||
uint8_t b = ReadByte();
|
|
||||||
WriteComplement(b);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
void ReadComplement(uint8_t b){
|
|
||||||
uint8_t expectedComplement = (uint8_t)~b;
|
|
||||||
uint8_t actualComplement = ReadByte();
|
|
||||||
//if(actualComplement != expectedComplement){return;}
|
|
||||||
}
|
|
||||||
void WriteComplement(uint8_t b){
|
|
||||||
uint8_t complement = (uint8_t)~b;
|
|
||||||
WriteByteAndDiscardEcho(complement);
|
|
||||||
}
|
|
||||||
void WriteByteAndDiscardEcho(uint8_t b){
|
|
||||||
WriteByteRaw(b);
|
|
||||||
uint8_t echo = ReadByte();
|
|
||||||
//if(echo != b){return;}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteByteRaw(uint8_t b){
|
|
||||||
TxData[0] = b;
|
|
||||||
HAL_UART_Transmit(&huart1, TxData, 1, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAX_PACKETS 16 // adjust to your needs
|
|
||||||
static ParsedPacket packets_buffer[MAX_PACKETS];
|
|
||||||
|
|
||||||
|
|
||||||
ParsedPacket* ReceivePackets(int *out_count)
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
if (count >= MAX_PACKETS) {
|
|
||||||
// reached max, stop receiving more
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ParsedPacket packet = ReceivePacket();
|
|
||||||
packets_buffer[count++] = packet;
|
|
||||||
|
|
||||||
if (packet.isAckNak || !connectionAlive) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SendAckPacket();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out_count) {
|
|
||||||
*out_count = count;
|
|
||||||
}
|
|
||||||
return packets_buffer; // returns pointer to static buffer
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
ParsedPacket* ReceivePackets(int *out_count) {
|
|
||||||
int capacity = 16; // initial capacity
|
|
||||||
int count = 0;
|
|
||||||
ParsedPacket *packets = malloc(capacity * sizeof(ParsedPacket));
|
|
||||||
if (!packets) return NULL;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
ParsedPacket packet = ReceivePacket();
|
|
||||||
|
|
||||||
// Resize array if needed
|
|
||||||
if (count >= capacity) {
|
|
||||||
capacity *= 2;
|
|
||||||
ParsedPacket *new_packets = realloc(packets, capacity * sizeof(ParsedPacket));
|
|
||||||
if (!new_packets) {
|
|
||||||
free(packets);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
packets = new_packets;
|
|
||||||
}
|
|
||||||
|
|
||||||
packets[count++] = packet;
|
|
||||||
|
|
||||||
if (packet.isAckNak) break;
|
|
||||||
|
|
||||||
SendAckPacket();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out_count) *out_count = count;
|
|
||||||
return packets;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
uint8_t _packetCounter = 0;
|
|
||||||
uint8_t _packetCounterInitialized = 0;
|
|
||||||
|
|
||||||
ParsedPacket ReceivePacket()
|
|
||||||
{
|
|
||||||
ParsedPacket packet = {0};
|
|
||||||
if (!connectionAlive) return packet; // add this
|
|
||||||
|
|
||||||
uint8_t index = 0;
|
|
||||||
|
|
||||||
uint8_t packetLength = ReadAndAckByte();
|
|
||||||
//packet.raw = (uint8_t*)malloc(packetLength*sizeof(uint8_t));
|
|
||||||
|
|
||||||
packet.raw[index++] = packetLength;
|
|
||||||
|
|
||||||
uint8_t packetCounter = ReadPacketCounter();
|
|
||||||
packet.raw[index++] = packetCounter;
|
|
||||||
|
|
||||||
uint8_t packetCommand = ReadAndAckByte();
|
|
||||||
packet.raw[index++] = packetCommand;
|
|
||||||
|
|
||||||
for (int i = 0; i < packetLength - 3; i++) {
|
|
||||||
uint8_t b = ReadAndAckByte();
|
|
||||||
packet.raw[index++] = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t packetEnd = ReadByte(); // no ACK
|
|
||||||
packet.raw[index++] = packetEnd;
|
|
||||||
packet.length = index; //tiene que ser index
|
|
||||||
|
|
||||||
if (packetEnd != 0x03) {
|
|
||||||
// Protocol error handling here
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now classify the packet type
|
|
||||||
packet.title = packetCommand;
|
|
||||||
|
|
||||||
switch (packetCommand) {
|
|
||||||
case PACKET_CMD_ACK:
|
|
||||||
packet.type = PACKET_TYPE_ACK;
|
|
||||||
packet.isAckNak = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PACKET_CMD_AsciiData:
|
|
||||||
if (packet.raw[3] == 0x00)
|
|
||||||
packet.type = PACKET_TYPE_CODING_WSC;
|
|
||||||
else
|
|
||||||
packet.type = 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;
|
|
||||||
|
|
||||||
case PACKET_CMD_NAK:
|
|
||||||
packet.type = PACKET_TYPE_NAK;
|
|
||||||
packet.isAckNak = 1;
|
|
||||||
default:
|
|
||||||
packet.type = PACKET_TYPE_UNKNOWN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
void ResetPacketCounter(){
|
|
||||||
_packetCounter = 0;
|
|
||||||
}
|
|
||||||
uint8_t ReadPacketCounter()
|
|
||||||
{
|
|
||||||
uint8_t packetCounter = ReadAndAckByte();
|
|
||||||
|
|
||||||
if (!_packetCounterInitialized)
|
|
||||||
{
|
|
||||||
_packetCounter = packetCounter;
|
|
||||||
_packetCounterInitialized = 1;
|
|
||||||
}
|
|
||||||
else if (packetCounter != _packetCounter)
|
|
||||||
{
|
|
||||||
// Handle mismatch (drop packet, reset, etc.)
|
|
||||||
}
|
|
||||||
|
|
||||||
_packetCounter++;
|
|
||||||
return packetCounter;
|
|
||||||
}
|
|
||||||
void SendAckPacket()
|
|
||||||
{
|
|
||||||
uint8_t ackByte = (uint8_t)PACKET_CMD_ACK;
|
|
||||||
SendPacket(&ackByte, 1);
|
|
||||||
}
|
|
||||||
void SendPacket(uint8_t* payload, uint8_t length)
|
|
||||||
{
|
|
||||||
uint8_t packetLength = length + 2; // +2 for length and counter
|
|
||||||
uint8_t packet[packetLength];
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
packet[index++] = packetLength; //0
|
|
||||||
packet[index++] = _packetCounter++; //1
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
packet[index++] = payload[i];
|
|
||||||
}
|
|
||||||
for (int i = 0; i < index; i++)
|
|
||||||
{
|
|
||||||
WriteByteAndReadAck(packet[i]);
|
|
||||||
HAL_Delay(5); // Small delay between bytes
|
|
||||||
}
|
|
||||||
WriteByteRaw(PACKET_END_EXPECTED); // End byte, no ACK expected
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteByteAndReadAck(uint8_t b)
|
|
||||||
{
|
|
||||||
WriteByteRaw(b);
|
|
||||||
uint8_t ack = ReadByte();
|
|
||||||
|
|
||||||
HAL_UART_AbortReceive(&huart1);
|
|
||||||
|
|
||||||
uint8_t expectedAck = (uint8_t)~b;
|
|
||||||
if (ack != expectedAck)
|
|
||||||
{
|
|
||||||
// Handle NAK or retry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Placeholder for platform-specific read functions
|
|
||||||
|
|
||||||
uint8_t KeepAlive() {
|
|
||||||
SendAckPacket();
|
|
||||||
ParsedPacket packet = ReceivePacket();
|
|
||||||
if(packet.type != PACKET_TYPE_ACK){
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
void EndCommunication(){
|
|
||||||
uint8_t endPacket = (uint8_t)PACKET_CMD_End;
|
|
||||||
SendPacket((uint8_t*)endPacket, 1);
|
|
||||||
ResetPacketCounter();
|
|
||||||
}
|
|
||||||
|
|
||||||
ParsedPacket* SendCustom(const uint8_t* data, int len, int *out_count) {
|
|
||||||
SendPacket((uint8_t*)data, len); //antes &
|
|
||||||
int count = 0;
|
|
||||||
ParsedPacket* packets = ReceivePackets(&count);
|
|
||||||
*out_count = count;
|
|
||||||
return packets;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -11,39 +11,34 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "stdint.h"
|
#include "stdint.h"
|
||||||
|
|
||||||
extern volatile uint8_t k_rx_byte;
|
#define ECU_INIT_ADDRESS 0xF1
|
||||||
extern volatile uint8_t rx_done_flag;
|
#define KLINE_GPIO_PORT GPIOB
|
||||||
extern uint8_t connectionAlive;
|
#define KLINE_PIN GPIO_PIN_14
|
||||||
|
|
||||||
#define KLINE_BUFFER_SIZE 20
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Packet framing */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
#define PACKET_END_EXPECTED 0x03
|
#define PACKET_END_EXPECTED 0x03
|
||||||
|
#define MAX_PACKET_SIZE 16
|
||||||
|
#define EEPROM_RESPONSE_BODY_MAX 64
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* ECU info struct */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
//typedef uint8_t PacketCommand;
|
|
||||||
#define MAX_PACKET_SIZE 17 // if too big it will crash 128<max<256 //funcionando en 256
|
|
||||||
//si es menor, no funciona????
|
|
||||||
#define EEPROM_RESPONSE_BODY_MAX 64 // Adjust as needed
|
|
||||||
|
|
||||||
#define MAX_CONTROLLER_PACKETS 8
|
|
||||||
#define MAX_PACKET_BODY_LENGTH 64
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char client_ident[13]; // 12 + null
|
char client_ident[13]; /* 12 chars + null */
|
||||||
char unk_ident1[11];
|
char unk_ident1[11];
|
||||||
char soft_info[11];
|
char soft_info[11];
|
||||||
char unk_ident2[11];
|
char unk_ident2[11];
|
||||||
char unk_ident3[11];
|
char unk_ident3[11];
|
||||||
} ControllerInfo;
|
} ControllerInfo;
|
||||||
|
|
||||||
typedef struct {
|
/* ------------------------------------------------------------------ */
|
||||||
uint8_t data[EEPROM_RESPONSE_BODY_MAX];
|
/* Packet commands */
|
||||||
uint8_t length;
|
/* ------------------------------------------------------------------ */
|
||||||
uint8_t valid;
|
|
||||||
} EepromResult;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PACKET_CMD_ReadIdent = 0x00,
|
PACKET_CMD_ReadIdent = 0x00,
|
||||||
@@ -66,9 +61,13 @@ typedef enum {
|
|||||||
PACKET_CMD_AsciiData = 0xF6,
|
PACKET_CMD_AsciiData = 0xF6,
|
||||||
PACKET_CMD_WriteEepromResponse = 0xF9,
|
PACKET_CMD_WriteEepromResponse = 0xF9,
|
||||||
PACKET_CMD_FaultCodesResponse = 0xFC,
|
PACKET_CMD_FaultCodesResponse = 0xFC,
|
||||||
PACKET_CMD_ReadRomEepromResponse = 0xFD
|
PACKET_CMD_ReadRomEepromResponse = 0xFD,
|
||||||
} PacketCommand;
|
} PacketCommand;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Packet types */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PACKET_TYPE_UNKNOWN = 0,
|
PACKET_TYPE_UNKNOWN = 0,
|
||||||
PACKET_TYPE_ACK,
|
PACKET_TYPE_ACK,
|
||||||
@@ -76,28 +75,19 @@ typedef enum {
|
|||||||
PACKET_TYPE_ASCII_DATA,
|
PACKET_TYPE_ASCII_DATA,
|
||||||
PACKET_TYPE_CODING_WSC,
|
PACKET_TYPE_CODING_WSC,
|
||||||
PACKET_TYPE_READ_EEPROM_RESPONSE,
|
PACKET_TYPE_READ_EEPROM_RESPONSE,
|
||||||
PACKET_TYPE_READ_ROM_EEPROM_RESPONSE
|
PACKET_TYPE_READ_ROM_EEPROM_RESPONSE,
|
||||||
} PacketType;
|
} PacketType;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Parsed packet struct */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
||||||
PacketType type;
|
PacketType type;
|
||||||
uint8_t title;
|
uint8_t title; /* raw command byte */
|
||||||
uint8_t length;
|
uint8_t length; /* total bytes in raw[] */
|
||||||
uint8_t raw[MAX_PACKET_SIZE]; // raw bytes
|
uint8_t raw[MAX_PACKET_SIZE];
|
||||||
uint8_t isAckNak;
|
uint8_t isAckNak;
|
||||||
|
|
||||||
} ParsedPacket;
|
} ParsedPacket;
|
||||||
|
|
||||||
extern void ResetPacketCounter(void);
|
|
||||||
extern uint8_t ReadPacketCounter(void);
|
|
||||||
extern ParsedPacket ReceivePacket(void);
|
|
||||||
extern uint8_t ReadByte();
|
|
||||||
extern uint8_t ReadAndAckByte(void);
|
|
||||||
extern uint8_t KeepAlive(void);
|
|
||||||
|
|
||||||
void SendPacket(uint8_t* payload, uint8_t length);
|
|
||||||
ParsedPacket* SendCustom(const uint8_t* data, int len, int *out_count);
|
|
||||||
ParsedPacket* ReceivePackets(int *out_count);
|
|
||||||
|
|
||||||
#endif /* INC_IKW1281CONNECTION_H_ */
|
#endif /* INC_IKW1281CONNECTION_H_ */
|
||||||
|
|||||||
@@ -1,783 +0,0 @@
|
|||||||
/*
|
|
||||||
* kline.c
|
|
||||||
*
|
|
||||||
* Created on: Aug 18, 2025
|
|
||||||
* Author: herli
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "kline.h"
|
|
||||||
#include "IKW1281Connection.h"
|
|
||||||
#include "hc_06.h"
|
|
||||||
|
|
||||||
#include "stdint.h"
|
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
uint8_t BitBang = 0;
|
|
||||||
|
|
||||||
void KLine_Send5Baud(uint8_t data)
|
|
||||||
{
|
|
||||||
HAL_UART_DeInit(&huart1);
|
|
||||||
|
|
||||||
// Reconfigure TX pin as output
|
|
||||||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
|
||||||
GPIO_InitStruct.Pin = KLINE_PIN;
|
|
||||||
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
|
|
||||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
||||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
|
||||||
HAL_GPIO_Init(KLINE_GPIO_PORT, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
// 5 baud = 200ms per bit
|
|
||||||
const uint32_t bit_delay_ms = 200;
|
|
||||||
|
|
||||||
// Start bit (low)
|
|
||||||
HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_RESET);
|
|
||||||
HAL_Delay(bit_delay_ms);
|
|
||||||
|
|
||||||
// Send 8 bits, LSB first
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
if (data & (1 << i)) {
|
|
||||||
HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_SET);
|
|
||||||
} else {
|
|
||||||
HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_RESET);
|
|
||||||
}
|
|
||||||
HAL_Delay(bit_delay_ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop bit (high)
|
|
||||||
HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_SET);
|
|
||||||
HAL_Delay(bit_delay_ms);
|
|
||||||
|
|
||||||
HAL_UART_Init(&huart1); // ensures TX idles high, necessary for g431
|
|
||||||
//HAL_UART_Receive_IT(&huart1, &k_rx_byte, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int WakeUp(uint8_t controllerAddress, uint8_t evenParity){
|
|
||||||
KLine_Send5Baud(controllerAddress); // ECU address
|
|
||||||
connectionAlive = 1;
|
|
||||||
uint8_t syncByte = ReadByte();
|
|
||||||
if(syncByte != 0x55){return 0;}
|
|
||||||
uint8_t keywordLsb = ReadByte();
|
|
||||||
uint8_t keywordMsb = ReadAndAckByte();
|
|
||||||
|
|
||||||
int protocolVersion = ((keywordMsb & 0x7F) << 7) + (keywordLsb & 0x7F);
|
|
||||||
return protocolVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*void PrintAsciiPayload(const ParsedPacket* packet, UART_HandleTypeDef* huart)
|
|
||||||
{
|
|
||||||
char data[128]; // adjust size as needed
|
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
// Start with a label
|
|
||||||
len += sprintf(&data[len], "ASCII: ");
|
|
||||||
|
|
||||||
for (int i = 4; i < packet->length - 1; i++) {
|
|
||||||
char c = packet->raw[i] & 0x7F;
|
|
||||||
if (len < sizeof(data) - 2) {
|
|
||||||
data[len++] = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Null-terminate and add newline
|
|
||||||
data[len++] = '\r\n';
|
|
||||||
data[len] = '\0';
|
|
||||||
|
|
||||||
HAL_UART_Transmit(huart, (uint8_t*)data, len, 1000);
|
|
||||||
|
|
||||||
// Optional: report more data
|
|
||||||
if (packet->raw[3] > 0x7F) {
|
|
||||||
const char more[] = "More data available via ReadIdent\r\n";
|
|
||||||
HAL_UART_Transmit(huart, (uint8_t*)more, sizeof(more) - 1, 1000);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
int FilterNonAckNak(ParsedPacket* all, int total, ParsedPacket* filtered, int max)
|
|
||||||
{
|
|
||||||
int j = 0;
|
|
||||||
for (int i = 0; i < total && j < max; i++) {
|
|
||||||
if (!all[i].isAckNak) {
|
|
||||||
filtered[j++] = all[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*void PrintEcuInfo(ControllerInfo* info) {
|
|
||||||
char data[64];
|
|
||||||
|
|
||||||
sprintf(data, "client_ident: %s\t\r\n", info->client_ident);
|
|
||||||
//HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000);
|
|
||||||
CDC_Transmit_FS((uint8_t*)data, strlen(data));
|
|
||||||
HAL_Delay(10); // allow USB stack to flush
|
|
||||||
|
|
||||||
sprintf(data, "unk_ident1: %s\t\r\n", info->unk_ident1);
|
|
||||||
//HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000);
|
|
||||||
CDC_Transmit_FS((uint8_t*)data, strlen(data));
|
|
||||||
HAL_Delay(10); // allow USB stack to flush
|
|
||||||
|
|
||||||
sprintf(data, "soft_info: %s\t\r\n", info->soft_info);
|
|
||||||
//HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000);
|
|
||||||
CDC_Transmit_FS((uint8_t*)data, strlen(data));
|
|
||||||
HAL_Delay(10); // allow USB stack to flush
|
|
||||||
|
|
||||||
if (strlen(info->unk_ident2) > 0) {
|
|
||||||
sprintf(data, "unk_ident2: %s\t\r\n", info->unk_ident2);
|
|
||||||
//HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000);
|
|
||||||
CDC_Transmit_FS((uint8_t*)data, strlen(data));
|
|
||||||
HAL_Delay(10); // allow USB stack to flush
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen(info->unk_ident3) > 0) {
|
|
||||||
sprintf(data, "unk_ident3: %s\t\r\n", info->unk_ident3);
|
|
||||||
CDC_Transmit_FS((uint8_t*)data, strlen(data));
|
|
||||||
HAL_Delay(10); // allow USB stack to flush
|
|
||||||
|
|
||||||
// HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
#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);
|
|
||||||
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
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
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) {
|
|
||||||
freeParsedPackets(packets, packetCount);
|
|
||||||
*outLength = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter to exclude Ack/Nak
|
|
||||||
ParsedPacket* useful = malloc(packetCount * sizeof(ParsedPacket));
|
|
||||||
int usefulCount = 0;
|
|
||||||
for (int i = 0; i < packetCount; i++) {
|
|
||||||
if (!packets[i].isAckNak) {
|
|
||||||
useful[usefulCount++] = packets[i];
|
|
||||||
} else {
|
|
||||||
// If dynamically allocated, free individual packet.raw
|
|
||||||
free(packets[i].raw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(packets); // Free the original array pointer (but not the reused raw data)
|
|
||||||
|
|
||||||
if (usefulCount != 1) {
|
|
||||||
freeParsedPackets(useful, usefulCount);
|
|
||||||
*outLength = 0;
|
|
||||||
return NULL; // Or handle as error
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* result = malloc(useful[0].length - 4); // Remove 3-byte header + end (or however yours works)
|
|
||||||
if (!result) {
|
|
||||||
freeParsedPackets(useful, usefulCount);
|
|
||||||
*outLength = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(result, useful[0].raw + 3, useful[0].length - 4); // Adjust if header is different
|
|
||||||
*outLength = useful[0].length - 4;
|
|
||||||
|
|
||||||
freeParsedPackets(useful, usefulCount);
|
|
||||||
return result;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
void ReadSerialNumber(char* serialOut, int maxLen) {
|
|
||||||
uint8_t request[] = { 0x19, 0x06, 0x00, 0x80 };
|
|
||||||
|
|
||||||
int packetCount = 0;
|
|
||||||
ParsedPacket* packets = SendCustom(request, sizeof(request), &packetCount);
|
|
||||||
|
|
||||||
if (!packets || packetCount == 0) {
|
|
||||||
serialOut[0] = '\0';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < packetCount; i++) {
|
|
||||||
ParsedPacket* p = &packets[i];
|
|
||||||
if (!p->isAckNak && p->type == PACKET_TYPE_READ_EEPROM_RESPONSE) {
|
|
||||||
int dataLen = p->length - 4; // Skip length, counter, command, end byte
|
|
||||||
if (dataLen > maxLen - 1) {
|
|
||||||
dataLen = maxLen - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(serialOut, p->raw + 3, dataLen); // Adjust if needed
|
|
||||||
serialOut[dataLen] = '\0';
|
|
||||||
|
|
||||||
//freeParsedPackets(packets, packetCount);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
serialOut[0] = '\0';
|
|
||||||
//freeParsedPackets(packets, packetCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*void freeParsedPackets(ParsedPacket* packets, int count) {
|
|
||||||
if (!packets) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
if (packets[i].raw != NULL) {
|
|
||||||
//free(packets[i].raw);
|
|
||||||
//packets[i].raw = NULL; // Avoid double free
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//free(packets); // Free the packet array itself
|
|
||||||
}*/
|
|
||||||
|
|
||||||
uint16_t ExtractEepromAddress(ParsedPacket *packets, int packet_count) {
|
|
||||||
for (int i = 0; i < packet_count; i++) {
|
|
||||||
ParsedPacket *p = &packets[i];
|
|
||||||
if (!p->isAckNak && p->length > 4) {
|
|
||||||
uint32_t address = (p->raw[3+1] << 8) | p->raw[3];
|
|
||||||
return address - 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
float ReadDfi(){
|
|
||||||
float Dfi = 0.0;
|
|
||||||
if(!KeepAlive()){
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(0);
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
//uint8_t request[] = { 0x18, 0x00, 0x03, 0xFF, 0xFF }; //TODO ERROR ENVIA AC 7E 00 20 00
|
|
||||||
int packet_count = 0;
|
|
||||||
//ParsedPacket* packets = SendCustom(request, 5, &packet_count, 1); //this changes with pump version but for now no exceptions
|
|
||||||
ParsedPacket* packets = SendCustom((uint8_t[]){ 0x18, 0x00, 0x03, 0xFF, 0xFF }, 5, &packet_count);
|
|
||||||
|
|
||||||
//Dfi = 1.0;
|
|
||||||
if(!KeepAlive()){
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(0);
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t request2[] = { 0x19, 0x02, 0x00, 0x44};
|
|
||||||
packets = SendCustom(request2, 4, &packet_count); //this changes with pump version but for now no exceptions
|
|
||||||
|
|
||||||
|
|
||||||
// Filter to exclude Ack/Nak
|
|
||||||
for (int i = 0; i < packet_count; i++) {
|
|
||||||
if (!packets[i].isAckNak) {
|
|
||||||
Dfi = ((int8_t)packets[i].raw[3] * 3.0) / 256.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//free(packets); // Free the original array pointer (but not the reused raw data)
|
|
||||||
/*if(Dfi != 0.0){
|
|
||||||
|
|
||||||
}*/
|
|
||||||
return Dfi;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write DFI parameter (EEPROM 0x44) after password unlock.
|
|
||||||
*
|
|
||||||
* version mapping (as in your C#):
|
|
||||||
* default: {18 00 03 2F FF ...}
|
|
||||||
* version==1: {18 00 03 2F F2 ...}
|
|
||||||
* version==2 or 3: {18 00 03 FF F2 ...}
|
|
||||||
*
|
|
||||||
* Returns 1 on success, 0 on failure (non-alive).
|
|
||||||
*/
|
|
||||||
int WriteDfi(float dfi, int version)
|
|
||||||
{
|
|
||||||
if (!KeepAlive()) {
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Password packets (14 bytes)
|
|
||||||
static const uint8_t pass_default[14] = {
|
|
||||||
0x18, 0x00, 0x03, 0x2F, 0xFF, 0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45
|
|
||||||
}; // V1 KHTCA8G0E (per your comment)
|
|
||||||
static const uint8_t pass_v2[14] = {
|
|
||||||
0x18, 0x00, 0x03, 0x2F, 0xF2, 0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45
|
|
||||||
}; // V2 KHTCA8G0E
|
|
||||||
static const uint8_t pass_v3v4[14] = {
|
|
||||||
0x18, 0x00, 0x03, 0xFF, 0xF2, 0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45
|
|
||||||
}; // V3, V4
|
|
||||||
|
|
||||||
const uint8_t *password_packet = pass_default;
|
|
||||||
if (version == 1) {
|
|
||||||
password_packet = pass_v2;
|
|
||||||
} else if (version == 2 || version == 3) {
|
|
||||||
password_packet = pass_v3v4;
|
|
||||||
}
|
|
||||||
|
|
||||||
int packet_count = 0;
|
|
||||||
(void)SendCustom(password_packet, 14, &packet_count); // throwOnNak=1 (strict)
|
|
||||||
|
|
||||||
if (!KeepAlive()) {
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// C#:
|
|
||||||
// sbyte value = (sbyte)((dfi * 256.0) / 3);
|
|
||||||
// if (value == 0) value = 1;
|
|
||||||
//
|
|
||||||
// NOTE: with dfi up to +1.5, math gives 128.0. That does not fit in int8_t.
|
|
||||||
// We clamp to [-128..127] to avoid wrap to -128.
|
|
||||||
int vi = (int)((dfi * 256.0f) / 3.0f); // trunc toward 0 (matches typical C# unchecked cast behavior for in-range)
|
|
||||||
vi = vi > 127 ? 127:vi;
|
|
||||||
vi = vi < -128 ? -128:vi;
|
|
||||||
//vi = clamp_int(vi, -128, 127);
|
|
||||||
int8_t value = (int8_t)vi;
|
|
||||||
if (value == 0) value = 1;
|
|
||||||
|
|
||||||
// byte value_csum = (byte)(0 - (byte)value);
|
|
||||||
uint8_t value_u = (uint8_t)value;
|
|
||||||
uint8_t value_csum = (uint8_t)(0u - value_u);
|
|
||||||
|
|
||||||
// Write command
|
|
||||||
uint8_t req[7] = { 0x1A, 0x02, 0x00, 0x44, value_u, value_csum, 0x03 };
|
|
||||||
(void)SendCustom(req, 7, &packet_count); // throwOnNak=1 (strict)
|
|
||||||
|
|
||||||
if (!KeepAlive()) {
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
void ReadCustomerChangeIndex(){
|
|
||||||
if(!KeepAlive()){
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint8_t request[] = { 0x18, 0x00, 0x00, 0x82, 0x33 };
|
|
||||||
|
|
||||||
int packet_count = 0;
|
|
||||||
ParsedPacket* packets = SendCustom(request, 5, &packet_count); //this changes with pump version but for now no exceptions
|
|
||||||
|
|
||||||
//char message[40];
|
|
||||||
//sprintf(message, "Reading customer change index\r\n");
|
|
||||||
//HAL_UART_Transmit(&huart2, (uint8_t*)message, strlen(message), 1000);
|
|
||||||
|
|
||||||
if(!KeepAlive()){
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(12);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t cust_change_address = 0;
|
|
||||||
|
|
||||||
int data_length = 0;
|
|
||||||
|
|
||||||
uint8_t* test_data = ReadRomEeprom(0x9FFE, 2, &data_length);
|
|
||||||
if(data_length > 1){
|
|
||||||
cust_change_address = (uint16_t)((test_data[1] << 8) | test_data[0]);
|
|
||||||
cust_change_address += 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
test_data = ReadRomEeprom(cust_change_address, 6, &data_length);
|
|
||||||
|
|
||||||
char cust_change_index[12];
|
|
||||||
|
|
||||||
if (test_data && data_length > 0) {
|
|
||||||
for (int i = 0; i < data_length; i++) {
|
|
||||||
cust_change_index[i] = (char)test_data[i]; // Convert byte to char
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//sprintf(message, "Mod. Index : %s\r\n", cust_change_index);
|
|
||||||
//CDC_Transmit_FS((uint8_t*)message, strlen(message));
|
|
||||||
/*char cust_change_index[12];
|
|
||||||
|
|
||||||
if (test_data && data_length > 0) {
|
|
||||||
int copy_len = (data_length < (sizeof(cust_change_index) - 1))
|
|
||||||
? data_length
|
|
||||||
: (sizeof(cust_change_index) - 1);
|
|
||||||
|
|
||||||
for (int i = 0; i < copy_len; i++) {
|
|
||||||
cust_change_index[i] = (char)test_data[i];
|
|
||||||
}
|
|
||||||
cust_change_index[copy_len] = '\0'; // Null-terminate
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(message, "Mod. Index : %s\r\n", cust_change_index);
|
|
||||||
CDC_Transmit_FS((uint8_t*)message, strlen(message));*/
|
|
||||||
//HAL_UART_Transmit(&huart2, (uint8_t*)cust_change_index, strlen(cust_change_index), 1000);
|
|
||||||
}
|
|
||||||
char identStr[11]; //11+1
|
|
||||||
|
|
||||||
void IdentifyEcu() {
|
|
||||||
//char outputStr[35];
|
|
||||||
//float dFi = ReadDfi();
|
|
||||||
//sprintf(outputStr, "dFi: %d.%02d\t\t\r\n", (int)dFi, (int)((dFi - (int)dFi) * 100));
|
|
||||||
|
|
||||||
//CDC_Transmit_FS((uint8_t*)outputStr, strlen(outputStr));
|
|
||||||
|
|
||||||
ReadCustomerChangeIndex();//Necessary
|
|
||||||
|
|
||||||
if(!KeepAlive()){
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int packet_count = 0;
|
|
||||||
ParsedPacket* packets = SendCustom((uint8_t[]){ 0x01, 0x02, 0x00, 0xC6 }, 4, &packet_count);
|
|
||||||
|
|
||||||
//uint16_t address = ExtractEepromAddress(packets, count);
|
|
||||||
|
|
||||||
uint16_t address = ExtractEepromAddress(packets, packet_count);
|
|
||||||
|
|
||||||
if(!KeepAlive()){
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address != 0) {
|
|
||||||
int data_length = 0;
|
|
||||||
uint8_t* test_data = ReadRomEeprom(address, 10, &data_length);
|
|
||||||
|
|
||||||
for (int i = 0; i < data_length && i < sizeof(identStr) - 1; i++) {
|
|
||||||
identStr[i] = (char)test_data[i];
|
|
||||||
} //identStr[10] = '\0';
|
|
||||||
//HAL_UART_Transmit(&huart2, (uint8_t*)identStr, strlen(identStr), 1000);
|
|
||||||
//sprintf(outputStr, "Ident. : %s\t\r\n", identStr);
|
|
||||||
|
|
||||||
//CDC_Transmit_FS((uint8_t*)outputStr, strlen(outputStr));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!KeepAlive()){
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char serial[20];
|
|
||||||
ReadSerialNumber(serial, sizeof(serial));
|
|
||||||
//sprintf(serial, "serial numb: %s\r\n", serial);
|
|
||||||
//HAL_UART_Transmit(&huart2, (uint8_t*)serial, strlen(serial), 1000);
|
|
||||||
/*sprintf(outputStr, "Serial N. : %s\t\r\n", serial);
|
|
||||||
|
|
||||||
CDC_Transmit_FS((uint8_t*)outputStr, strlen(outputStr));*/
|
|
||||||
|
|
||||||
//sprintf(serial, "serial numb: %s\r\n", serial);
|
|
||||||
//HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* FAULT CODE READING */
|
|
||||||
|
|
||||||
|
|
||||||
#define FAULTCODE_NONE_DTC 0x00
|
|
||||||
|
|
||||||
/* --- Tuning limits (adapt to your max) --- */
|
|
||||||
#define MAX_FAULTCODES 20
|
|
||||||
#define MAX_FC_AGGREGATE_LEN 512 // total concatenated body bytes
|
|
||||||
|
|
||||||
int ReadFaultCodes(FaultCode* outCodes, int maxCodes)
|
|
||||||
{
|
|
||||||
//char logmsg[64];
|
|
||||||
|
|
||||||
if (!outCodes || maxCodes <= 0) return -1;
|
|
||||||
|
|
||||||
// Keep the link alive as you do elsewhere TODO
|
|
||||||
if (!KeepAlive()) {
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(0);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// "Sending ReadFaultCodes packet"
|
|
||||||
/*sprintf(logmsg, "Sending ReadFaultCodes packet\r\n");
|
|
||||||
CDC_Transmit_FS((uint8_t*)logmsg, (uint16_t)strlen(logmsg));*/
|
|
||||||
|
|
||||||
// Send single-byte payload with command 0x07
|
|
||||||
uint8_t cmd = (uint8_t)PACKET_CMD_FaultCodesRead; // already defined in your enum
|
|
||||||
SendPacket(&cmd, 1);
|
|
||||||
|
|
||||||
// Receive all response packets
|
|
||||||
int packet_count = 0;
|
|
||||||
ParsedPacket* packets = ReceivePackets(&packet_count);
|
|
||||||
if (!packets || packet_count <= 0) {
|
|
||||||
return 0; // no codes
|
|
||||||
}
|
|
||||||
|
|
||||||
// Concatenate all bodies from non-ACK/NAK packets
|
|
||||||
// Body = raw[3 .. (3 + body_len - 1)], where body_len = (length - 3).
|
|
||||||
// Full raw frame is: [len][counter][command][body...][end]
|
|
||||||
// Your receiver code writes the end byte separately (0x03).
|
|
||||||
// NOTE: guard against malformed lengths.
|
|
||||||
uint8_t fc_buf[64];
|
|
||||||
int fc_len = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < packet_count; i++) {
|
|
||||||
if (packets[i].isAckNak) continue;
|
|
||||||
|
|
||||||
// If you classify types and want to be strict, enforce here:
|
|
||||||
if (packets[i].title != PACKET_CMD_FaultCodesResponse) {
|
|
||||||
/*sprintf(logmsg, "Expected FaultCodesPacket but got type=0x%02X\r\n", packets[i].type);
|
|
||||||
CDC_Transmit_FS((uint8_t*)logmsg, (uint16_t)strlen(logmsg));*/
|
|
||||||
//free(packets);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!packets[i].raw || packets[i].length < 3) continue;
|
|
||||||
|
|
||||||
int body_len = (int)packets[i].length - 4; // subtract [len, counter, command]
|
|
||||||
if (body_len <= 0) continue;
|
|
||||||
|
|
||||||
// Defensive: make sure the raw buffer actually has those bytes (+1 end is read separately)
|
|
||||||
// We assume your parser filled .raw accordingly.
|
|
||||||
|
|
||||||
if ((fc_len + body_len) > (int)sizeof(fc_buf)) {
|
|
||||||
/*sprintf(logmsg, "Fault code buffer overflow\r\n");
|
|
||||||
CDC_Transmit_FS((uint8_t*)logmsg, (uint16_t)strlen(logmsg));*/
|
|
||||||
//free(packets);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&fc_buf[fc_len], &packets[i].raw[3], (size_t)body_len);
|
|
||||||
fc_len += body_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Done with the packet list (your comment: free only the array, not reused raw)
|
|
||||||
//free(packets);
|
|
||||||
|
|
||||||
// Parse aggregated data:
|
|
||||||
// C# does: take first 3 bytes (dtc,status,extra), then Skip(8) per record.
|
|
||||||
// That means record stride = 8 bytes, but only first 3 matter here.
|
|
||||||
// Stop when fewer than 3 bytes remain.
|
|
||||||
int produced = 0;
|
|
||||||
int off = 0;
|
|
||||||
|
|
||||||
while (off + 3 <= fc_len) {
|
|
||||||
uint8_t dtc = fc_buf[off + 0];
|
|
||||||
uint8_t status = fc_buf[off + 1];
|
|
||||||
uint8_t extra = fc_buf[off + 2];
|
|
||||||
|
|
||||||
if (dtc != FAULTCODE_NONE_DTC) {
|
|
||||||
if (produced < maxCodes) {
|
|
||||||
outCodes[produced].dtc = dtc;
|
|
||||||
outCodes[produced].status = status;
|
|
||||||
outCodes[produced].extra = extra;
|
|
||||||
produced++;
|
|
||||||
} else {
|
|
||||||
/*sprintf(logmsg, "Fault code output full (%d)\r\n", maxCodes);
|
|
||||||
CDC_Transmit_FS((uint8_t*)logmsg, (uint16_t)strlen(logmsg));*/
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance to next 8-byte record
|
|
||||||
off += 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
return produced; // number of valid fault codes in outCodes[]
|
|
||||||
}
|
|
||||||
int ClearFaultCodes(void)
|
|
||||||
{
|
|
||||||
int packet_count = 0;
|
|
||||||
uint8_t req[1] = { PACKET_CMD_FaultCodesDelete };
|
|
||||||
|
|
||||||
ParsedPacket *packets = SendCustom(req, 1, &packet_count);
|
|
||||||
|
|
||||||
// C# expects exactly 1 packet
|
|
||||||
if (packet_count != 1) {
|
|
||||||
// in embedded: return error code (or assert/log)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ParsedPacket *p = &packets[0];
|
|
||||||
|
|
||||||
// Must be ACK or NAK
|
|
||||||
if (!p->isAckNak) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p->type == PACKET_TYPE_NAK) return 0;
|
|
||||||
if (p->type == PACKET_TYPE_ACK) return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*void PrintFaultCodes(const FaultCode* list, int n)
|
|
||||||
{
|
|
||||||
if (!list || n <= 0) {
|
|
||||||
const char* msg = "No fault codes\r\n";
|
|
||||||
CDC_Transmit_FS((uint8_t*)msg, (uint16_t)strlen(msg));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
PrintFaultCode(&list[i]);
|
|
||||||
HAL_Delay(10);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
typedef struct { uint8_t code; const char* text; } DtcMapEntry;
|
|
||||||
|
|
||||||
static const DtcMapEntry DTC_MAP[] = {
|
|
||||||
{0x50, "Fuel quantity solenoid valve Output stage error"},
|
|
||||||
{0x51, "Fuel quantity solenoid valve."},
|
|
||||||
{0x52, "Angle sensor/ IWZ system."},
|
|
||||||
{0x53, "Angle sensor/ IWZ system"},
|
|
||||||
{0x54, "Control unit temperature sensor, temperature too high"},
|
|
||||||
{0x55, "Control unit temperature sensor"},
|
|
||||||
{0x56, "Battery voltage out of range"},
|
|
||||||
{0x57, "Timing device control. Permanent control deviation"},
|
|
||||||
{0x58, "Fuel quantity / timing solenoid valve"},
|
|
||||||
{0x59, "BIP Fault (Begin of Injection Point)"},
|
|
||||||
{0x5A, "Engine speed signal"},
|
|
||||||
{0x5B, "Engine speed signal"},
|
|
||||||
{0x5C, "CAN-Bus (sporadic)"},
|
|
||||||
{0x5D, "CAN bus error"},
|
|
||||||
{0x5E, "Self-test error"},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char* GetDtcText(uint8_t code)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < (sizeof(DTC_MAP)/sizeof(DTC_MAP[0])); i++) {
|
|
||||||
if (DTC_MAP[i].code == code) return DTC_MAP[i].text;
|
|
||||||
}
|
|
||||||
return "Unknown Error Code";
|
|
||||||
}
|
|
||||||
/*int FaultCode_ToString(const FaultCode* fc, char* dst, int dst_len)
|
|
||||||
{
|
|
||||||
if (!fc || !dst || dst_len <= 0) return 0;
|
|
||||||
|
|
||||||
uint8_t status1 = (uint8_t)(fc->status & 0x7F);
|
|
||||||
const char* txt = GetDtcText(fc->dtc);
|
|
||||||
|
|
||||||
// Format like: "5A Engine speed signal (03)\r\n"
|
|
||||||
// (use %02u to mimic ":d2" from your C# example)
|
|
||||||
int n = snprintf(dst, (size_t)dst_len, "%02X %s (%02u)\r\n",
|
|
||||||
fc->dtc, txt, status1);
|
|
||||||
if (n < 0) n = 0;
|
|
||||||
if (n >= dst_len) n = dst_len - 1;
|
|
||||||
return n;
|
|
||||||
}*/
|
|
||||||
/*void PrintFaultCode(const FaultCode* fc)
|
|
||||||
{
|
|
||||||
char line[128];
|
|
||||||
int n = FaultCode_ToString(fc, line, sizeof(line));
|
|
||||||
if (n > 0) {
|
|
||||||
CDC_Transmit_FS((uint8_t*)line, (uint16_t)n);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
void KLINE_THROW_NONALIVE_EXCEPTION(uint8_t id){
|
|
||||||
//char erroralive[20];
|
|
||||||
//sprintf(erroralive, "isnt alive %u\r\r\n", id);
|
|
||||||
BT_KLINE_ERROR();
|
|
||||||
//HAL_UART_Transmit(&huart2, (uint8_t*)erroralive, strlen(erroralive), 1000);
|
|
||||||
//CDC_Transmit_FS((uint8_t*)erroralive, strlen(erroralive));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t ReadAudiPin(uint16_t* pin) {
|
|
||||||
int data_length = 0;
|
|
||||||
uint8_t* data = ReadRomEeprom(0x04FA, 2, &data_length);
|
|
||||||
if(data_length < 1){return 0;}
|
|
||||||
*pin = (uint16_t)((data[1] << 8) | data[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
uint8_t WriteAudiPin(uint16_t pin) {
|
|
||||||
/*uint8_t* data = ReadRomEeprom(0x04FA, 2, &data_length);
|
|
||||||
if(data_length < 1){return 0;}
|
|
||||||
*pin = (uint16_t)((data[1] << 8) | data[0]);*/
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t ReadVoltage(uint16_t* volt) {
|
|
||||||
int data_length = 0;
|
|
||||||
uint8_t* data = ReadRomEeprom(0x0142, 2, &data_length);
|
|
||||||
if(data_length < 1){return 0;}
|
|
||||||
*volt = (uint16_t)((data[1] << 8) | data[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* kline.h
|
|
||||||
*
|
|
||||||
* Created on: Aug 18, 2025
|
|
||||||
* Author: herli
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef INC_KLINE_H_
|
|
||||||
#define INC_KLINE_H_
|
|
||||||
|
|
||||||
#include "IKW1281Connection.h"
|
|
||||||
|
|
||||||
#define ECU_INIT_ADDRESS 0xF1
|
|
||||||
|
|
||||||
#define KLINE_GPIO_PORT GPIOB
|
|
||||||
#define KLINE_PIN GPIO_PIN_14
|
|
||||||
|
|
||||||
extern uint8_t BitBang;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t dtc; // Primary DTC byte
|
|
||||||
uint8_t status; // Status byte
|
|
||||||
uint8_t extra; // Third byte in the 3-byte header (kept for parity with C#)
|
|
||||||
} FaultCode;
|
|
||||||
|
|
||||||
extern ControllerInfo ReadEcuInfo(void);
|
|
||||||
int WakeUp(uint8_t controllerAddress, uint8_t evenParity);
|
|
||||||
void IdentifyEcu(void);
|
|
||||||
int ReadFaultCodes(FaultCode* outCodes, int maxCodes);
|
|
||||||
uint8_t ReadVoltage(uint16_t* volt);
|
|
||||||
extern void KLINE_THROW_NONALIVE_EXCEPTION(uint8_t id);
|
|
||||||
int FaultCode_ToString(const FaultCode* fc, char* dst, int dst_len);
|
|
||||||
|
|
||||||
#endif /* INC_KLINE_H_ */
|
|
||||||
@@ -0,0 +1,791 @@
|
|||||||
|
/*
|
||||||
|
* kline_fsm.c
|
||||||
|
*
|
||||||
|
* Full non-blocking KWP1281 state machine.
|
||||||
|
*
|
||||||
|
* Timing model
|
||||||
|
* ------------
|
||||||
|
* Every state that needs a delay stores a deadline:
|
||||||
|
* fsm.deadline = HAL_GetTick() + N_ms;
|
||||||
|
* and on the next ST_xxx entry just checks:
|
||||||
|
* if ((int32_t)(HAL_GetTick() - fsm.deadline) < 0) return;
|
||||||
|
*
|
||||||
|
* This means KLine_FSM_Run() returns almost immediately on every call
|
||||||
|
* and the main loop stays responsive for HC-06 processing.
|
||||||
|
*
|
||||||
|
* Zero HAL_Delay calls anywhere in this file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kline_fsm.h"
|
||||||
|
#include "kline_ring.h"
|
||||||
|
#include "main.h" /* huart1, KLINE_GPIO_PORT, KLINE_PIN */
|
||||||
|
#include "hc_06.h" /* BT_OnPSG5CommEstablished, BT_SendCommStatus */
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Internal timing constants (ms) */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
#define BITBANG_BIT_MS 200u /* 5 baud = 200 ms/bit */
|
||||||
|
#define KWP_INTERBYTE_TX_MS 5u /* between bytes we send */
|
||||||
|
#define KWP_BYTE_TIMEOUT_MS 1000u /* max wait for a byte from ECU */
|
||||||
|
#define KWP_KW2_COMP_DELAY_MS 5u /* delay before sending ~KW2 */
|
||||||
|
#define KWP_KEEPALIVE_PERIOD_MS 500u /* how often we must send keep-alive */
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Low-level TX helpers (synchronous – tiny at 9600 baud, safe) */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void tx_byte(uint8_t b)
|
||||||
|
{
|
||||||
|
HAL_UART_Transmit(&huart1, &b, 1, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KLine_FSM_TxByte(uint8_t b)
|
||||||
|
{
|
||||||
|
tx_byte(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Packet parsing helpers */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
#define MAX_RECV_PACKETS 16
|
||||||
|
#define MAX_PACKET_RAW 20
|
||||||
|
|
||||||
|
/* Scratch packet being assembled right now */
|
||||||
|
static ParsedPacket cur_pkt;
|
||||||
|
static uint8_t cur_pkt_body_idx; /* how many body bytes received so far */
|
||||||
|
|
||||||
|
/* Completed packet list for the current ReceivePackets burst */
|
||||||
|
static ParsedPacket pkt_list[MAX_RECV_PACKETS];
|
||||||
|
static int pkt_count;
|
||||||
|
|
||||||
|
static uint8_t pkt_counter_val;
|
||||||
|
static uint8_t pkt_counter_init;
|
||||||
|
|
||||||
|
static void classify_packet(ParsedPacket *p)
|
||||||
|
{
|
||||||
|
p->title = p->raw[2];
|
||||||
|
p->isAckNak = 0;
|
||||||
|
|
||||||
|
switch (p->raw[2]) {
|
||||||
|
case PACKET_CMD_ACK:
|
||||||
|
p->type = PACKET_TYPE_ACK;
|
||||||
|
p->isAckNak = 1;
|
||||||
|
break;
|
||||||
|
case PACKET_CMD_NAK:
|
||||||
|
p->type = PACKET_TYPE_NAK;
|
||||||
|
p->isAckNak = 1;
|
||||||
|
break;
|
||||||
|
case PACKET_CMD_AsciiData:
|
||||||
|
p->type = (p->raw[3] == 0x00) ? PACKET_TYPE_CODING_WSC
|
||||||
|
: PACKET_TYPE_ASCII_DATA;
|
||||||
|
break;
|
||||||
|
case PACKET_CMD_ReadEepromResponse:
|
||||||
|
p->type = PACKET_TYPE_READ_EEPROM_RESPONSE;
|
||||||
|
break;
|
||||||
|
case PACKET_CMD_ReadRomEepromResponse:
|
||||||
|
p->type = PACKET_TYPE_READ_ROM_EEPROM_RESPONSE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
p->type = PACKET_TYPE_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Outgoing packet scratch buffer */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
#define MAX_SEND_PACKET 17
|
||||||
|
|
||||||
|
static uint8_t send_buf[MAX_SEND_PACKET];
|
||||||
|
static uint8_t send_len; /* total bytes in send_buf (excl. end byte) */
|
||||||
|
static uint8_t send_idx; /* index of next byte to transmit */
|
||||||
|
|
||||||
|
//static uint8_t tx_pkt_counter = 0;
|
||||||
|
|
||||||
|
static void build_packet(const uint8_t *payload, uint8_t payload_len)
|
||||||
|
{
|
||||||
|
send_len = payload_len + 2; /* length byte + counter byte + payload */
|
||||||
|
send_buf[0] = send_len;
|
||||||
|
send_buf[1] = pkt_counter_val++;
|
||||||
|
for (uint8_t i = 0; i < payload_len; i++)
|
||||||
|
send_buf[2 + i] = payload[i];
|
||||||
|
//send_buf[2 + payload_len] = PACKET_END_EXPECTED;
|
||||||
|
send_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* FSM context */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/* What to do after the generic send-packet sub-FSM finishes */
|
||||||
|
AFTER_SEND_IDLE,
|
||||||
|
AFTER_SEND_RECV_ECUINFO,
|
||||||
|
AFTER_SEND_RECV_IDENT,
|
||||||
|
AFTER_SEND_RECV_KA,
|
||||||
|
AFTER_SEND_RECV_FAULTCODES,
|
||||||
|
} AfterSend;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/* What to do after the generic recv-packets sub-FSM finishes */
|
||||||
|
AFTER_RECV_ECUINFO,
|
||||||
|
AFTER_RECV_IDENT,
|
||||||
|
AFTER_RECV_KA,
|
||||||
|
AFTER_RECV_FAULTCODES,
|
||||||
|
} AfterRecv;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
KLine_FSMState state;
|
||||||
|
KLine_Status status;
|
||||||
|
uint32_t deadline;
|
||||||
|
|
||||||
|
/* 5-baud bit-bang */
|
||||||
|
uint8_t bb_addr;
|
||||||
|
uint8_t bb_bit; /* current bit index 0-7 */
|
||||||
|
|
||||||
|
/* WakeUp */
|
||||||
|
uint8_t kw1;
|
||||||
|
|
||||||
|
/* Generic TX sub-machine */
|
||||||
|
AfterSend after_send;
|
||||||
|
uint8_t last_sent_byte; /* for ACK check */
|
||||||
|
|
||||||
|
/* Generic RX sub-machine */
|
||||||
|
AfterRecv after_recv;
|
||||||
|
|
||||||
|
/* ECU info */
|
||||||
|
ControllerInfo ecu_info;
|
||||||
|
|
||||||
|
/* Fault codes */
|
||||||
|
KLine_FaultCode fault_codes[KLINE_MAX_FAULT_CODES];
|
||||||
|
int fault_count;
|
||||||
|
|
||||||
|
/* Keep-alive */
|
||||||
|
uint32_t ka_due;
|
||||||
|
uint8_t ka_suspended; /* non-zero → KA paused while an op runs */
|
||||||
|
|
||||||
|
/* IdentifyEcu phase counter (how many ident packets received so far) */
|
||||||
|
uint8_t ident_phase;
|
||||||
|
|
||||||
|
} KLine_FSM;
|
||||||
|
|
||||||
|
static KLine_FSM fsm;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Forward declarations for sub-machine entry points */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void enter_send_packet(const uint8_t *payload, uint8_t len, AfterSend after);
|
||||||
|
static void enter_recv_packets(AfterRecv after);
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Byte-level receive helpers */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to read a byte with complement-ACK from the ECU.
|
||||||
|
* Returns 1 and stores byte in *out if available, 0 if still waiting.
|
||||||
|
* Handles timeout: transitions to ST_ERROR on timeout.
|
||||||
|
*/
|
||||||
|
static uint8_t try_read_and_ack(uint8_t *out)
|
||||||
|
{
|
||||||
|
if (!KLine_Ring_Available()) {
|
||||||
|
if ((int32_t)(HAL_GetTick() - fsm.deadline) >= 0) {
|
||||||
|
fsm.state = ST_ERROR;
|
||||||
|
fsm.status = KLINE_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*out = KLine_Ring_Pop();
|
||||||
|
tx_byte((uint8_t)~(*out)); /* send complement ACK */
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t echo_deadline = HAL_GetTick() + 5u; /* ~1ms at 9600, 5ms generous */
|
||||||
|
while (!KLine_Ring_Available()) {
|
||||||
|
if ((int32_t)(HAL_GetTick() - echo_deadline) >= 0) break;
|
||||||
|
}
|
||||||
|
if (KLine_Ring_Available()) KLine_Ring_Pop();
|
||||||
|
|
||||||
|
fsm.deadline = HAL_GetTick() + KWP_BYTE_TIMEOUT_MS;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t try_read_raw(uint8_t *out)
|
||||||
|
{
|
||||||
|
if (!KLine_Ring_Available()) {
|
||||||
|
if ((int32_t)(HAL_GetTick() - fsm.deadline) >= 0) {
|
||||||
|
fsm.state = ST_ERROR;
|
||||||
|
fsm.status = KLINE_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*out = KLine_Ring_Pop();
|
||||||
|
fsm.deadline = HAL_GetTick() + KWP_BYTE_TIMEOUT_MS;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* ECU info parser */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void parse_ecu_info(void)
|
||||||
|
{
|
||||||
|
char combined[128] = {0};
|
||||||
|
for (int i = 0; i < pkt_count; i++) {
|
||||||
|
ParsedPacket *p = &pkt_list[i];
|
||||||
|
if (p->type == PACKET_TYPE_ASCII_DATA && p->length > 4) {
|
||||||
|
size_t len = p->length - 4;
|
||||||
|
strncat(combined, (char*)(p->raw + 3), len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memset(&fsm.ecu_info, 0, sizeof(ControllerInfo));
|
||||||
|
strncpy(fsm.ecu_info.client_ident, combined, 12);
|
||||||
|
strncpy(fsm.ecu_info.unk_ident1, combined + 12, 10);
|
||||||
|
strncpy(fsm.ecu_info.soft_info, combined + 22, 10);
|
||||||
|
if (strlen(combined) > 40)
|
||||||
|
strncpy(fsm.ecu_info.unk_ident2, combined + 32, 10);
|
||||||
|
if (strlen(combined) > 50)
|
||||||
|
strncpy(fsm.ecu_info.unk_ident3, combined + 42, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Fault code parser */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void parse_fault_codes(void)
|
||||||
|
{
|
||||||
|
uint8_t fc_buf[64];
|
||||||
|
int fc_len = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < pkt_count; i++) {
|
||||||
|
if (pkt_list[i].isAckNak) continue;
|
||||||
|
if (pkt_list[i].title != PACKET_CMD_FaultCodesResponse) continue;
|
||||||
|
int body = (int)pkt_list[i].length - 4;
|
||||||
|
if (body <= 0) continue;
|
||||||
|
if (fc_len + body > (int)sizeof(fc_buf)) break;
|
||||||
|
memcpy(&fc_buf[fc_len], &pkt_list[i].raw[3], (size_t)body);
|
||||||
|
fc_len += body;
|
||||||
|
}
|
||||||
|
|
||||||
|
fsm.fault_count = 0;
|
||||||
|
int off = 0;
|
||||||
|
while (off + 3 <= fc_len && fsm.fault_count < KLINE_MAX_FAULT_CODES) {
|
||||||
|
if (fc_buf[off] != 0x00) {
|
||||||
|
fsm.fault_codes[fsm.fault_count].dtc = fc_buf[off + 0];
|
||||||
|
fsm.fault_codes[fsm.fault_count].status = fc_buf[off + 1];
|
||||||
|
fsm.fault_codes[fsm.fault_count].extra = fc_buf[off + 2];
|
||||||
|
fsm.fault_count++;
|
||||||
|
}
|
||||||
|
off += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Sub-machine entry helpers */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void enter_send_packet(const uint8_t *payload, uint8_t len, AfterSend after)
|
||||||
|
{
|
||||||
|
build_packet(payload, len);
|
||||||
|
fsm.after_send = after;
|
||||||
|
fsm.state = ST_SENDPKT_BYTE;
|
||||||
|
fsm.deadline = HAL_GetTick() + KWP_BYTE_TIMEOUT_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enter_recv_packets(AfterRecv after)
|
||||||
|
{
|
||||||
|
pkt_count = 0;
|
||||||
|
pkt_counter_init = 0;
|
||||||
|
fsm.after_recv = after;
|
||||||
|
/* Start assembling the first packet */
|
||||||
|
memset(&cur_pkt, 0, sizeof(cur_pkt));
|
||||||
|
cur_pkt_body_idx = 0;
|
||||||
|
fsm.state = ST_RECVPKT_LENGTH;
|
||||||
|
fsm.deadline = HAL_GetTick() + KWP_BYTE_TIMEOUT_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Main FSM tick */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
void KLine_FSM_Run(void)
|
||||||
|
{
|
||||||
|
switch (fsm.state)
|
||||||
|
{
|
||||||
|
/* ====================================================== */
|
||||||
|
case ST_IDLE:
|
||||||
|
/* Nothing to do — wait for KLine_FSM_RequestConnect() */
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ====================================================== */
|
||||||
|
/* 5-baud bit-bang init */
|
||||||
|
/* ====================================================== */
|
||||||
|
|
||||||
|
case ST_BITBANG_START:
|
||||||
|
{
|
||||||
|
/* De-init UART, take over TX pin as GPIO */
|
||||||
|
HAL_UART_DeInit(&huart1);
|
||||||
|
|
||||||
|
GPIO_InitTypeDef g = {0};
|
||||||
|
g.Pin = KLINE_PIN;
|
||||||
|
g.Mode = GPIO_MODE_OUTPUT_PP;
|
||||||
|
g.Pull = GPIO_NOPULL;
|
||||||
|
g.Speed = GPIO_SPEED_FREQ_LOW;
|
||||||
|
HAL_GPIO_Init(KLINE_GPIO_PORT, &g);
|
||||||
|
|
||||||
|
/* Drive start bit (low) */
|
||||||
|
HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_RESET);
|
||||||
|
fsm.bb_bit = 0;
|
||||||
|
fsm.deadline = HAL_GetTick() + BITBANG_BIT_MS;
|
||||||
|
fsm.state = ST_BITBANG_STARTBIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ST_BITBANG_STARTBIT:
|
||||||
|
if ((int32_t)(HAL_GetTick() - fsm.deadline) < 0) return;
|
||||||
|
/* Fall into first data bit */
|
||||||
|
fsm.state = ST_BITBANG_DATABIT;
|
||||||
|
/* intentional fall-through */
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case ST_BITBANG_DATABIT:
|
||||||
|
{
|
||||||
|
if (fsm.bb_bit > 0) {
|
||||||
|
/* We get here after a bit-delay has elapsed */
|
||||||
|
if ((int32_t)(HAL_GetTick() - fsm.deadline) < 0) return;
|
||||||
|
}
|
||||||
|
if (fsm.bb_bit == 8) {
|
||||||
|
/* All data bits sent — send stop bit */
|
||||||
|
HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_SET);
|
||||||
|
fsm.deadline = HAL_GetTick() + BITBANG_BIT_MS;
|
||||||
|
fsm.state = ST_BITBANG_STOPBIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
GPIO_PinState level = (fsm.bb_addr & (1u << fsm.bb_bit))
|
||||||
|
? GPIO_PIN_SET : GPIO_PIN_RESET;
|
||||||
|
HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, level);
|
||||||
|
fsm.bb_bit++;
|
||||||
|
fsm.deadline = HAL_GetTick() + BITBANG_BIT_MS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ST_BITBANG_STOPBIT:
|
||||||
|
if ((int32_t)(HAL_GetTick() - fsm.deadline) < 0) return;
|
||||||
|
fsm.state = ST_BITBANG_DONE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ST_BITBANG_DONE:
|
||||||
|
{
|
||||||
|
/* Restore UART */
|
||||||
|
extern void MX_USART1_UART_Init(void); /* generated by CubeMX */
|
||||||
|
MX_USART1_UART_Init();
|
||||||
|
KLine_Ring_Init(); /* flushes ring and arms RX IT */
|
||||||
|
|
||||||
|
fsm.deadline = HAL_GetTick() + KWP_BYTE_TIMEOUT_MS;
|
||||||
|
fsm.state = ST_WAIT_SYNC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================================== */
|
||||||
|
/* WakeUp handshake */
|
||||||
|
/* ====================================================== */
|
||||||
|
|
||||||
|
case ST_WAIT_SYNC:
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
if (!try_read_raw(&b)) return;
|
||||||
|
if (b != 0x55) {
|
||||||
|
fsm.state = ST_ERROR;
|
||||||
|
fsm.status = KLINE_STATUS_ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fsm.state = ST_READ_KW1;
|
||||||
|
fsm.deadline = HAL_GetTick() + KWP_BYTE_TIMEOUT_MS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ST_READ_KW1:
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
if (!try_read_raw(&b)) return;
|
||||||
|
//tx_byte((uint8_t)~b); /* NO ACK KW1 */
|
||||||
|
fsm.kw1 = b;
|
||||||
|
fsm.state = ST_READ_KW2;
|
||||||
|
fsm.deadline = HAL_GetTick() + KWP_BYTE_TIMEOUT_MS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ST_READ_KW2:
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
if (!try_read_raw(&b)) return;
|
||||||
|
/* Store KW2, schedule complement send after inter-byte delay */
|
||||||
|
send_buf[0] = (uint8_t)~b; /* reuse send_buf[0] as a scratch */
|
||||||
|
fsm.deadline = HAL_GetTick() + KWP_KW2_COMP_DELAY_MS;
|
||||||
|
fsm.state = ST_SEND_KW2_COMPLEMENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ST_SEND_KW2_COMPLEMENT:
|
||||||
|
if ((int32_t)(HAL_GetTick() - fsm.deadline) < 0) return;
|
||||||
|
tx_byte(send_buf[0]); /* send ~KW2 */
|
||||||
|
|
||||||
|
//uint8_t b;
|
||||||
|
//if (!try_read_raw(&b)) return; //should return answer to kw2
|
||||||
|
while (!KLine_Ring_Available()) {}
|
||||||
|
KLine_Ring_Pop(); /* discard loopback echo */
|
||||||
|
|
||||||
|
/* Now receive the ECU info packet burst */
|
||||||
|
enter_recv_packets(AFTER_RECV_ECUINFO);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ====================================================== */
|
||||||
|
/* Generic send-packet sub-machine */
|
||||||
|
/* ====================================================== */
|
||||||
|
|
||||||
|
case ST_SENDPKT_BYTE:
|
||||||
|
{
|
||||||
|
if (send_idx >= send_len) {
|
||||||
|
/* All bytes sent — send end byte (no ACK) */
|
||||||
|
tx_byte(PACKET_END_EXPECTED);
|
||||||
|
while (!KLine_Ring_Available()) {}
|
||||||
|
KLine_Ring_Pop(); /* discard loopback echo */
|
||||||
|
/* Transition based on what comes after this send */
|
||||||
|
switch (fsm.after_send) {
|
||||||
|
case AFTER_SEND_RECV_ECUINFO:
|
||||||
|
enter_recv_packets(AFTER_RECV_ECUINFO); break;
|
||||||
|
case AFTER_SEND_RECV_IDENT:
|
||||||
|
enter_recv_packets(AFTER_RECV_IDENT); break;
|
||||||
|
case AFTER_SEND_RECV_KA:
|
||||||
|
enter_recv_packets(AFTER_RECV_KA); break;
|
||||||
|
case AFTER_SEND_RECV_FAULTCODES:
|
||||||
|
enter_recv_packets(AFTER_RECV_FAULTCODES);break;
|
||||||
|
default:
|
||||||
|
fsm.state = ST_KA_WAIT; break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Send next byte */
|
||||||
|
fsm.last_sent_byte = send_buf[send_idx++];
|
||||||
|
tx_byte(fsm.last_sent_byte);
|
||||||
|
while (!KLine_Ring_Available()) {}
|
||||||
|
KLine_Ring_Pop(); /* discard loopback echo */
|
||||||
|
|
||||||
|
fsm.deadline = HAL_GetTick() + KWP_BYTE_TIMEOUT_MS;
|
||||||
|
fsm.state = ST_SENDPKT_WAIT_ACK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ST_SENDPKT_WAIT_ACK:
|
||||||
|
{
|
||||||
|
/* Wait for ECU to echo ~byte as ACK */
|
||||||
|
if (!KLine_Ring_Available()) {
|
||||||
|
if ((int32_t)(HAL_GetTick() - fsm.deadline) >= 0) {
|
||||||
|
fsm.state = ST_ERROR;
|
||||||
|
fsm.status = KLINE_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t ack = KLine_Ring_Pop();
|
||||||
|
(void)ack; /* Could check ack == ~last_sent_byte here */
|
||||||
|
/* Inter-byte delay before next byte */
|
||||||
|
fsm.deadline = HAL_GetTick() + KWP_INTERBYTE_TX_MS;
|
||||||
|
fsm.state = ST_SENDPKT_NEXT_BYTE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ST_SENDPKT_NEXT_BYTE:
|
||||||
|
if ((int32_t)(HAL_GetTick() - fsm.deadline) < 0) return;
|
||||||
|
fsm.state = ST_SENDPKT_BYTE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ====================================================== */
|
||||||
|
/* Generic receive-packets sub-machine */
|
||||||
|
/* ====================================================== */
|
||||||
|
|
||||||
|
case ST_RECVPKT_LENGTH:
|
||||||
|
{
|
||||||
|
//if ((int32_t)(HAL_GetTick() - (fsm.deadline - KWP_BYTE_TIMEOUT_MS + 25)) < 0) return;
|
||||||
|
|
||||||
|
uint8_t b;
|
||||||
|
if (!try_read_and_ack(&b)) return;
|
||||||
|
|
||||||
|
if(send_len == 3){
|
||||||
|
send_len++;
|
||||||
|
}
|
||||||
|
memset(&cur_pkt, 0, sizeof(cur_pkt));
|
||||||
|
cur_pkt_body_idx = 0;
|
||||||
|
cur_pkt.raw[0] = b; /* raw[0] = length byte */
|
||||||
|
fsm.state = ST_RECVPKT_COUNTER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ST_RECVPKT_COUNTER:
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
if (!try_read_and_ack(&b)) return;
|
||||||
|
if (!pkt_counter_init) {
|
||||||
|
pkt_counter_val = b;
|
||||||
|
pkt_counter_init = 1;
|
||||||
|
}
|
||||||
|
pkt_counter_val++;
|
||||||
|
cur_pkt.raw[1] = b;
|
||||||
|
fsm.state = ST_RECVPKT_COMMAND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ST_RECVPKT_COMMAND:
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
if (!try_read_and_ack(&b)) return;
|
||||||
|
cur_pkt.raw[2] = b;
|
||||||
|
cur_pkt_body_idx = 3;
|
||||||
|
/* body length = packetLength - 3 (len, counter, command already read) */
|
||||||
|
/* The end byte (0x03) is read separately in ST_RECVPKT_END */
|
||||||
|
if (cur_pkt.raw[0] <= 3) {
|
||||||
|
/* No body bytes — go straight to end */
|
||||||
|
fsm.state = ST_RECVPKT_END;
|
||||||
|
} else {
|
||||||
|
fsm.state = ST_RECVPKT_BODY;
|
||||||
|
}
|
||||||
|
if(b == PACKET_CMD_ACK || b == PACKET_CMD_NAK){
|
||||||
|
cur_pkt.isAckNak = 1;
|
||||||
|
}else{
|
||||||
|
cur_pkt.isAckNak = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ST_RECVPKT_BODY:
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
if (!try_read_and_ack(&b)) return;
|
||||||
|
if (cur_pkt_body_idx < MAX_PACKET_RAW)
|
||||||
|
cur_pkt.raw[cur_pkt_body_idx] = b;
|
||||||
|
cur_pkt_body_idx++;
|
||||||
|
/* body bytes = raw[0] - 3, so we've finished when we have raw[0] bytes total */
|
||||||
|
if (cur_pkt_body_idx >= cur_pkt.raw[0]) {
|
||||||
|
fsm.state = ST_RECVPKT_END;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ST_RECVPKT_END:
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
if (!try_read_raw(&b)) return; /* end byte – no ACK */
|
||||||
|
cur_pkt.raw[cur_pkt_body_idx] = b;
|
||||||
|
cur_pkt.length = cur_pkt_body_idx + 1;
|
||||||
|
classify_packet(&cur_pkt);
|
||||||
|
|
||||||
|
if (pkt_count < MAX_RECV_PACKETS)
|
||||||
|
pkt_list[pkt_count++] = cur_pkt;
|
||||||
|
|
||||||
|
if (cur_pkt.isAckNak) {
|
||||||
|
/* This burst is complete — dispatch */
|
||||||
|
switch (fsm.after_recv) {
|
||||||
|
|
||||||
|
case AFTER_RECV_ECUINFO:
|
||||||
|
parse_ecu_info();
|
||||||
|
/* Now do IdentifyEcu: send ReadIdent, receive ident packets */
|
||||||
|
fsm.ident_phase = 0;
|
||||||
|
fsm.state = ST_IDENT_KEEPALIVE_SEND;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AFTER_RECV_IDENT:
|
||||||
|
/* More ident packets may follow — check if we need another round */
|
||||||
|
fsm.ident_phase++;
|
||||||
|
if (fsm.ident_phase < 3) {
|
||||||
|
/* Send another ReadIdent ACK and receive next burst */
|
||||||
|
fsm.state = ST_IDENT_KEEPALIVE_SEND;
|
||||||
|
} else {
|
||||||
|
/* Done identifying — enter keep-alive + fault code read */
|
||||||
|
uint8_t cmd = (uint8_t)PACKET_CMD_FaultCodesRead;
|
||||||
|
enter_send_packet(&cmd, 1, AFTER_SEND_RECV_FAULTCODES);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AFTER_RECV_KA:
|
||||||
|
/* Keep-alive round-trip complete */
|
||||||
|
if (pkt_list[pkt_count-1].type != PACKET_TYPE_ACK) {
|
||||||
|
fsm.state = ST_ERROR;
|
||||||
|
fsm.status = KLINE_STATUS_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
KLine_OnKeepAliveTick();
|
||||||
|
fsm.ka_due = HAL_GetTick() + KWP_KEEPALIVE_PERIOD_MS;
|
||||||
|
fsm.state = ST_KA_WAIT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AFTER_RECV_FAULTCODES:
|
||||||
|
parse_fault_codes();
|
||||||
|
/* Session fully established */
|
||||||
|
fsm.status = KLINE_STATUS_CONNECTED;
|
||||||
|
KLine_OnConnected();
|
||||||
|
fsm.ka_due = HAL_GetTick() + KWP_KEEPALIVE_PERIOD_MS;
|
||||||
|
fsm.state = ST_KA_WAIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* More packets in this burst — send ACK then read next */
|
||||||
|
fsm.state = ST_RECVPKT_SEND_ACK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ST_RECVPKT_SEND_ACK:
|
||||||
|
{
|
||||||
|
/* Send ACK packet (len=3, counter, 0x09) then read next packet */
|
||||||
|
uint8_t ack_payload = (uint8_t)PACKET_CMD_ACK;
|
||||||
|
enter_send_packet(&ack_payload, 1, AFTER_SEND_IDLE);
|
||||||
|
|
||||||
|
/* After sending the ACK packet we continue receiving in the same burst */
|
||||||
|
fsm.after_send = AFTER_SEND_IDLE; /* send sub-machine won't auto-recv */
|
||||||
|
|
||||||
|
/* Prepare for next packet in burst */
|
||||||
|
memset(&cur_pkt, 0, sizeof(cur_pkt));
|
||||||
|
cur_pkt_body_idx = 0;
|
||||||
|
|
||||||
|
/* Override the send sub-machine's post-send state to loop back to recv */
|
||||||
|
/* We do this by saving after_recv and re-entering after send completes */
|
||||||
|
/* Simplest: finish the send inline here before entering recv again. */
|
||||||
|
/* Actually: enter_send_packet already set state = ST_SENDPKT_BYTE, */
|
||||||
|
/* we need it to go to ST_RECVPKT_LENGTH after. We tag after_send. */
|
||||||
|
switch (fsm.after_recv) {
|
||||||
|
case AFTER_RECV_ECUINFO:
|
||||||
|
fsm.after_send = AFTER_SEND_RECV_ECUINFO; break;
|
||||||
|
case AFTER_RECV_IDENT:
|
||||||
|
fsm.after_send = AFTER_SEND_RECV_IDENT; break;
|
||||||
|
case AFTER_RECV_KA:
|
||||||
|
fsm.after_send = AFTER_SEND_RECV_KA; break;
|
||||||
|
case AFTER_RECV_FAULTCODES:
|
||||||
|
fsm.after_send = AFTER_SEND_RECV_FAULTCODES; break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================================== */
|
||||||
|
/* IdentifyEcu sub-sequence */
|
||||||
|
/* ====================================================== */
|
||||||
|
|
||||||
|
case ST_IDENT_KEEPALIVE_SEND:
|
||||||
|
{
|
||||||
|
/* Send ACK packet as keep-alive / handshake before ReadIdent */
|
||||||
|
uint8_t ack = (uint8_t)PACKET_CMD_ACK;
|
||||||
|
enter_send_packet(&ack, 1, AFTER_SEND_RECV_IDENT);
|
||||||
|
/* After sending ACK we expect a packet burst back */
|
||||||
|
/* The after_recv is set inside enter_recv_packets in AFTER_SEND_RECV_IDENT path */
|
||||||
|
/* Override: we actually want AFTER_RECV_IDENT for the receive burst */
|
||||||
|
fsm.after_send = AFTER_SEND_RECV_IDENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================================== */
|
||||||
|
/* Keep-alive steady state */
|
||||||
|
/* ====================================================== */
|
||||||
|
|
||||||
|
case ST_KA_WAIT:
|
||||||
|
if (fsm.ka_suspended) return; /* operations running — don't send KA */
|
||||||
|
if ((int32_t)(HAL_GetTick() - fsm.ka_due) < 0) return;
|
||||||
|
/* Time to send a keep-alive */
|
||||||
|
{
|
||||||
|
uint8_t ack = (uint8_t)PACKET_CMD_ACK;
|
||||||
|
enter_send_packet(&ack, 1, AFTER_SEND_RECV_KA);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ====================================================== */
|
||||||
|
/* Error */
|
||||||
|
/* ====================================================== */
|
||||||
|
|
||||||
|
case ST_ERROR:
|
||||||
|
fsm.status = KLINE_STATUS_ERROR;
|
||||||
|
KLine_OnDisconnected();
|
||||||
|
/* Disarm UART and flush ring */
|
||||||
|
HAL_UART_AbortReceive(&huart1);
|
||||||
|
KLine_Ring_Flush();
|
||||||
|
fsm.state = ST_IDLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Public API */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
void KLine_FSM_Init(void)
|
||||||
|
{
|
||||||
|
memset(&fsm, 0, sizeof(fsm));
|
||||||
|
fsm.state = ST_IDLE;
|
||||||
|
fsm.status = KLINE_STATUS_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KLine_FSM_RequestConnect(uint8_t ecuAddress)
|
||||||
|
{
|
||||||
|
if (fsm.state != ST_IDLE) return;
|
||||||
|
memset(&fsm, 0, sizeof(fsm));
|
||||||
|
fsm.bb_addr = ecuAddress;
|
||||||
|
fsm.status = KLINE_STATUS_CONNECTING;
|
||||||
|
fsm.state = ST_BITBANG_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
KLine_Status KLine_FSM_GetStatus(void)
|
||||||
|
{
|
||||||
|
return fsm.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t KLine_FSM_IsConnected(void)
|
||||||
|
{
|
||||||
|
return fsm.status == KLINE_STATUS_CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ControllerInfo* KLine_FSM_GetEcuInfo(void)
|
||||||
|
{
|
||||||
|
return &fsm.ecu_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KLine_FaultCode* KLine_FSM_GetFaultCodes(int *out_count)
|
||||||
|
{
|
||||||
|
if (out_count) *out_count = fsm.fault_count;
|
||||||
|
return fsm.fault_codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Operations support API */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
uint8_t KLine_FSM_GetNextCounter(void)
|
||||||
|
{
|
||||||
|
return pkt_counter_val++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KLine_FSM_SuspendKA(uint8_t suspend)
|
||||||
|
{
|
||||||
|
fsm.ka_suspended = suspend;
|
||||||
|
if (!suspend) {
|
||||||
|
/* Resume: push deadline forward so we don't immediately timeout */
|
||||||
|
fsm.ka_due = HAL_GetTick() + KWP_KEEPALIVE_PERIOD_MS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KLine_FSM_ResetKATimer(void)
|
||||||
|
{
|
||||||
|
fsm.ka_due = HAL_GetTick() + KWP_KEEPALIVE_PERIOD_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Weak default callbacks — override in main.c */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
__attribute__((weak)) void KLine_OnConnected(void) {}
|
||||||
|
__attribute__((weak)) void KLine_OnDisconnected(void) {}
|
||||||
|
__attribute__((weak)) void KLine_OnKeepAliveTick(void){}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* kline_fsm.h
|
||||||
|
* Non-blocking KWP1281 protocol state machine for STM32H533.
|
||||||
|
*
|
||||||
|
* Design rules:
|
||||||
|
* - KLine_FSM_Run() is called every main-loop iteration. It never blocks.
|
||||||
|
* - Timing is always "store a deadline, check it next tick". No HAL_Delay.
|
||||||
|
* - UART1 RX bytes are consumed from the ring buffer (kline_ring.h).
|
||||||
|
* - UART1 TX is synchronous (HAL_UART_Transmit with short timeout) because
|
||||||
|
* TX completes in microseconds at 9600 baud and does not race with anything.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INC_KLINE_FSM_H_
|
||||||
|
#define INC_KLINE_FSM_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "IKW1281Connection.h" /* ParsedPacket, PacketCommand, ControllerInfo */
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Public result / status types */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
KLINE_STATUS_IDLE = 0,
|
||||||
|
KLINE_STATUS_CONNECTING,
|
||||||
|
KLINE_STATUS_CONNECTED,
|
||||||
|
KLINE_STATUS_ERROR,
|
||||||
|
} KLine_Status;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Internal FSM states (exposed in header for debuggability) */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
|
||||||
|
/* --- Idle --- */
|
||||||
|
ST_IDLE,
|
||||||
|
|
||||||
|
/* --- 5-baud init sequence --- */
|
||||||
|
ST_BITBANG_START, /* De-init UART, configure TX pin as GPIO */
|
||||||
|
ST_BITBANG_STARTBIT, /* Drive line low (start bit) */
|
||||||
|
ST_BITBANG_DATABIT, /* Clock out 8 data bits one by one */
|
||||||
|
ST_BITBANG_STOPBIT, /* Drive line high (stop bit) */
|
||||||
|
ST_BITBANG_DONE, /* Re-init UART, arm RX ring */
|
||||||
|
|
||||||
|
/* --- Sync byte from ECU (0x55) --- */
|
||||||
|
ST_WAIT_SYNC,
|
||||||
|
|
||||||
|
/* --- Keywords --- */
|
||||||
|
ST_READ_KW1,
|
||||||
|
ST_READ_KW2,
|
||||||
|
ST_SEND_KW2_COMPLEMENT, /* Write ~KW2 after inter-byte delay */
|
||||||
|
|
||||||
|
/* --- ECU info packets (initial ReceivePackets burst) --- */
|
||||||
|
ST_RECV_ECUINFO_PACKET,
|
||||||
|
ST_SEND_ECUINFO_ACK,
|
||||||
|
|
||||||
|
/* --- IdentifyEcu sub-sequence --- */
|
||||||
|
ST_IDENT_KEEPALIVE_SEND,
|
||||||
|
ST_IDENT_KEEPALIVE_RECV,
|
||||||
|
ST_IDENT_RECV_PACKET,
|
||||||
|
ST_IDENT_SEND_ACK,
|
||||||
|
|
||||||
|
/* --- Steady-state keep-alive --- */
|
||||||
|
ST_KA_SEND_ACK_PACKET, /* Send ACK packet to ECU */
|
||||||
|
ST_KA_RECV_ACK, /* Expect ACK packet back */
|
||||||
|
ST_KA_WAIT, /* Idle until next KA is due */
|
||||||
|
|
||||||
|
/* --- Generic packet TX (SendPacket) --- */
|
||||||
|
ST_SENDPKT_BYTE, /* Send one byte of the outgoing packet */
|
||||||
|
ST_SENDPKT_WAIT_ACK, /* Wait for complement-ACK from ECU */
|
||||||
|
ST_SENDPKT_NEXT_BYTE, /* Inter-byte delay then next byte */
|
||||||
|
ST_SENDPKT_END, /* Send 0x03 end byte, no ACK */
|
||||||
|
|
||||||
|
/* --- Generic packet RX (ReceivePackets) --- */
|
||||||
|
ST_RECVPKT_LENGTH,
|
||||||
|
ST_RECVPKT_COUNTER,
|
||||||
|
ST_RECVPKT_COMMAND,
|
||||||
|
ST_RECVPKT_BODY,
|
||||||
|
ST_RECVPKT_END,
|
||||||
|
ST_RECVPKT_SEND_ACK, /* Send ACK packet after non-ACK/NAK packet */
|
||||||
|
|
||||||
|
/* --- Error / disconnected --- */
|
||||||
|
ST_ERROR,
|
||||||
|
|
||||||
|
} KLine_FSMState;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Public API */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/* Call after all HAL inits, before the while(1) loop */
|
||||||
|
void KLine_FSM_Init(void);
|
||||||
|
|
||||||
|
/* Call every main-loop iteration – returns immediately */
|
||||||
|
void KLine_FSM_Run(void);
|
||||||
|
|
||||||
|
/* Trigger a connection attempt (replaces BitBang = 1 / TryConnection()) */
|
||||||
|
void KLine_FSM_RequestConnect(uint8_t ecuAddress);
|
||||||
|
|
||||||
|
/* Query current status */
|
||||||
|
KLine_Status KLine_FSM_GetStatus(void);
|
||||||
|
|
||||||
|
/* True while a keep-alive session is active */
|
||||||
|
uint8_t KLine_FSM_IsConnected(void);
|
||||||
|
|
||||||
|
/* Access last-received ECU info */
|
||||||
|
const ControllerInfo* KLine_FSM_GetEcuInfo(void);
|
||||||
|
|
||||||
|
/* Access last-received fault codes */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t dtc;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t extra;
|
||||||
|
} KLine_FaultCode;
|
||||||
|
|
||||||
|
#define KLINE_MAX_FAULT_CODES 20
|
||||||
|
const KLine_FaultCode* KLine_FSM_GetFaultCodes(int *out_count);
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Operations support API */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the current packet counter and increment it.
|
||||||
|
* Used by kline_operations.c so that operation packets share the same
|
||||||
|
* session counter the FSM uses. Must only be called from main-loop context.
|
||||||
|
*/
|
||||||
|
uint8_t KLine_FSM_GetNextCounter(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Suspend / resume keep-alive ticks.
|
||||||
|
* When suspend != 0, the FSM will NOT send keep-alive packets while in
|
||||||
|
* ST_KA_WAIT. The caller must resume (suspend=0) when the operation
|
||||||
|
* finishes so that keep-alive resumes normally.
|
||||||
|
*
|
||||||
|
* This also resets the KA timer so the next keep-alive fires a full
|
||||||
|
* period after the operation ends, preventing an immediate timeout.
|
||||||
|
*/
|
||||||
|
void KLine_FSM_SuspendKA(uint8_t suspend);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Manually reset the keep-alive deadline to "now + period".
|
||||||
|
* Called by operation code after finishing so the ECU doesn't time out.
|
||||||
|
*/
|
||||||
|
void KLine_FSM_ResetKATimer(void);
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Low-level TX helper (shared with kline_operations.c) */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
void KLine_FSM_TxByte(uint8_t b);
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Callbacks – implement in main.c / hc_06 glue */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
void KLine_OnConnected(void); /* called when session established */
|
||||||
|
void KLine_OnDisconnected(void); /* called on timeout / error */
|
||||||
|
void KLine_OnKeepAliveTick(void); /* called each successful keep-alive */
|
||||||
|
|
||||||
|
#endif /* INC_KLINE_FSM_H_ */
|
||||||
@@ -0,0 +1,925 @@
|
|||||||
|
/*
|
||||||
|
* kline_operations.c
|
||||||
|
*
|
||||||
|
* Non-blocking KWP1281 operations for the FSM architecture.
|
||||||
|
*
|
||||||
|
* Each operation (ReadDfi, WriteDfi, ReadVoltage, ReadAudiPin,
|
||||||
|
* ClearFaultCodes) is implemented as a multi-step state machine driven
|
||||||
|
* by KLineOp_Poll(), which is called every main-loop iteration.
|
||||||
|
*
|
||||||
|
* Design invariants:
|
||||||
|
* - No HAL_Delay anywhere.
|
||||||
|
* - All timing is deadline-based: deadline = HAL_GetTick() + N.
|
||||||
|
* - RX via KLine_Ring_Available() / KLine_Ring_Pop().
|
||||||
|
* - TX via KLine_FSM_TxByte() (synchronous, safe at 9600 baud).
|
||||||
|
* - Packet counter via KLine_FSM_GetNextCounter() (shared session).
|
||||||
|
* - Keep-alive suspended while an op runs (KLine_FSM_SuspendKA).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kline_operations.h"
|
||||||
|
#include "kline_fsm.h"
|
||||||
|
#include "kline_ring.h"
|
||||||
|
#include "IKW1281Connection.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Timing constants */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
#define OP_BYTE_TIMEOUT_MS 1000u
|
||||||
|
#define OP_INTERBYTE_TX_MS 5u
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Internal operation types */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
OPTYPE_NONE = 0,
|
||||||
|
OPTYPE_READ_DFI,
|
||||||
|
OPTYPE_WRITE_DFI,
|
||||||
|
OPTYPE_READ_VOLTAGE,
|
||||||
|
OPTYPE_READ_AUDI_PIN,
|
||||||
|
OPTYPE_CLEAR_FAULT_CODES,
|
||||||
|
} OpType;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Sub-step states for the generic send/receive packet engine */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/* Idle / done */
|
||||||
|
OPS_IDLE,
|
||||||
|
|
||||||
|
/* Send packet bytes one at a time, wait for complement ACK */
|
||||||
|
OPS_SEND_BYTE,
|
||||||
|
OPS_SEND_WAIT_ACK,
|
||||||
|
OPS_SEND_INTERBYTE_DELAY,
|
||||||
|
OPS_SEND_END_BYTE,
|
||||||
|
|
||||||
|
/* Receive a packet byte by byte */
|
||||||
|
OPS_RECV_LENGTH,
|
||||||
|
OPS_RECV_COUNTER,
|
||||||
|
OPS_RECV_COMMAND,
|
||||||
|
OPS_RECV_BODY,
|
||||||
|
OPS_RECV_END,
|
||||||
|
|
||||||
|
/* After receiving a non-ACK/NAK packet, send ACK then receive more */
|
||||||
|
OPS_RECV_SEND_ACK,
|
||||||
|
|
||||||
|
/* High-level sequencer decides what comes next */
|
||||||
|
OPS_STEP_DONE,
|
||||||
|
|
||||||
|
} OpSubState;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Operation context */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
#define OP_MAX_SEND 24
|
||||||
|
#define OP_MAX_RECV_RAW 20
|
||||||
|
#define OP_MAX_RECV_PKTS 8
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* What operation */
|
||||||
|
OpType type;
|
||||||
|
KLineOp_Status result;
|
||||||
|
|
||||||
|
/* Sub-state */
|
||||||
|
OpSubState sub;
|
||||||
|
uint32_t deadline;
|
||||||
|
|
||||||
|
/* Send buffer */
|
||||||
|
uint8_t send_buf[OP_MAX_SEND];
|
||||||
|
uint8_t send_len;
|
||||||
|
uint8_t send_idx;
|
||||||
|
uint8_t last_sent;
|
||||||
|
|
||||||
|
/* Receive scratch */
|
||||||
|
ParsedPacket cur_pkt;
|
||||||
|
uint8_t cur_body_idx;
|
||||||
|
|
||||||
|
/* Completed receive packets for current burst */
|
||||||
|
ParsedPacket rx_pkts[OP_MAX_RECV_PKTS];
|
||||||
|
int rx_pkt_count;
|
||||||
|
|
||||||
|
/* High-level step index (each op defines its own sequence) */
|
||||||
|
int step;
|
||||||
|
|
||||||
|
/* Operation-specific params/results */
|
||||||
|
float dfi_value;
|
||||||
|
int dfi_version;
|
||||||
|
uint16_t u16_result; /* voltage or audi pin */
|
||||||
|
int int_result; /* WriteDfi / ClearFC success */
|
||||||
|
|
||||||
|
} OpCtx;
|
||||||
|
|
||||||
|
static OpCtx op;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Packet classification (same logic as kline_fsm.c) */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void op_classify_packet(ParsedPacket *p)
|
||||||
|
{
|
||||||
|
p->title = p->raw[2];
|
||||||
|
switch (p->raw[2]) {
|
||||||
|
case PACKET_CMD_ACK:
|
||||||
|
p->type = PACKET_TYPE_ACK;
|
||||||
|
p->isAckNak = 1;
|
||||||
|
break;
|
||||||
|
case PACKET_CMD_NAK:
|
||||||
|
p->type = PACKET_TYPE_NAK;
|
||||||
|
p->isAckNak = 1;
|
||||||
|
break;
|
||||||
|
case PACKET_CMD_AsciiData:
|
||||||
|
p->type = (p->raw[3] == 0x00) ? PACKET_TYPE_CODING_WSC
|
||||||
|
: PACKET_TYPE_ASCII_DATA;
|
||||||
|
break;
|
||||||
|
case PACKET_CMD_ReadEepromResponse:
|
||||||
|
p->type = PACKET_TYPE_READ_EEPROM_RESPONSE;
|
||||||
|
break;
|
||||||
|
case PACKET_CMD_ReadRomEepromResponse:
|
||||||
|
p->type = PACKET_TYPE_READ_ROM_EEPROM_RESPONSE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
p->type = PACKET_TYPE_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Build a KWP1281 packet into op.send_buf */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void op_build_packet(const uint8_t *payload, uint8_t payload_len)
|
||||||
|
{
|
||||||
|
op.send_len = payload_len + 2; /* length + counter + payload */
|
||||||
|
op.send_buf[0] = op.send_len;
|
||||||
|
op.send_buf[1] = KLine_FSM_GetNextCounter();
|
||||||
|
for (uint8_t i = 0; i < payload_len; i++)
|
||||||
|
op.send_buf[2 + i] = payload[i];
|
||||||
|
op.send_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Enter send / receive sub-states */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void op_enter_send(const uint8_t *payload, uint8_t len)
|
||||||
|
{
|
||||||
|
op_build_packet(payload, len);
|
||||||
|
op.sub = OPS_SEND_BYTE;
|
||||||
|
op.deadline = HAL_GetTick() + OP_BYTE_TIMEOUT_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void op_enter_recv(void)
|
||||||
|
{
|
||||||
|
op.rx_pkt_count = 0;
|
||||||
|
memset(&op.cur_pkt, 0, sizeof(op.cur_pkt));
|
||||||
|
op.cur_body_idx = 0;
|
||||||
|
op.sub = OPS_RECV_LENGTH;
|
||||||
|
op.deadline = HAL_GetTick() + OP_BYTE_TIMEOUT_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Byte-level RX helpers */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/* Read a byte and send complement ACK. Returns 1 if got byte. */
|
||||||
|
static uint8_t op_try_read_ack(uint8_t *out)
|
||||||
|
{
|
||||||
|
if (!KLine_Ring_Available()) {
|
||||||
|
if ((int32_t)(HAL_GetTick() - op.deadline) >= 0) {
|
||||||
|
op.result = KLOP_FAIL;
|
||||||
|
op.sub = OPS_IDLE;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*out = KLine_Ring_Pop();
|
||||||
|
KLine_FSM_TxByte((uint8_t)~(*out));
|
||||||
|
op.deadline = HAL_GetTick() + OP_BYTE_TIMEOUT_MS;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read a byte with no ACK (for end byte). */
|
||||||
|
static uint8_t op_try_read_raw(uint8_t *out)
|
||||||
|
{
|
||||||
|
if (!KLine_Ring_Available()) {
|
||||||
|
if ((int32_t)(HAL_GetTick() - op.deadline) >= 0) {
|
||||||
|
op.result = KLOP_FAIL;
|
||||||
|
op.sub = OPS_IDLE;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*out = KLine_Ring_Pop();
|
||||||
|
op.deadline = HAL_GetTick() + OP_BYTE_TIMEOUT_MS;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Send sub-machine */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void op_run_send(void)
|
||||||
|
{
|
||||||
|
switch (op.sub)
|
||||||
|
{
|
||||||
|
case OPS_SEND_BYTE:
|
||||||
|
if (op.send_idx >= op.send_len) {
|
||||||
|
/* All data bytes sent — send end byte (no ACK expected) */
|
||||||
|
KLine_FSM_TxByte(PACKET_END_EXPECTED);
|
||||||
|
/* Packet fully transmitted */
|
||||||
|
op.sub = OPS_STEP_DONE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
op.last_sent = op.send_buf[op.send_idx++];
|
||||||
|
KLine_FSM_TxByte(op.last_sent);
|
||||||
|
op.deadline = HAL_GetTick() + OP_BYTE_TIMEOUT_MS;
|
||||||
|
op.sub = OPS_SEND_WAIT_ACK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPS_SEND_WAIT_ACK:
|
||||||
|
if (!KLine_Ring_Available()) {
|
||||||
|
if ((int32_t)(HAL_GetTick() - op.deadline) >= 0) {
|
||||||
|
op.result = KLOP_FAIL;
|
||||||
|
op.sub = OPS_IDLE;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(void)KLine_Ring_Pop(); /* consume complement ACK */
|
||||||
|
op.deadline = HAL_GetTick() + OP_INTERBYTE_TX_MS;
|
||||||
|
op.sub = OPS_SEND_INTERBYTE_DELAY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPS_SEND_INTERBYTE_DELAY:
|
||||||
|
if ((int32_t)(HAL_GetTick() - op.deadline) < 0) return;
|
||||||
|
op.sub = OPS_SEND_BYTE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Receive sub-machine */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void op_run_recv(void)
|
||||||
|
{
|
||||||
|
switch (op.sub)
|
||||||
|
{
|
||||||
|
case OPS_RECV_LENGTH:
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
if (!op_try_read_ack(&b)) return;
|
||||||
|
memset(&op.cur_pkt, 0, sizeof(op.cur_pkt));
|
||||||
|
op.cur_body_idx = 0;
|
||||||
|
op.cur_pkt.raw[0] = b;
|
||||||
|
op.sub = OPS_RECV_COUNTER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPS_RECV_COUNTER:
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
if (!op_try_read_ack(&b)) return;
|
||||||
|
op.cur_pkt.raw[1] = b;
|
||||||
|
op.sub = OPS_RECV_COMMAND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPS_RECV_COMMAND:
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
if (!op_try_read_ack(&b)) return;
|
||||||
|
op.cur_pkt.raw[2] = b;
|
||||||
|
op.cur_body_idx = 3;
|
||||||
|
if (op.cur_pkt.raw[0] <= 3) {
|
||||||
|
op.sub = OPS_RECV_END;
|
||||||
|
} else {
|
||||||
|
op.sub = OPS_RECV_BODY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPS_RECV_BODY:
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
if (!op_try_read_ack(&b)) return;
|
||||||
|
if (op.cur_body_idx < OP_MAX_RECV_RAW)
|
||||||
|
op.cur_pkt.raw[op.cur_body_idx] = b;
|
||||||
|
op.cur_body_idx++;
|
||||||
|
if (op.cur_body_idx >= op.cur_pkt.raw[0]) {
|
||||||
|
op.sub = OPS_RECV_END;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPS_RECV_END:
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
if (!op_try_read_raw(&b)) return;
|
||||||
|
op.cur_pkt.raw[op.cur_body_idx] = b;
|
||||||
|
op.cur_pkt.length = op.cur_body_idx + 1;
|
||||||
|
op_classify_packet(&op.cur_pkt);
|
||||||
|
|
||||||
|
if (op.rx_pkt_count < OP_MAX_RECV_PKTS)
|
||||||
|
op.rx_pkts[op.rx_pkt_count++] = op.cur_pkt;
|
||||||
|
|
||||||
|
if (op.cur_pkt.isAckNak) {
|
||||||
|
/* Burst complete */
|
||||||
|
op.sub = OPS_STEP_DONE;
|
||||||
|
} else {
|
||||||
|
/* More packets — need to send ACK then continue receiving */
|
||||||
|
op.sub = OPS_RECV_SEND_ACK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPS_RECV_SEND_ACK:
|
||||||
|
{
|
||||||
|
/* Build and send an ACK packet, then loop back to recv */
|
||||||
|
uint8_t ack_payload = (uint8_t)PACKET_CMD_ACK;
|
||||||
|
op_build_packet(&ack_payload, 1);
|
||||||
|
/* We'll send this packet then re-enter recv */
|
||||||
|
op.sub = OPS_SEND_BYTE;
|
||||||
|
/* Mark that after this send completes, we go back to receiving.
|
||||||
|
* We use a special trick: after send completes (OPS_STEP_DONE),
|
||||||
|
* the sequencer checks if we were mid-burst and loops back. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Helper: send a packet then receive the response burst */
|
||||||
|
/* Returns 1 when the full send+receive is done, 0 while busy. */
|
||||||
|
/* The sequencer uses op.step to track phases: */
|
||||||
|
/* step N → sending packet */
|
||||||
|
/* step N+1 → receiving response */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/* Send-then-receive sequencer states per step:
|
||||||
|
* phase 0 = start send
|
||||||
|
* phase 1 = finish send → start recv
|
||||||
|
* phase 2 = finish recv → done
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
PHASE_SEND_START,
|
||||||
|
PHASE_SENDING,
|
||||||
|
PHASE_RECV_START,
|
||||||
|
PHASE_RECEIVING,
|
||||||
|
PHASE_COMPLETE,
|
||||||
|
} SendRecvPhase;
|
||||||
|
|
||||||
|
static SendRecvPhase sr_phase;
|
||||||
|
static uint8_t sr_was_mid_burst; /* flag: we were sending an ACK mid-burst */
|
||||||
|
|
||||||
|
static void sr_begin_send(const uint8_t *payload, uint8_t len)
|
||||||
|
{
|
||||||
|
op_enter_send(payload, len);
|
||||||
|
sr_phase = PHASE_SENDING;
|
||||||
|
sr_was_mid_burst = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sr_begin_send_then_recv(const uint8_t *payload, uint8_t len)
|
||||||
|
{
|
||||||
|
op_enter_send(payload, len);
|
||||||
|
sr_phase = PHASE_SENDING;
|
||||||
|
sr_was_mid_burst = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns 1 when the send-then-receive cycle is complete */
|
||||||
|
static uint8_t sr_poll(void)
|
||||||
|
{
|
||||||
|
if (op.result == KLOP_FAIL) return 1; /* error abort */
|
||||||
|
|
||||||
|
switch (sr_phase)
|
||||||
|
{
|
||||||
|
case PHASE_SENDING:
|
||||||
|
if (op.sub == OPS_STEP_DONE) {
|
||||||
|
if (sr_was_mid_burst) {
|
||||||
|
/* We just finished sending an intra-burst ACK — go back to recv */
|
||||||
|
sr_was_mid_burst = 0;
|
||||||
|
op_enter_recv();
|
||||||
|
sr_phase = PHASE_RECEIVING;
|
||||||
|
/* But we keep the existing rx_pkt list intact — don't reset count */
|
||||||
|
/* Actually op_enter_recv resets count. We need a variant. */
|
||||||
|
/* Fix: manually set sub to recv length without resetting pkt list */
|
||||||
|
/* We'll inline it: */
|
||||||
|
memset(&op.cur_pkt, 0, sizeof(op.cur_pkt));
|
||||||
|
op.cur_body_idx = 0;
|
||||||
|
op.sub = OPS_RECV_LENGTH;
|
||||||
|
op.deadline = HAL_GetTick() + OP_BYTE_TIMEOUT_MS;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Normal send complete — start receiving */
|
||||||
|
op_enter_recv();
|
||||||
|
sr_phase = PHASE_RECEIVING;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Still sending */
|
||||||
|
if (op.sub >= OPS_SEND_BYTE && op.sub <= OPS_SEND_INTERBYTE_DELAY) {
|
||||||
|
op_run_send();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case PHASE_RECEIVING:
|
||||||
|
if (op.sub == OPS_STEP_DONE) { //AQUI ACABA DE ENVIAR EL MENSAJE
|
||||||
|
/* Burst complete */
|
||||||
|
sr_phase = PHASE_COMPLETE;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (op.sub == OPS_RECV_SEND_ACK) {
|
||||||
|
/* Need to send ACK mid-burst, then continue recv */
|
||||||
|
uint8_t ack_payload = (uint8_t)PACKET_CMD_ACK;
|
||||||
|
op_build_packet(&ack_payload, 1);
|
||||||
|
op.sub = OPS_SEND_BYTE;
|
||||||
|
op.deadline = HAL_GetTick() + OP_BYTE_TIMEOUT_MS;
|
||||||
|
sr_was_mid_burst = 1;
|
||||||
|
sr_phase = PHASE_SENDING;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (op.sub >= OPS_RECV_LENGTH && op.sub <= OPS_RECV_END) {
|
||||||
|
op_run_recv();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case PHASE_COMPLETE:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Send-only sequencer (no receive expected, or we handle it inline) */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void send_only_begin(const uint8_t *payload, uint8_t len)
|
||||||
|
{
|
||||||
|
op_enter_send(payload, len);
|
||||||
|
sr_phase = PHASE_SENDING;
|
||||||
|
sr_was_mid_burst = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t send_only_poll(void)
|
||||||
|
{
|
||||||
|
if (op.result == KLOP_FAIL) return 1;
|
||||||
|
if (op.sub == OPS_STEP_DONE) return 1;
|
||||||
|
if (op.sub >= OPS_SEND_BYTE && op.sub <= OPS_SEND_INTERBYTE_DELAY) {
|
||||||
|
op_run_send();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Helper: find first non-ACK/NAK packet in rx list */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static ParsedPacket* op_find_data_packet(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < op.rx_pkt_count; i++) {
|
||||||
|
if (!op.rx_pkts[i].isAckNak)
|
||||||
|
return &op.rx_pkts[i];
|
||||||
|
}
|
||||||
|
return (ParsedPacket*)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Common init/finish for all operations */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static int op_begin(OpType t)
|
||||||
|
{
|
||||||
|
if (op.type != OPTYPE_NONE) return -1; /* already busy */
|
||||||
|
memset(&op, 0, sizeof(op));
|
||||||
|
op.type = t;
|
||||||
|
op.result = KLOP_BUSY;
|
||||||
|
op.sub = OPS_IDLE;
|
||||||
|
op.step = 0;
|
||||||
|
sr_phase = PHASE_SEND_START;
|
||||||
|
KLine_FSM_SuspendKA(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void op_finish(KLineOp_Status s)
|
||||||
|
{
|
||||||
|
op.result = s;
|
||||||
|
op.type = OPTYPE_NONE;
|
||||||
|
op.sub = OPS_IDLE;
|
||||||
|
KLine_FSM_SuspendKA(0);
|
||||||
|
KLine_FSM_ResetKATimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================== */
|
||||||
|
/* ReadRomEeprom: send {0x03, count, addr_hi, addr_lo} → recv */
|
||||||
|
/* Extracts body bytes raw[3 .. length-2] */
|
||||||
|
/* ================================================================== */
|
||||||
|
|
||||||
|
/* Shared result buffer for ROM EEPROM reads */
|
||||||
|
static uint8_t rom_eeprom_data[16];
|
||||||
|
static int rom_eeprom_len;
|
||||||
|
|
||||||
|
/* ================================================================== */
|
||||||
|
/* Operation: ReadVoltage */
|
||||||
|
/* ReadRomEeprom(0x0142, 2) → (data[1]<<8)|data[0] */
|
||||||
|
/* ================================================================== */
|
||||||
|
|
||||||
|
static void poll_read_voltage(void)
|
||||||
|
{
|
||||||
|
switch (op.step)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
uint8_t req[4] = {
|
||||||
|
(uint8_t)PACKET_CMD_ReadRomEeprom,
|
||||||
|
2, /* count */
|
||||||
|
(uint8_t)(0x0142 >> 8), /* addr hi */
|
||||||
|
(uint8_t)(0x0142 & 0xFF) /* addr lo */
|
||||||
|
};
|
||||||
|
sr_begin_send_then_recv(req, 4);
|
||||||
|
op.step = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
if (!sr_poll()) return;
|
||||||
|
if (op.result == KLOP_FAIL) {op_finish(KLOP_FAIL); return; }
|
||||||
|
{
|
||||||
|
/* Check for NAK */
|
||||||
|
if (op.rx_pkt_count == 1 && op.rx_pkts[0].type == PACKET_TYPE_NAK) {
|
||||||
|
op_finish(KLOP_FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ParsedPacket *p = op_find_data_packet();
|
||||||
|
if (!p || p->length < 5) {
|
||||||
|
op_finish(KLOP_FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int data_len = p->length - 4;
|
||||||
|
if (data_len >= 2) {
|
||||||
|
op.u16_result = (uint16_t)((p->raw[4] << 8) | p->raw[3]);
|
||||||
|
} else {
|
||||||
|
op_finish(KLOP_FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
op_finish(KLOP_OK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================== */
|
||||||
|
/* Operation: ReadAudiPin */
|
||||||
|
/* ReadRomEeprom(0x04FA, 2) → (data[1]<<8)|data[0] */
|
||||||
|
/* ================================================================== */
|
||||||
|
|
||||||
|
static void poll_read_audi_pin(void)
|
||||||
|
{
|
||||||
|
switch (op.step)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
uint8_t req[4] = {
|
||||||
|
(uint8_t)PACKET_CMD_ReadRomEeprom,
|
||||||
|
2,
|
||||||
|
(uint8_t)(0x04FA >> 8),
|
||||||
|
(uint8_t)(0x04FA & 0xFF)
|
||||||
|
};
|
||||||
|
sr_begin_send_then_recv(req, 4);
|
||||||
|
op.step = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
if (!sr_poll()) return;
|
||||||
|
if (op.result == KLOP_FAIL) { op_finish(KLOP_FAIL); return; }
|
||||||
|
{
|
||||||
|
if (op.rx_pkt_count == 1 && op.rx_pkts[0].type == PACKET_TYPE_NAK) {
|
||||||
|
op_finish(KLOP_FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ParsedPacket *p = op_find_data_packet();
|
||||||
|
if (!p || p->length < 5) {
|
||||||
|
op_finish(KLOP_FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int data_len = p->length - 4;
|
||||||
|
if (data_len >= 2) {
|
||||||
|
op.u16_result = (uint16_t)((p->raw[4] << 8) | p->raw[3]);
|
||||||
|
} else {
|
||||||
|
op_finish(KLOP_FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
op_finish(KLOP_OK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================== */
|
||||||
|
/* Operation: ClearFaultCodes */
|
||||||
|
/* Send {0x05}, expect ACK or NAK back */
|
||||||
|
/* ================================================================== */
|
||||||
|
|
||||||
|
static void poll_clear_fault_codes(void)
|
||||||
|
{
|
||||||
|
switch (op.step)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
uint8_t req[1] = { (uint8_t)PACKET_CMD_FaultCodesDelete };
|
||||||
|
sr_begin_send_then_recv(req, 1);
|
||||||
|
op.step = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
if (!sr_poll()) return;
|
||||||
|
if (op.result == KLOP_FAIL) { op_finish(KLOP_FAIL); return; }
|
||||||
|
{
|
||||||
|
if (op.rx_pkt_count >= 1 && op.rx_pkts[0].type == PACKET_TYPE_ACK) {
|
||||||
|
op.int_result = 1;
|
||||||
|
op_finish(KLOP_OK);
|
||||||
|
} else {
|
||||||
|
op.int_result = 0;
|
||||||
|
op_finish(KLOP_FAIL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================== */
|
||||||
|
/* Operation: ReadDfi */
|
||||||
|
/* Step 0: send group reading unlock {0x18,0x00,0x03,0xFF,0xFF} */
|
||||||
|
/* Step 1: recv response */
|
||||||
|
/* Step 2: send ReadEeprom {0x19,0x02,0x00,0x44} */
|
||||||
|
/* Step 3: recv response → extract DFI */
|
||||||
|
/* ================================================================== */
|
||||||
|
|
||||||
|
static void poll_read_dfi(void)
|
||||||
|
{
|
||||||
|
switch (op.step)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
/* Group reading unlock */
|
||||||
|
uint8_t req[5] = { 0x18, 0x00, 0x03, 0xFF, 0xFF };
|
||||||
|
sr_begin_send_then_recv(req, 5);
|
||||||
|
op.step = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
if (!sr_poll()) return;
|
||||||
|
if (op.result == KLOP_FAIL) { op_finish(KLOP_FAIL); return; }
|
||||||
|
/* Don't care about the unlock response content — move on */
|
||||||
|
op.step = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
/* ReadEeprom at 0x0044 */
|
||||||
|
uint8_t req[4] = {
|
||||||
|
(uint8_t)PACKET_CMD_ReadEeprom,
|
||||||
|
0x02, 0x00, 0x44
|
||||||
|
};
|
||||||
|
sr_begin_send_then_recv(req, 4);
|
||||||
|
op.step = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
if (!sr_poll()) return;
|
||||||
|
if (op.result == KLOP_FAIL) { op_finish(KLOP_FAIL); return; }
|
||||||
|
{
|
||||||
|
ParsedPacket *p = op_find_data_packet();
|
||||||
|
if (!p || p->length < 5) {
|
||||||
|
op.dfi_value = 0.0f;
|
||||||
|
op_finish(KLOP_FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* DFI = (int8_t)raw[3] * 3.0 / 256.0 */
|
||||||
|
int8_t raw_val = (int8_t)p->raw[3];
|
||||||
|
op.dfi_value = ((float)raw_val * 3.0f) / 256.0f;
|
||||||
|
}
|
||||||
|
op_finish(KLOP_OK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================== */
|
||||||
|
/* Operation: WriteDfi */
|
||||||
|
/* Step 0: send version-dependent password (14 bytes) */
|
||||||
|
/* Step 1: recv response */
|
||||||
|
/* Step 2: send WriteEeprom {0x1A,0x02,0x00,0x44,val,~val,0x03} */
|
||||||
|
/* Step 3: recv response */
|
||||||
|
/* ================================================================== */
|
||||||
|
|
||||||
|
static const uint8_t pass_default[14] = {
|
||||||
|
0x18, 0x00, 0x03, 0x2F, 0xFF,
|
||||||
|
0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45
|
||||||
|
};
|
||||||
|
static const uint8_t pass_v2[14] = {
|
||||||
|
0x18, 0x00, 0x03, 0x2F, 0xF2,
|
||||||
|
0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45
|
||||||
|
};
|
||||||
|
static const uint8_t pass_v3v4[14] = {
|
||||||
|
0x18, 0x00, 0x03, 0xFF, 0xF2,
|
||||||
|
0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45
|
||||||
|
};
|
||||||
|
|
||||||
|
static void poll_write_dfi(void)
|
||||||
|
{
|
||||||
|
switch (op.step)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
/* Select password based on version */
|
||||||
|
const uint8_t *password = pass_default;
|
||||||
|
if (op.dfi_version == 1) {
|
||||||
|
password = pass_v2;
|
||||||
|
} else if (op.dfi_version == 2 || op.dfi_version == 3) {
|
||||||
|
password = pass_v3v4;
|
||||||
|
}
|
||||||
|
sr_begin_send_then_recv(password, 14);
|
||||||
|
op.step = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
if (!sr_poll()) return;
|
||||||
|
if (op.result == KLOP_FAIL) { op_finish(KLOP_FAIL); return; }
|
||||||
|
/* Password response received — move on to write */
|
||||||
|
op.step = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
/* Compute value byte */
|
||||||
|
int vi = (int)((op.dfi_value * 256.0f) / 3.0f);
|
||||||
|
if (vi > 127) vi = 127;
|
||||||
|
if (vi < -128) vi = -128;
|
||||||
|
int8_t value = (int8_t)vi;
|
||||||
|
if (value == 0) value = 1;
|
||||||
|
|
||||||
|
uint8_t value_u = (uint8_t)value;
|
||||||
|
uint8_t value_csum = (uint8_t)(0u - value_u);
|
||||||
|
|
||||||
|
uint8_t req[7] = {
|
||||||
|
(uint8_t)PACKET_CMD_WriteEeprom,
|
||||||
|
0x02, 0x00, 0x44,
|
||||||
|
value_u, value_csum, 0x03
|
||||||
|
};
|
||||||
|
sr_begin_send_then_recv(req, 7);
|
||||||
|
op.step = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
if (!sr_poll()) return;
|
||||||
|
if (op.result == KLOP_FAIL) {
|
||||||
|
op.int_result = 0;
|
||||||
|
op_finish(KLOP_FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Check for NAK */
|
||||||
|
if (op.rx_pkt_count >= 1 && op.rx_pkts[0].type == PACKET_TYPE_NAK) {
|
||||||
|
op.int_result = 0;
|
||||||
|
op_finish(KLOP_FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
op.int_result = 1;
|
||||||
|
op_finish(KLOP_OK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================== */
|
||||||
|
/* Public API: Start functions */
|
||||||
|
/* ================================================================== */
|
||||||
|
|
||||||
|
int KLineOp_Start_ReadDfi(void)
|
||||||
|
{
|
||||||
|
return op_begin(OPTYPE_READ_DFI);
|
||||||
|
}
|
||||||
|
|
||||||
|
int KLineOp_Start_WriteDfi(float dfi, int version)
|
||||||
|
{
|
||||||
|
if (op_begin(OPTYPE_WRITE_DFI) != 0) return -1;
|
||||||
|
op.dfi_value = dfi;
|
||||||
|
op.dfi_version = version;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int KLineOp_Start_ReadVoltage(void)
|
||||||
|
{
|
||||||
|
return op_begin(OPTYPE_READ_VOLTAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int KLineOp_Start_ReadAudiPin(void)
|
||||||
|
{
|
||||||
|
return op_begin(OPTYPE_READ_AUDI_PIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
int KLineOp_Start_ClearFaultCodes(void)
|
||||||
|
{
|
||||||
|
return op_begin(OPTYPE_CLEAR_FAULT_CODES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================== */
|
||||||
|
/* Public API: Poll */
|
||||||
|
/* ================================================================== */
|
||||||
|
|
||||||
|
KLineOp_Status KLineOp_Poll(void)
|
||||||
|
{
|
||||||
|
if (op.result != KLOP_BUSY) return op.result;
|
||||||
|
|
||||||
|
switch (op.type)
|
||||||
|
{
|
||||||
|
case OPTYPE_READ_DFI: poll_read_dfi(); break;
|
||||||
|
case OPTYPE_WRITE_DFI: poll_write_dfi(); break;
|
||||||
|
case OPTYPE_READ_VOLTAGE: poll_read_voltage(); break;
|
||||||
|
case OPTYPE_READ_AUDI_PIN: poll_read_audi_pin(); break;
|
||||||
|
case OPTYPE_CLEAR_FAULT_CODES: poll_clear_fault_codes(); break;
|
||||||
|
default:
|
||||||
|
op.result = KLOP_IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return op.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================== */
|
||||||
|
/* Public API: Result accessors */
|
||||||
|
/* ================================================================== */
|
||||||
|
|
||||||
|
float KLineOp_Result_Dfi(void) { return op.dfi_value; }
|
||||||
|
uint16_t KLineOp_Result_Voltage(void) { return op.u16_result; }
|
||||||
|
uint16_t KLineOp_Result_AudiPin(void) { return op.u16_result; }
|
||||||
|
int KLineOp_Result_WriteDfi(void) { return op.int_result; }
|
||||||
|
int KLineOp_Result_ClearFaultCodes(void) { return op.int_result; }
|
||||||
|
|
||||||
|
/* ================================================================== */
|
||||||
|
/* Legacy shim functions (called from hc_06.c) */
|
||||||
|
/* */
|
||||||
|
/* These are the OLD blocking API signatures. They now kick off the */
|
||||||
|
/* async operation and BLOCK by polling in a tight loop. This is a */
|
||||||
|
/* transitional shim so hc_06.c keeps working without changes. */
|
||||||
|
/* */
|
||||||
|
/* For a fully non-blocking architecture, hc_06.c should be updated */
|
||||||
|
/* to use KLineOp_Start_XXX() + KLineOp_Poll() directly. */
|
||||||
|
/* ================================================================== */
|
||||||
|
|
||||||
|
float ReadDfi(void)
|
||||||
|
{
|
||||||
|
if (KLineOp_Start_ReadDfi() != 0) return 0.0f;
|
||||||
|
KLineOp_Status s;
|
||||||
|
do { s = KLineOp_Poll(); } while (s == KLOP_BUSY);
|
||||||
|
return (s == KLOP_OK) ? KLineOp_Result_Dfi() : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WriteDfi(float dfi, int version)
|
||||||
|
{
|
||||||
|
if (KLineOp_Start_WriteDfi(dfi, version) != 0) return 0;
|
||||||
|
KLineOp_Status s;
|
||||||
|
do { s = KLineOp_Poll(); } while (s == KLOP_BUSY);
|
||||||
|
return (s == KLOP_OK) ? KLineOp_Result_WriteDfi() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ReadVoltage(uint16_t *volt)
|
||||||
|
{
|
||||||
|
if (volt) *volt = 0;
|
||||||
|
if (KLineOp_Start_ReadVoltage() != 0) return 0;
|
||||||
|
KLineOp_Status s;
|
||||||
|
do { s = KLineOp_Poll(); } while (s == KLOP_BUSY);
|
||||||
|
if (s == KLOP_OK) {
|
||||||
|
if (volt) *volt = KLineOp_Result_Voltage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ReadAudiPin(uint16_t *pin)
|
||||||
|
{
|
||||||
|
if (pin) *pin = 0;
|
||||||
|
if (KLineOp_Start_ReadAudiPin() != 0) return 0;
|
||||||
|
KLineOp_Status s;
|
||||||
|
do { s = KLineOp_Poll(); } while (s == KLOP_BUSY);
|
||||||
|
if (s == KLOP_OK) {
|
||||||
|
if (pin) *pin = KLineOp_Result_AudiPin();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ClearFaultCodes(void)
|
||||||
|
{
|
||||||
|
if (KLineOp_Start_ClearFaultCodes() != 0) return 0;
|
||||||
|
KLineOp_Status s;
|
||||||
|
do { s = KLineOp_Poll(); } while (s == KLOP_BUSY);
|
||||||
|
return (s == KLOP_OK) ? KLineOp_Result_ClearFaultCodes() : 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* kline_operations.h
|
||||||
|
*
|
||||||
|
* Created on: 1 abr 2026
|
||||||
|
* Author: herli
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KLINE_MASTER_LIBS_KLINE_OPERATIONS_H_
|
||||||
|
#define KLINE_MASTER_LIBS_KLINE_OPERATIONS_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Operation result status */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
KLOP_IDLE = 0, /* no operation pending */
|
||||||
|
KLOP_BUSY = 1, /* operation in progress — keep polling */
|
||||||
|
KLOP_OK = 2, /* completed successfully */
|
||||||
|
KLOP_FAIL = 3, /* completed with failure (NAK, timeout, etc.) */
|
||||||
|
} KLineOp_Status;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Start functions — return 0 on success, -1 if already busy */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
int KLineOp_Start_ReadDfi(void);
|
||||||
|
int KLineOp_Start_WriteDfi(float dfi, int version);
|
||||||
|
int KLineOp_Start_ReadVoltage(void);
|
||||||
|
int KLineOp_Start_ReadAudiPin(void);
|
||||||
|
int KLineOp_Start_ClearFaultCodes(void);
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Poll — call every main-loop iteration */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
KLineOp_Status KLineOp_Poll(void);
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Result accessors (valid only after KLineOp_Poll() returns KLOP_OK) */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
float KLineOp_Result_Dfi(void);
|
||||||
|
uint16_t KLineOp_Result_Voltage(void);
|
||||||
|
uint16_t KLineOp_Result_AudiPin(void);
|
||||||
|
int KLineOp_Result_WriteDfi(void); /* 1=success, 0=fail */
|
||||||
|
int KLineOp_Result_ClearFaultCodes(void); /* 1=success, 0=fail */
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* KLINE_MASTER_LIBS_KLINE_OPERATIONS_H_ */
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* kline_ring.c
|
||||||
|
* ISR-safe ring buffer for K-Line UART1 RX.
|
||||||
|
*
|
||||||
|
* Producer: HAL_UART_RxCpltCallback (ISR context) via KLine_Ring_ISR_PushByte()
|
||||||
|
* Consumer: KLine_FSM_Run() (main-loop context)
|
||||||
|
*
|
||||||
|
* No locking needed: head written only by ISR, tail written only by main.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kline_ring.h"
|
||||||
|
#include "main.h" /* for huart1 */
|
||||||
|
|
||||||
|
volatile uint8_t kline_rx_staging = 0;
|
||||||
|
|
||||||
|
static volatile uint8_t ring_buf[KLINE_RING_SIZE];
|
||||||
|
static volatile uint32_t ring_head = 0; /* written by ISR */
|
||||||
|
static volatile uint32_t ring_tail = 0; /* written by main */
|
||||||
|
|
||||||
|
void KLine_Ring_Init(void)
|
||||||
|
{
|
||||||
|
ring_head = 0;
|
||||||
|
ring_tail = 0;
|
||||||
|
HAL_UART_Receive_IT(&huart1, (uint8_t*)&kline_rx_staging, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- ISR context ---- */
|
||||||
|
void KLine_Ring_ISR_PushByte(uint8_t b)
|
||||||
|
{
|
||||||
|
uint32_t next = (ring_head + 1u) & KLINE_RING_MASK;
|
||||||
|
if (next != ring_tail) /* drop byte on overflow rather than corrupt */
|
||||||
|
{
|
||||||
|
ring_buf[ring_head] = b;
|
||||||
|
ring_head = next;
|
||||||
|
}
|
||||||
|
/* Re-arm immediately – this is the ONLY place we call Receive_IT for UART1 */
|
||||||
|
HAL_UART_Receive_IT(&huart1, (uint8_t*)&kline_rx_staging, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Main-loop context ---- */
|
||||||
|
uint8_t KLine_Ring_Available(void)
|
||||||
|
{
|
||||||
|
return ring_head != ring_tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t KLine_Ring_Pop(void)
|
||||||
|
{
|
||||||
|
uint8_t b = ring_buf[ring_tail];
|
||||||
|
ring_tail = (ring_tail + 1u) & KLINE_RING_MASK;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KLine_Ring_Flush(void)
|
||||||
|
{
|
||||||
|
ring_tail = ring_head;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* kline_ring.h
|
||||||
|
* Lock-free single-producer (ISR) / single-consumer (FSM) ring buffer
|
||||||
|
* for UART1 K-Line RX bytes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INC_KLINE_RING_H_
|
||||||
|
#define INC_KLINE_RING_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define KLINE_RING_SIZE 64u /* Must be power of 2 */
|
||||||
|
#define KLINE_RING_MASK (KLINE_RING_SIZE - 1u)
|
||||||
|
|
||||||
|
/* Call once after HAL_UART_Init to start receiving */
|
||||||
|
void KLine_Ring_Init(void);
|
||||||
|
|
||||||
|
/* Called ONLY from UART1 RxCplt ISR callback */
|
||||||
|
void KLine_Ring_ISR_PushByte(uint8_t b);
|
||||||
|
|
||||||
|
/* Called from FSM / main context */
|
||||||
|
uint8_t KLine_Ring_Available(void); /* non-zero if at least one byte ready */
|
||||||
|
uint8_t KLine_Ring_Pop(void); /* pop oldest byte; undefined if empty */
|
||||||
|
void KLine_Ring_Flush(void); /* discard all pending bytes */
|
||||||
|
|
||||||
|
/* Raw RX staging byte – huart1 DMA/IT target, touched only by HAL + ISR */
|
||||||
|
extern volatile uint8_t kline_rx_staging;
|
||||||
|
|
||||||
|
#endif /* INC_KLINE_RING_H_ */
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
|
|
||||||
/* Private includes ----------------------------------------------------------*/
|
/* Private includes ----------------------------------------------------------*/
|
||||||
/* USER CODE BEGIN Includes */
|
/* USER CODE BEGIN Includes */
|
||||||
#include "kline.h"
|
#include "kline_ring.h"
|
||||||
#include "IKW1281Connection.h"
|
#include "kline_fsm.h"
|
||||||
#include "hc_06.h"
|
#include "hc_06.h"
|
||||||
/* USER CODE END Includes */
|
/* USER CODE END Includes */
|
||||||
|
|
||||||
@@ -49,7 +49,12 @@ UART_HandleTypeDef huart1;
|
|||||||
UART_HandleTypeDef huart3;
|
UART_HandleTypeDef huart3;
|
||||||
|
|
||||||
/* USER CODE BEGIN PV */
|
/* USER CODE BEGIN PV */
|
||||||
|
/*
|
||||||
|
* kline_rx_staging is the 1-byte HAL IT target for UART1.
|
||||||
|
* It lives here so the linker puts it in normal SRAM (not any cached/MPU region).
|
||||||
|
* It is only written by HAL inside the ISR and read by KLine_Ring_ISR_PushByte.
|
||||||
|
*/
|
||||||
|
/* defined in kline_ring.c: volatile uint8_t kline_rx_staging; */
|
||||||
/* USER CODE END PV */
|
/* USER CODE END PV */
|
||||||
|
|
||||||
/* Private function prototypes -----------------------------------------------*/
|
/* Private function prototypes -----------------------------------------------*/
|
||||||
@@ -57,72 +62,67 @@ void SystemClock_Config(void);
|
|||||||
static void MPU_Config(void);
|
static void MPU_Config(void);
|
||||||
static void MX_GPIO_Init(void);
|
static void MX_GPIO_Init(void);
|
||||||
static void MX_FDCAN1_Init(void);
|
static void MX_FDCAN1_Init(void);
|
||||||
static void MX_USART1_UART_Init(void);
|
void MX_USART1_UART_Init(void);
|
||||||
static void MX_USART3_UART_Init(void);
|
static void MX_USART3_UART_Init(void);
|
||||||
/* USER CODE BEGIN PFP */
|
/* USER CODE BEGIN PFP */
|
||||||
|
//void MX_USART1_UART_Init(void);
|
||||||
|
//SIEMPRE QUITAR EL STATIC
|
||||||
/* USER CODE END PFP */
|
/* USER CODE END PFP */
|
||||||
|
|
||||||
/* Private user code ---------------------------------------------------------*/
|
/* Private user code ---------------------------------------------------------*/
|
||||||
/* USER CODE BEGIN 0 */
|
/* USER CODE BEGIN 0 */
|
||||||
volatile uint8_t k_rx_byte;
|
|
||||||
volatile uint8_t rx_done_flag = 0;
|
|
||||||
|
|
||||||
ControllerInfo ecuinfo = {0};
|
|
||||||
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
|
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
|
||||||
{
|
{
|
||||||
if (huart->Instance == huart3.Instance)
|
if (huart->Instance == USART3)
|
||||||
{
|
{
|
||||||
HC06_UART_RxByteCallback(huart);
|
HC06_UART_RxByteCallback(huart);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (huart->Instance == USART1)
|
if (huart->Instance == USART1)
|
||||||
{
|
{
|
||||||
rx_done_flag = 1;
|
/*
|
||||||
// Do NOT re-arm here. ReadByte() does it.
|
* Push received byte into the ring buffer.
|
||||||
|
* KLine_Ring_ISR_PushByte() re-arms Receive_IT internally.
|
||||||
|
* DO NOT call HAL_UART_Receive_IT here directly.
|
||||||
|
*/
|
||||||
|
KLine_Ring_ISR_PushByte(kline_rx_staging);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
|
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
|
||||||
{
|
{
|
||||||
HC06_UART_TxCpltCallback(huart);
|
HC06_UART_TxCpltCallback(huart);
|
||||||
}
|
}
|
||||||
uint8_t pc_connected = 0;
|
|
||||||
uint8_t sent_init_message = 0;
|
|
||||||
|
|
||||||
uint8_t psg_connected = 0;
|
/* ---- KLine FSM callbacks (implement BT integration here) ---- */
|
||||||
|
|
||||||
void OnEnterReceived(void) {
|
|
||||||
//char msg[] = "Enter pressed!\r\n";
|
|
||||||
//CDC_Transmit_FS((uint8_t*)msg, strlen(msg));
|
|
||||||
BitBang = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t alive_due_ms =0;
|
|
||||||
|
|
||||||
void TryConnection(void){
|
|
||||||
BitBang = 0;
|
|
||||||
|
|
||||||
int protocolVersion = WakeUp(ECU_INIT_ADDRESS, 0);
|
|
||||||
|
|
||||||
if (!protocolVersion || !connectionAlive) return; // add connectionAlive check
|
|
||||||
|
|
||||||
ecuinfo = ReadEcuInfo();
|
|
||||||
|
|
||||||
if(!KeepAlive()){
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(4);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
IdentifyEcu();
|
|
||||||
|
|
||||||
/*FaultCode fault_codes[20]; // buffer for up to 32 codes (tune size as needed)
|
|
||||||
int N_fc = ReadFaultCodes(fault_codes, 20);
|
|
||||||
PrintFaultCodes(fault_codes, N_fc);*/
|
|
||||||
//free(fault_code)
|
|
||||||
psg_connected = 1;
|
|
||||||
alive_due_ms = HAL_GetTick() + 500;
|
|
||||||
|
|
||||||
|
void KLine_OnConnected(void)
|
||||||
|
{
|
||||||
BT_OnPSG5CommEstablished();
|
BT_OnPSG5CommEstablished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KLine_OnDisconnected(void)
|
||||||
|
{
|
||||||
|
BT_KLINE_ERROR();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KLine_OnKeepAliveTick(void)
|
||||||
|
{
|
||||||
|
BT_SendCommStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Button / trigger ---- */
|
||||||
|
|
||||||
|
void OnEnterReceived(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* User pressed button — request a KWP1281 connection.
|
||||||
|
* The FSM will handle everything from here, non-blocking.
|
||||||
|
*/
|
||||||
|
KLine_FSM_RequestConnect(ECU_INIT_ADDRESS);
|
||||||
|
}
|
||||||
|
int BitBang = 0;
|
||||||
/* USER CODE END 0 */
|
/* USER CODE END 0 */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -161,12 +161,17 @@ int main(void)
|
|||||||
MX_USART1_UART_Init();
|
MX_USART1_UART_Init();
|
||||||
MX_USART3_UART_Init();
|
MX_USART3_UART_Init();
|
||||||
/* USER CODE BEGIN 2 */
|
/* USER CODE BEGIN 2 */
|
||||||
//HAL_UART_Receive_IT(&huart1, &k_rx_byte, 1);
|
|
||||||
HAL_Delay(25); // Wait before starting UART (standard KWP wait time)
|
KLine_FSM_Init();
|
||||||
//int protocolVersion = WakeUp(ECU_INIT_ADDRESS, 0);
|
/* Note: KLine_Ring_Init() is called inside the FSM after the 5-baud
|
||||||
|
bitbang sequence restores UART1, so we do NOT call it here. */
|
||||||
|
|
||||||
|
HAL_Delay(25); /* Standard KWP power-on wait — only HAL_Delay in the project */
|
||||||
|
|
||||||
HC06_Init();
|
HC06_Init();
|
||||||
HC06_AT_BootSetup(); // sets NAME, then switches to BIN mode
|
HC06_AT_BootSetup();
|
||||||
|
|
||||||
|
|
||||||
/* USER CODE END 2 */
|
/* USER CODE END 2 */
|
||||||
|
|
||||||
/* Infinite loop */
|
/* Infinite loop */
|
||||||
@@ -176,26 +181,12 @@ int main(void)
|
|||||||
/* USER CODE END WHILE */
|
/* USER CODE END WHILE */
|
||||||
|
|
||||||
/* USER CODE BEGIN 3 */
|
/* USER CODE BEGIN 3 */
|
||||||
|
|
||||||
HC06_Process();
|
|
||||||
if(BitBang){
|
if(BitBang){
|
||||||
//HAL_GPIO_WritePin(KLINE_GPIO_PORT, GPIO_PIN_8, GPIO_PIN_RESET);
|
KLine_FSM_RequestConnect(ECU_INIT_ADDRESS);
|
||||||
TryConnection();
|
BitBang = 0;
|
||||||
//AQUI HARIA FALTA IMPLEMENTAR UN TIMER QUE CADA x ms haga un keep alive
|
|
||||||
//EndCommunication();
|
|
||||||
}
|
|
||||||
if(psg_connected){
|
|
||||||
if ((int32_t)(HAL_GetTick() - alive_due_ms) >= 0){
|
|
||||||
if(!KeepAlive()){
|
|
||||||
KLINE_THROW_NONALIVE_EXCEPTION(4);
|
|
||||||
psg_connected = 0;
|
|
||||||
//return;
|
|
||||||
}
|
|
||||||
BT_SendCommStatus();
|
|
||||||
alive_due_ms = HAL_GetTick() + 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
KLine_FSM_Run();
|
||||||
|
HC06_Process();
|
||||||
}
|
}
|
||||||
/* USER CODE END 3 */
|
/* USER CODE END 3 */
|
||||||
}
|
}
|
||||||
@@ -304,11 +295,11 @@ static void MX_FDCAN1_Init(void)
|
|||||||
* @param None
|
* @param None
|
||||||
* @retval None
|
* @retval None
|
||||||
*/
|
*/
|
||||||
static void MX_USART1_UART_Init(void)
|
void MX_USART1_UART_Init(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
/* USER CODE BEGIN USART1_Init 0 */
|
/* USER CODE BEGIN USART1_Init 0 */
|
||||||
|
//siempre quitar el static
|
||||||
/* USER CODE END USART1_Init 0 */
|
/* USER CODE END USART1_Init 0 */
|
||||||
|
|
||||||
/* USER CODE BEGIN USART1_Init 1 */
|
/* USER CODE BEGIN USART1_Init 1 */
|
||||||
|
|||||||
Reference in New Issue
Block a user