STM32G474RB firmware for solar buck converter with MPPT, CC control, Vfly compensation, and adaptive deadtime. Includes Textual TUI debug console for real-time telemetry, parameter tuning, and SQLite logging. Added pyproject.toml for uv: `cd code64 && uv run debug-console` Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
457 lines
16 KiB
C
457 lines
16 KiB
C
/*
|
|
* debug_protocol.c
|
|
*
|
|
* Created on: Mar 5, 2026
|
|
* Author: janik
|
|
*/
|
|
|
|
#include "debug_protocol.h"
|
|
#include "main.h"
|
|
#include "cc_controller.h"
|
|
#include "mppt.h"
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
/* ---- External variables from main.c ---- */
|
|
extern volatile float vin, iin, vout, iout, vfly, etemp;
|
|
extern volatile int16_t last_tmp;
|
|
extern volatile int16_t vfly_correction;
|
|
extern uint16_t VREF;
|
|
extern float vfly_integral;
|
|
extern volatile float vfly_avg_debug;
|
|
extern float vfly_kp, vfly_ki;
|
|
extern uint16_t vfly_clamp;
|
|
extern uint16_t vfly_loop_counter_trigger;
|
|
extern volatile uint8_t vfly_active;
|
|
|
|
extern CCController cc;
|
|
extern volatile float cc_target;
|
|
extern volatile int cc_active;
|
|
extern float cc_gain;
|
|
extern float CC_MIN_STEP, CC_MAX_STEP;
|
|
extern uint16_t cc_loop_counter_trigger;
|
|
|
|
extern MPPTController mppt;
|
|
extern volatile int mppt_active;
|
|
extern uint16_t mppt_loop_counter_trigger;
|
|
extern float mppt_initial_iref;
|
|
extern float mppt_step;
|
|
extern float mppt_iref_min, mppt_iref_max;
|
|
extern float mppt_dv_threshold;
|
|
extern float mppt_deadband;
|
|
|
|
extern float vin_min_ctrl;
|
|
|
|
extern uint8_t dt_values[];
|
|
|
|
/* Raw ADC DMA buffers */
|
|
extern uint16_t DMA1BUF1; /* adc4: vbat */
|
|
extern uint16_t DMA1BUF2[3]; /* adc1: etemp, vin, iin */
|
|
extern uint16_t DMA1BUF3[2]; /* adc2: vfly, iout */
|
|
extern uint16_t DMA1BUF4; /* adc5: itemp */
|
|
|
|
extern COMP_HandleTypeDef hcomp1, hcomp3, hcomp4;
|
|
extern FMAC_HandleTypeDef hfmac;
|
|
|
|
/* ---- CRC8 table (poly 0x07) ---- */
|
|
static const uint8_t crc8_table[256] = {
|
|
0x00,0x07,0x0E,0x09,0x1C,0x1B,0x12,0x15,0x38,0x3F,0x36,0x31,0x24,0x23,0x2A,0x2D,
|
|
0x70,0x77,0x7E,0x79,0x6C,0x6B,0x62,0x65,0x48,0x4F,0x46,0x41,0x54,0x53,0x5A,0x5D,
|
|
0xE0,0xE7,0xEE,0xE9,0xFC,0xFB,0xF2,0xF5,0xD8,0xDF,0xD6,0xD1,0xC4,0xC3,0xCA,0xCD,
|
|
0x90,0x97,0x9E,0x99,0x8C,0x8B,0x82,0x85,0xA8,0xAF,0xA6,0xA1,0xB4,0xB3,0xBA,0xBD,
|
|
0xC7,0xC0,0xC9,0xCE,0xDB,0xDC,0xD5,0xD2,0xFF,0xF8,0xF1,0xF6,0xE3,0xE4,0xED,0xEA,
|
|
0xB7,0xB0,0xB9,0xBE,0xAB,0xAC,0xA5,0xA2,0x8F,0x88,0x81,0x86,0x93,0x94,0x9D,0x9A,
|
|
0x27,0x20,0x29,0x2E,0x3B,0x3C,0x35,0x32,0x1F,0x18,0x11,0x16,0x03,0x04,0x0D,0x0A,
|
|
0x57,0x50,0x59,0x5E,0x4B,0x4C,0x45,0x42,0x6F,0x68,0x61,0x66,0x73,0x74,0x7D,0x7A,
|
|
0x89,0x8E,0x87,0x80,0x95,0x92,0x9B,0x9C,0xB1,0xB6,0xBF,0xB8,0xAD,0xAA,0xA3,0xA4,
|
|
0xF9,0xFE,0xF7,0xF0,0xE5,0xE2,0xEB,0xEC,0xC1,0xC6,0xCF,0xC8,0xDD,0xDA,0xD3,0xD4,
|
|
0x69,0x6E,0x67,0x60,0x75,0x72,0x7B,0x7C,0x51,0x56,0x5F,0x58,0x4D,0x4A,0x43,0x44,
|
|
0x19,0x1E,0x17,0x10,0x05,0x02,0x0B,0x0C,0x21,0x26,0x2F,0x28,0x3D,0x3A,0x33,0x34,
|
|
0x4E,0x49,0x40,0x47,0x52,0x55,0x5C,0x5B,0x76,0x71,0x78,0x7F,0x6A,0x6D,0x64,0x63,
|
|
0x3E,0x39,0x30,0x37,0x22,0x25,0x2C,0x2B,0x06,0x01,0x08,0x0F,0x1A,0x1D,0x14,0x13,
|
|
0xAE,0xA9,0xA0,0xA7,0xB2,0xB5,0xBC,0xBB,0x96,0x91,0x98,0x9F,0x8A,0x8D,0x84,0x83,
|
|
0xDE,0xD9,0xD0,0xD7,0xC2,0xC5,0xCC,0xCB,0xE6,0xE1,0xE8,0xEF,0xFA,0xFD,0xF4,0xF3,
|
|
};
|
|
|
|
static uint8_t crc8(const uint8_t *data, uint16_t len)
|
|
{
|
|
uint8_t crc = 0x00;
|
|
for (uint16_t i = 0; i < len; i++)
|
|
crc = crc8_table[crc ^ data[i]];
|
|
return crc;
|
|
}
|
|
|
|
/* ---- Parameter table ---- */
|
|
#define PARAM_COUNT 27
|
|
|
|
static ParamEntry param_table[PARAM_COUNT];
|
|
|
|
static void param_table_init(void)
|
|
{
|
|
int i = 0;
|
|
/* Vfly params */
|
|
param_table[i++] = (ParamEntry){PID_VFLY_KP, PTYPE_FLOAT, &vfly_kp, -10.0f, 10.0f};
|
|
param_table[i++] = (ParamEntry){PID_VFLY_KI, PTYPE_FLOAT, &vfly_ki, -10.0f, 10.0f};
|
|
param_table[i++] = (ParamEntry){PID_VFLY_CLAMP, PTYPE_UINT16, &vfly_clamp, 0, 10000};
|
|
param_table[i++] = (ParamEntry){PID_VFLY_LOOP_COUNTER_TRIGGER, PTYPE_UINT16, &vfly_loop_counter_trigger, 1, 10000};
|
|
param_table[i++] = (ParamEntry){PID_VFLY_ACTIVE, PTYPE_UINT8, (void*)&vfly_active, 0, 1};
|
|
|
|
/* CC params */
|
|
param_table[i++] = (ParamEntry){PID_CC_TARGET, PTYPE_FLOAT, (void*)&cc_target, 0, 60000};
|
|
param_table[i++] = (ParamEntry){PID_CC_GAIN, PTYPE_FLOAT, &cc_gain, -1.0f, 1.0f};
|
|
param_table[i++] = (ParamEntry){PID_CC_MIN_STEP, PTYPE_FLOAT, &CC_MIN_STEP, -1000, 0};
|
|
param_table[i++] = (ParamEntry){PID_CC_MAX_STEP, PTYPE_FLOAT, &CC_MAX_STEP, 0, 1000};
|
|
param_table[i++] = (ParamEntry){PID_CC_LOOP_COUNTER_TRIGGER, PTYPE_UINT16, &cc_loop_counter_trigger, 1, 10000};
|
|
param_table[i++] = (ParamEntry){PID_CC_ACTIVE, PTYPE_INT32, (void*)&cc_active, 0, 1};
|
|
param_table[i++] = (ParamEntry){PID_VREF, PTYPE_UINT16, &VREF, 3100, 3700};
|
|
|
|
/* MPPT params */
|
|
param_table[i++] = (ParamEntry){PID_MPPT_STEP, PTYPE_FLOAT, &mppt_step, 0, 10000};
|
|
param_table[i++] = (ParamEntry){PID_MPPT_IREF_MIN, PTYPE_FLOAT, &mppt_iref_min, 0, 60000};
|
|
param_table[i++] = (ParamEntry){PID_MPPT_IREF_MAX, PTYPE_FLOAT, &mppt_iref_max, 0, 60000};
|
|
param_table[i++] = (ParamEntry){PID_MPPT_DV_THRESHOLD, PTYPE_FLOAT, &mppt_dv_threshold, 0, 10000};
|
|
param_table[i++] = (ParamEntry){PID_MPPT_LOOP_COUNTER_TRIGGER, PTYPE_UINT16, &mppt_loop_counter_trigger, 1, 10000};
|
|
param_table[i++] = (ParamEntry){PID_MPPT_ACTIVE, PTYPE_INT32, (void*)&mppt_active, 0, 1};
|
|
param_table[i++] = (ParamEntry){PID_MPPT_INITIAL_IREF, PTYPE_FLOAT, &mppt_initial_iref, 0, 60000};
|
|
param_table[i++] = (ParamEntry){PID_MPPT_DEADBAND, PTYPE_FLOAT, &mppt_deadband, 0, 1.0f};
|
|
|
|
/* Global */
|
|
param_table[i++] = (ParamEntry){PID_VIN_MIN_CTRL, PTYPE_FLOAT, &vin_min_ctrl, 0, 90000};
|
|
|
|
/* Deadtime segments */
|
|
param_table[i++] = (ParamEntry){PID_DT_SEG0, PTYPE_UINT8, &dt_values[0], DT_HARD_MIN, 200};
|
|
param_table[i++] = (ParamEntry){PID_DT_SEG1, PTYPE_UINT8, &dt_values[1], DT_HARD_MIN, 200};
|
|
param_table[i++] = (ParamEntry){PID_DT_SEG2, PTYPE_UINT8, &dt_values[2], DT_HARD_MIN, 200};
|
|
param_table[i++] = (ParamEntry){PID_DT_SEG3, PTYPE_UINT8, &dt_values[3], DT_HARD_MIN, 200};
|
|
param_table[i++] = (ParamEntry){PID_DT_SEG4, PTYPE_UINT8, &dt_values[4], DT_HARD_MIN, 200};
|
|
param_table[i++] = (ParamEntry){PID_DT_SEG5, PTYPE_UINT8, &dt_values[5], DT_HARD_MIN, 200};
|
|
}
|
|
|
|
/* ---- Protocol context ---- */
|
|
ProtoCtx proto;
|
|
|
|
/* ---- Internal helpers ---- */
|
|
|
|
static void start_rx(void)
|
|
{
|
|
HAL_UART_Receive_IT(proto.huart, &proto.rx_byte, 1);
|
|
}
|
|
|
|
static void uart_recover(void)
|
|
{
|
|
/* Clear error flags and reset UART state so TX/RX work again */
|
|
__HAL_UART_CLEAR_FLAG(proto.huart, UART_CLEAR_OREF | UART_CLEAR_NEF | UART_CLEAR_PEF | UART_CLEAR_FEF);
|
|
proto.huart->gState = HAL_UART_STATE_READY;
|
|
proto.huart->RxState = HAL_UART_STATE_READY;
|
|
proto.huart->ErrorCode = HAL_UART_ERROR_NONE;
|
|
/* Re-arm RX */
|
|
start_rx();
|
|
}
|
|
|
|
static void send_frame(uint8_t cmd, const uint8_t *payload, uint8_t len)
|
|
{
|
|
/* Pick the inactive buffer */
|
|
uint8_t buf_idx = proto.tx_active ^ 1;
|
|
uint8_t *buf = proto.tx_buf[buf_idx];
|
|
uint16_t total = PROTO_HEADER_SIZE + len + 1;
|
|
|
|
buf[0] = PROTO_SYNC_BYTE;
|
|
buf[1] = cmd;
|
|
buf[2] = len;
|
|
if (len > 0)
|
|
memcpy(&buf[3], payload, len);
|
|
buf[3 + len] = crc8(buf, 3 + len);
|
|
|
|
proto.tx_active = buf_idx;
|
|
if (HAL_UART_Transmit(proto.huart, buf, total, 5) != HAL_OK)
|
|
{
|
|
proto.uart_errors++;
|
|
uart_recover();
|
|
}
|
|
}
|
|
|
|
static ParamEntry* find_param(uint8_t id)
|
|
{
|
|
for (int i = 0; i < PARAM_COUNT; i++)
|
|
{
|
|
if (param_table[i].id == id)
|
|
return ¶m_table[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void apply_param_write(const ParamWritePayload *pw)
|
|
{
|
|
ParamEntry *p = find_param(pw->param_id);
|
|
if (!p) return;
|
|
|
|
/* Extract value and clamp */
|
|
float fval;
|
|
switch (p->type)
|
|
{
|
|
case PTYPE_FLOAT:
|
|
fval = pw->value.f;
|
|
if (fval < p->min_val) fval = p->min_val;
|
|
if (fval > p->max_val) fval = p->max_val;
|
|
*(float*)p->ptr = fval;
|
|
break;
|
|
case PTYPE_UINT16:
|
|
fval = (float)pw->value.u16;
|
|
if (fval < p->min_val) fval = p->min_val;
|
|
if (fval > p->max_val) fval = p->max_val;
|
|
*(uint16_t*)p->ptr = (uint16_t)fval;
|
|
break;
|
|
case PTYPE_UINT8:
|
|
fval = (float)pw->value.u8;
|
|
if (fval < p->min_val) fval = p->min_val;
|
|
if (fval > p->max_val) fval = p->max_val;
|
|
*(uint8_t*)p->ptr = (uint8_t)fval;
|
|
break;
|
|
case PTYPE_INT32:
|
|
fval = (float)pw->value.i32;
|
|
if (fval < p->min_val) fval = p->min_val;
|
|
if (fval > p->max_val) fval = p->max_val;
|
|
*(int32_t*)p->ptr = (int32_t)fval;
|
|
break;
|
|
}
|
|
|
|
/* Side effects: VREF update -> ADC3 offset + CC output */
|
|
if (pw->param_id == PID_VREF)
|
|
{
|
|
ADC3->OFR1 = (ADC3->OFR1 & ~0xFFFU) | (VREF & 0xFFF);
|
|
cc.output_f = (float)VREF;
|
|
}
|
|
/* Side effects for CC params: sync struct fields */
|
|
else if (pw->param_id == PID_CC_GAIN)
|
|
cc.gain = cc_gain;
|
|
else if (pw->param_id == PID_CC_MIN_STEP)
|
|
cc.min_step = CC_MIN_STEP;
|
|
else if (pw->param_id == PID_CC_MAX_STEP)
|
|
cc.max_step = CC_MAX_STEP;
|
|
/* Side effects for MPPT params: sync struct fields */
|
|
else if (pw->param_id == PID_MPPT_STEP)
|
|
mppt.step = mppt_step;
|
|
else if (pw->param_id == PID_MPPT_IREF_MIN)
|
|
mppt.iref_min = mppt_iref_min;
|
|
else if (pw->param_id == PID_MPPT_IREF_MAX)
|
|
mppt.iref_max = mppt_iref_max;
|
|
else if (pw->param_id == PID_MPPT_DV_THRESHOLD)
|
|
mppt.dv_min = mppt_dv_threshold;
|
|
else if (pw->param_id == PID_MPPT_DEADBAND)
|
|
mppt.deadband = mppt_deadband;
|
|
else if (pw->param_id == PID_MPPT_ACTIVE && mppt_active)
|
|
cc_active = 1;
|
|
|
|
/* Send ACK: echo back the write payload */
|
|
send_frame(CMD_PARAM_WRITE_ACK, (const uint8_t*)pw, sizeof(ParamWritePayload));
|
|
}
|
|
|
|
static void send_param_value(const ParamEntry *p)
|
|
{
|
|
ParamWritePayload pv;
|
|
pv.param_id = p->id;
|
|
pv.param_type = p->type;
|
|
pv._pad[0] = 0; pv._pad[1] = 0;
|
|
pv.value.u32 = 0;
|
|
switch (p->type)
|
|
{
|
|
case PTYPE_FLOAT: pv.value.f = *(float*)p->ptr; break;
|
|
case PTYPE_UINT16: pv.value.u16 = *(uint16_t*)p->ptr; break;
|
|
case PTYPE_UINT8: pv.value.u8 = *(uint8_t*)p->ptr; break;
|
|
case PTYPE_INT32: pv.value.i32 = *(int32_t*)p->ptr; break;
|
|
}
|
|
send_frame(CMD_PARAM_VALUE, (const uint8_t*)&pv, sizeof(ParamWritePayload));
|
|
}
|
|
|
|
static void send_all_params(void)
|
|
{
|
|
for (int i = 0; i < PARAM_COUNT; i++)
|
|
send_param_value(¶m_table[i]);
|
|
}
|
|
|
|
static void process_rx_frame(uint8_t cmd, const uint8_t *payload, uint8_t len)
|
|
{
|
|
switch (cmd)
|
|
{
|
|
case CMD_PARAM_WRITE:
|
|
if (len >= sizeof(ParamWritePayload))
|
|
apply_param_write((const ParamWritePayload*)payload);
|
|
break;
|
|
case CMD_PARAM_READ_ALL:
|
|
send_all_params();
|
|
break;
|
|
case CMD_PING:
|
|
send_frame(CMD_PONG, NULL, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* ---- Public API ---- */
|
|
|
|
void Proto_Init(UART_HandleTypeDef *huart)
|
|
{
|
|
memset(&proto, 0, sizeof(proto));
|
|
proto.huart = huart;
|
|
proto.rx_state = RX_WAIT_SYNC;
|
|
param_table_init();
|
|
start_rx();
|
|
}
|
|
|
|
void Proto_SendTelemetry(void)
|
|
{
|
|
TelemetryPayload t;
|
|
t.vin = vin;
|
|
t.vout = vout;
|
|
t.iin = iin;
|
|
t.iout = iout;
|
|
t.vfly = vfly;
|
|
t.etemp = etemp;
|
|
t.last_tmp = last_tmp;
|
|
t.VREF = VREF;
|
|
t.vfly_correction = vfly_correction;
|
|
t._pad0 = 0;
|
|
t.vfly_integral = vfly_integral;
|
|
t.vfly_avg_debug = vfly_avg_debug;
|
|
t.cc_output_f = cc.output_f;
|
|
t.mppt_iref = mppt.iref;
|
|
t.mppt_last_vin = mppt.last_vin;
|
|
t.mppt_last_iin = mppt.last_iin;
|
|
t.p_in = vin * (-iin) / 1e6f; /* mV * mA → W */
|
|
t.p_out = vout * iout / 1e6f; /* mV * mA → W */
|
|
t.seq = proto.seq++;
|
|
t._pad1[0] = 0; t._pad1[1] = 0; t._pad1[2] = 0;
|
|
|
|
send_frame(CMD_TELEMETRY, (const uint8_t*)&t, sizeof(TelemetryPayload));
|
|
}
|
|
|
|
void Proto_SendError(const char *msg)
|
|
{
|
|
uint8_t len = 0;
|
|
while (msg[len] && len < PROTO_MAX_PAYLOAD) len++;
|
|
send_frame(CMD_ERROR_MSG, (const uint8_t*)msg, len);
|
|
}
|
|
|
|
void Proto_SendDiagDump(const char *reason)
|
|
{
|
|
char buf[128];
|
|
|
|
/* Line 1: reason */
|
|
Proto_SendError(reason);
|
|
|
|
/* Line 2: converted analog values (integer-only — no float printf with nano.specs) */
|
|
snprintf(buf, sizeof(buf), "DIAG V:%lu/%lu I:%d/%lu Vfly:%lu T:%d.%u",
|
|
(unsigned long)(vin > 0 ? (uint32_t)vin : 0),
|
|
(unsigned long)(vout > 0 ? (uint32_t)vout : 0),
|
|
(int)iin,
|
|
(unsigned long)(iout > 0 ? (uint32_t)iout : 0),
|
|
(unsigned long)(vfly > 0 ? (uint32_t)vfly : 0),
|
|
(int)etemp, ((unsigned)(etemp > 0 ? etemp : -etemp) % 10));
|
|
Proto_SendError(buf);
|
|
|
|
/* Line 3: raw ADC counts */
|
|
snprintf(buf, sizeof(buf), "RAW adc1[%u,%u,%u] adc2[%u,%u] adc4[%u] adc5[%u]",
|
|
DMA1BUF2[0], DMA1BUF2[1], DMA1BUF2[2],
|
|
DMA1BUF3[0], DMA1BUF3[1],
|
|
DMA1BUF1, DMA1BUF4);
|
|
Proto_SendError(buf);
|
|
|
|
/* Line 4: controller state */
|
|
snprintf(buf, sizeof(buf), "CTRL VREF:%u last_tmp:%d vfly_corr:%d cc_out:%d mppt_iref:%d",
|
|
VREF, (int)last_tmp, (int)vfly_correction,
|
|
(int)cc.output_f, (int)mppt.iref);
|
|
Proto_SendError(buf);
|
|
|
|
/* Line 5: HRTIM fault status + comparator outputs */
|
|
uint32_t hrtim_isr = HRTIM1->sCommonRegs.ISR;
|
|
uint32_t hrtim_oenr = HRTIM1->sCommonRegs.OENR;
|
|
uint32_t comp_out = 0;
|
|
if (HAL_COMP_GetOutputLevel(&hcomp1) == COMP_OUTPUT_LEVEL_HIGH) comp_out |= 1;
|
|
if (HAL_COMP_GetOutputLevel(&hcomp3) == COMP_OUTPUT_LEVEL_HIGH) comp_out |= 2;
|
|
if (HAL_COMP_GetOutputLevel(&hcomp4) == COMP_OUTPUT_LEVEL_HIGH) comp_out |= 4;
|
|
snprintf(buf, sizeof(buf), "HW HRTIM_ISR:%08lX OENR:%08lX COMP:%lu FMAC_SR:%08lX",
|
|
hrtim_isr, hrtim_oenr, comp_out, FMAC->SR);
|
|
Proto_SendError(buf);
|
|
|
|
/* Line 6: HRTIM duty + master compare */
|
|
snprintf(buf, sizeof(buf), "HRTIM TE_CMP1:%lu TF_CMP1:%lu MCMP1:%lu",
|
|
HRTIM1->sTimerxRegs[HRTIM_TIMERINDEX_TIMER_E].CMP1xR,
|
|
HRTIM1->sTimerxRegs[HRTIM_TIMERINDEX_TIMER_F].CMP1xR,
|
|
HRTIM1->sMasterRegs.MCMP1R);
|
|
Proto_SendError(buf);
|
|
|
|
/* Line 7: active flags + ADC3 offset */
|
|
snprintf(buf, sizeof(buf), "FLAGS cc_act:%d mppt_act:%d vfly_act:%u ADC3_OFR1:%08lX",
|
|
cc_active, mppt_active, (unsigned)vfly_active, ADC3->OFR1);
|
|
Proto_SendError(buf);
|
|
}
|
|
|
|
/* ---- HAL Callbacks ---- */
|
|
|
|
void Proto_TxCpltCallback(UART_HandleTypeDef *huart)
|
|
{
|
|
if (huart == proto.huart)
|
|
proto.tx_busy = 0;
|
|
}
|
|
|
|
void Proto_ErrorCallback(UART_HandleTypeDef *huart)
|
|
{
|
|
if (huart != proto.huart)
|
|
return;
|
|
proto.uart_errors++;
|
|
uart_recover();
|
|
}
|
|
|
|
void Proto_RxCpltCallback(UART_HandleTypeDef *huart)
|
|
{
|
|
if (huart != proto.huart)
|
|
return;
|
|
|
|
uint8_t b = proto.rx_byte;
|
|
|
|
switch (proto.rx_state)
|
|
{
|
|
case RX_WAIT_SYNC:
|
|
if (b == PROTO_SYNC_BYTE)
|
|
{
|
|
proto.rx_buf[0] = b;
|
|
proto.rx_state = RX_WAIT_CMD;
|
|
}
|
|
break;
|
|
case RX_WAIT_CMD:
|
|
proto.rx_cmd = b;
|
|
proto.rx_buf[1] = b;
|
|
proto.rx_state = RX_WAIT_LEN;
|
|
break;
|
|
case RX_WAIT_LEN:
|
|
proto.rx_len = b;
|
|
proto.rx_buf[2] = b;
|
|
proto.rx_idx = 0;
|
|
if (b == 0)
|
|
proto.rx_state = RX_WAIT_CRC;
|
|
else if (b > PROTO_MAX_PAYLOAD)
|
|
proto.rx_state = RX_WAIT_SYNC; /* invalid, reset */
|
|
else
|
|
proto.rx_state = RX_WAIT_PAYLOAD;
|
|
break;
|
|
case RX_WAIT_PAYLOAD:
|
|
proto.rx_buf[PROTO_HEADER_SIZE + proto.rx_idx] = b;
|
|
proto.rx_idx++;
|
|
if (proto.rx_idx >= proto.rx_len)
|
|
proto.rx_state = RX_WAIT_CRC;
|
|
break;
|
|
case RX_WAIT_CRC:
|
|
{
|
|
uint8_t expected = crc8(proto.rx_buf, PROTO_HEADER_SIZE + proto.rx_len);
|
|
if (b == expected)
|
|
process_rx_frame(proto.rx_cmd, &proto.rx_buf[PROTO_HEADER_SIZE], proto.rx_len);
|
|
proto.rx_state = RX_WAIT_SYNC;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Re-arm RX */
|
|
start_rx();
|
|
}
|