File syn_gbt27930.c¶
File List > proto > syn_gbt27930.c
Go to the documentation of this file
#include "syn_gbt27930.h"
#if !defined(SYN_USE_GBT27930) || SYN_USE_GBT27930
#include <string.h>
void syn_gbt27930_init(SYN_GBT27930_Session *session, SYN_GBT27930_Role role)
{
if (session == NULL) {
return;
}
memset(session, 0, sizeof(*session));
session->role = role;
session->state = SYN_GBT27930_STATE_IDLE;
session->telemetry.charge_mode = 0x01; /* Default CV */
}
SYN_Status syn_gbt27930_start_handshake(SYN_GBT27930_Session *session)
{
if (session == NULL) {
return SYN_INVALID_PARAM;
}
session->state = SYN_GBT27930_STATE_HANDSHAKE;
session->timer_ms = 0;
session->timeout_ms = 0;
session->ready_for_charging = false;
session->peer_ready_for_charging = false;
return SYN_OK;
}
void syn_gbt27930_stop_charging(SYN_GBT27930_Session *session, uint8_t reason)
{
if (session == NULL) {
return;
}
session->state = SYN_GBT27930_STATE_STOPPING;
session->stop_reason = reason;
session->timer_ms = 0;
}
SYN_Status syn_gbt27930_process_rx_frame(SYN_GBT27930_Session *session, const SYN_CAN_Frame *frame)
{
if (session == NULL || frame == NULL || !frame->extended) {
return SYN_INVALID_PARAM;
}
SYN_J1939_Header hdr;
if (syn_j1939_id_unpack(frame->id, &hdr) != SYN_OK) {
return SYN_INVALID_PARAM; /* LCOV_EXCL_LINE: Unreachable guard */
}
uint32_t pgn = hdr.pgn;
/* Handle Received Frames by PGN */
switch (pgn) {
case SYN_GBT27930_PGN_CHM: /* Charger Handshake */
if (session->role == SYN_GBT27930_ROLE_BMS &&
session->state == SYN_GBT27930_STATE_HANDSHAKE) {
session->timeout_ms = 0;
}
break;
case SYN_GBT27930_PGN_BHM: /* BMS Handshake */
if (session->role == SYN_GBT27930_ROLE_CHARGER &&
session->state == SYN_GBT27930_STATE_HANDSHAKE) {
if (frame->dlc >= 2) {
session->bms_cfg.max_charge_volt_v =
(uint16_t)(frame->data[0] | ((uint16_t)frame->data[1] << 8));
session->timeout_ms = 0;
}
}
break;
case SYN_GBT27930_PGN_CRM: /* Charger Recognition */
if (session->role == SYN_GBT27930_ROLE_BMS &&
session->state == SYN_GBT27930_STATE_HANDSHAKE) {
if (frame->dlc >= 1 && frame->data[0] == 0x00) {
session->state = SYN_GBT27930_STATE_PARAM_CONFIG;
session->timeout_ms = 0;
}
}
break;
case SYN_GBT27930_PGN_BRM: /* BMS Recognition */
if (session->role == SYN_GBT27930_ROLE_CHARGER &&
session->state == SYN_GBT27930_STATE_HANDSHAKE) {
session->state = SYN_GBT27930_STATE_PARAM_CONFIG;
session->timeout_ms = 0;
}
break;
case SYN_GBT27930_PGN_BCP: /* BMS Charging Parameter */
if (session->role == SYN_GBT27930_ROLE_CHARGER &&
session->state == SYN_GBT27930_STATE_PARAM_CONFIG) {
if (frame->dlc >= 6) {
session->bms_cfg.max_charge_volt_v =
(uint16_t)(frame->data[0] | ((uint16_t)frame->data[1] << 8));
session->bms_cfg.max_charge_curr_a =
(uint16_t)(frame->data[2] | ((uint16_t)frame->data[3] << 8));
session->timeout_ms = 0;
}
}
break;
case SYN_GBT27930_PGN_CML: /* Charger Max Output */
if (session->role == SYN_GBT27930_ROLE_BMS &&
session->state == SYN_GBT27930_STATE_PARAM_CONFIG) {
if (frame->dlc >= 8) {
session->charger_cfg.max_output_volt_v =
(uint16_t)(frame->data[0] | ((uint16_t)frame->data[1] << 8));
session->charger_cfg.min_output_volt_v =
(uint16_t)(frame->data[2] | ((uint16_t)frame->data[3] << 8));
session->timeout_ms = 0;
}
}
break;
case SYN_GBT27930_PGN_BRO: /* BMS Ready State */
if (session->role == SYN_GBT27930_ROLE_CHARGER) {
if (frame->dlc >= 1 && frame->data[0] == 0xAA) {
session->peer_ready_for_charging = true;
session->timeout_ms = 0;
if (session->ready_for_charging && session->peer_ready_for_charging) {
session->state = SYN_GBT27930_STATE_CHARGING;
}
}
}
break;
case SYN_GBT27930_PGN_CRO: /* Charger Output Ready State */
if (session->role == SYN_GBT27930_ROLE_BMS) {
if (frame->dlc >= 1 && frame->data[0] == 0xAA) {
session->peer_ready_for_charging = true;
session->timeout_ms = 0;
if (session->ready_for_charging && session->peer_ready_for_charging) {
session->state = SYN_GBT27930_STATE_CHARGING;
}
}
}
break;
case SYN_GBT27930_PGN_BCL: /* BMS Charging Demand */
if (session->role == SYN_GBT27930_ROLE_CHARGER &&
session->state == SYN_GBT27930_STATE_CHARGING) {
if (frame->dlc >= 5) {
session->telemetry.volt_demand_v =
(uint16_t)(frame->data[0] | ((uint16_t)frame->data[1] << 8));
session->telemetry.curr_demand_a =
(uint16_t)(frame->data[2] | ((uint16_t)frame->data[3] << 8));
session->telemetry.charge_mode = frame->data[4];
session->timeout_ms = 0;
}
}
break;
case SYN_GBT27930_PGN_BCS: /* BMS Overall Status */
if (session->role == SYN_GBT27930_ROLE_CHARGER &&
session->state == SYN_GBT27930_STATE_CHARGING) {
if (frame->dlc >= 7) {
session->telemetry.measured_volt_v =
(uint16_t)(frame->data[0] | ((uint16_t)frame->data[1] << 8));
session->telemetry.measured_curr_a =
(uint16_t)(frame->data[2] | ((uint16_t)frame->data[3] << 8));
session->telemetry.soc_percent = frame->data[6];
session->timeout_ms = 0;
}
}
break;
case SYN_GBT27930_PGN_CCS: /* Charger Charging Status */
if (session->role == SYN_GBT27930_ROLE_BMS &&
session->state == SYN_GBT27930_STATE_CHARGING) {
if (frame->dlc >= 6) {
session->telemetry.measured_volt_v =
(uint16_t)(frame->data[0] | ((uint16_t)frame->data[1] << 8));
session->telemetry.measured_curr_a =
(uint16_t)(frame->data[2] | ((uint16_t)frame->data[3] << 8));
session->timeout_ms = 0;
}
}
break;
case SYN_GBT27930_PGN_BST: /* BMS Stop Charging */
case SYN_GBT27930_PGN_CST: /* Charger Stop Charging */
session->state = SYN_GBT27930_STATE_STOPPING;
session->timeout_ms = 0;
if (frame->dlc >= 1) {
session->stop_reason = frame->data[0];
}
break;
case SYN_GBT27930_PGN_BEM: /* BMS Error */
case SYN_GBT27930_PGN_CEM: /* Charger Error */
session->state = SYN_GBT27930_STATE_ERROR;
session->timeout_ms = 0;
if (frame->dlc >= 1) {
session->fault_code = frame->data[0];
}
break;
default:
break;
}
return SYN_OK;
}
bool syn_gbt27930_step(SYN_GBT27930_Session *session, uint32_t dt_ms, SYN_CAN_Frame *tx_frame)
{
if (session == NULL || tx_frame == NULL || session->state == SYN_GBT27930_STATE_IDLE) {
return false;
}
session->timer_ms += dt_ms;
session->timeout_ms += dt_ms;
/* Specification-mandated message timeout monitoring */
uint32_t timeout_limit_ms = (session->state == SYN_GBT27930_STATE_CHARGING) ? 1000 : 5000;
if (session->state != SYN_GBT27930_STATE_ERROR &&
session->state != SYN_GBT27930_STATE_STOPPING && session->timeout_ms >= timeout_limit_ms) {
session->state = SYN_GBT27930_STATE_ERROR;
session->fault_code = 0x01; /* Communication Timeout Fault */
}
/* Frame Transmission Intervals */
uint32_t interval_ms = 250;
if (session->state == SYN_GBT27930_STATE_CHARGING) {
interval_ms = 50; /* Fast 50ms periodic demand/status loop */
} else if (session->state == SYN_GBT27930_STATE_STOPPING) {
interval_ms = 10; /* Fast 10ms stop notification loop */
} else if (session->state == SYN_GBT27930_STATE_ERROR) {
interval_ms = 100; /* Periodic 100ms error broadcast loop */
}
if (session->timer_ms < interval_ms) {
return false;
}
session->timer_ms = 0;
memset(tx_frame, 0, sizeof(*tx_frame));
tx_frame->extended = true; /* Extended 29-bit CAN ID */
tx_frame->dlc = 8;
uint8_t sa = (session->role == SYN_GBT27930_ROLE_BMS) ? SYN_GBT27930_ADDR_BMS
: SYN_GBT27930_ADDR_CHARGER;
uint8_t da = (session->role == SYN_GBT27930_ROLE_BMS) ? SYN_GBT27930_ADDR_CHARGER
: SYN_GBT27930_ADDR_BMS;
if (session->role == SYN_GBT27930_ROLE_BMS) {
switch (session->state) {
case SYN_GBT27930_STATE_HANDSHAKE:
tx_frame->id = syn_j1939_id_pack(6, SYN_GBT27930_PGN_BHM, sa, da);
tx_frame->data[0] = (uint8_t)(session->bms_cfg.max_charge_volt_v & 0xFF);
tx_frame->data[1] = (uint8_t)((session->bms_cfg.max_charge_volt_v >> 8) & 0xFF);
return true;
case SYN_GBT27930_STATE_PARAM_CONFIG:
tx_frame->id = syn_j1939_id_pack(6, SYN_GBT27930_PGN_BRO, sa, da);
tx_frame->data[0] = session->ready_for_charging ? 0xAA : 0x00;
return true;
case SYN_GBT27930_STATE_CHARGING:
tx_frame->id = syn_j1939_id_pack(6, SYN_GBT27930_PGN_BCL, sa, da);
tx_frame->data[0] = (uint8_t)(session->telemetry.volt_demand_v & 0xFF);
tx_frame->data[1] = (uint8_t)((session->telemetry.volt_demand_v >> 8) & 0xFF);
tx_frame->data[2] = (uint8_t)(session->telemetry.curr_demand_a & 0xFF);
tx_frame->data[3] = (uint8_t)((session->telemetry.curr_demand_a >> 8) & 0xFF);
tx_frame->data[4] = session->telemetry.charge_mode;
return true;
case SYN_GBT27930_STATE_STOPPING:
tx_frame->id = syn_j1939_id_pack(4, SYN_GBT27930_PGN_BST, sa, da);
tx_frame->data[0] = session->stop_reason;
return true;
case SYN_GBT27930_STATE_ERROR:
tx_frame->id = syn_j1939_id_pack(2, SYN_GBT27930_PGN_BEM, sa, da);
tx_frame->data[0] = session->fault_code;
return true;
default:
return false;
}
} else { /* Charger Role */
switch (session->state) {
case SYN_GBT27930_STATE_HANDSHAKE:
tx_frame->id = syn_j1939_id_pack(6, SYN_GBT27930_PGN_CHM, sa, da);
tx_frame->data[0] = 0x01; /* Version 1.1 */
return true;
case SYN_GBT27930_STATE_PARAM_CONFIG:
tx_frame->id = syn_j1939_id_pack(6, SYN_GBT27930_PGN_CRO, sa, da);
tx_frame->data[0] = session->ready_for_charging ? 0xAA : 0x00;
return true;
case SYN_GBT27930_STATE_CHARGING:
tx_frame->id = syn_j1939_id_pack(6, SYN_GBT27930_PGN_CCS, sa, da);
tx_frame->data[0] = (uint8_t)(session->telemetry.measured_volt_v & 0xFF);
tx_frame->data[1] = (uint8_t)((session->telemetry.measured_volt_v >> 8) & 0xFF);
tx_frame->data[2] = (uint8_t)(session->telemetry.measured_curr_a & 0xFF);
tx_frame->data[3] = (uint8_t)((session->telemetry.measured_curr_a >> 8) & 0xFF);
return true;
case SYN_GBT27930_STATE_STOPPING:
tx_frame->id = syn_j1939_id_pack(4, SYN_GBT27930_PGN_CST, sa, da);
tx_frame->data[0] = session->stop_reason;
return true;
case SYN_GBT27930_STATE_ERROR:
tx_frame->id = syn_j1939_id_pack(2, SYN_GBT27930_PGN_CEM, sa, da);
tx_frame->data[0] = session->fault_code;
return true;
default:
return false;
}
}
}
#endif /* SYN_USE_GBT27930 */