File syn_lin.c¶
Go to the documentation of this file
#include "syn_lin.h"
#if defined(SYN_USE_LIN) && SYN_USE_LIN
/* ── PID Parity Calculation ──────────────────────────────────────────────── */
uint8_t syn_lin_calc_pid(uint8_t id)
{
uint8_t id_clean = id & SYN_LIN_ID_MAX;
uint8_t id0 = (id_clean >> 0) & 0x01;
uint8_t id1 = (id_clean >> 1) & 0x01;
uint8_t id2 = (id_clean >> 2) & 0x01;
uint8_t id3 = (id_clean >> 3) & 0x01;
uint8_t id4 = (id_clean >> 4) & 0x01;
uint8_t id5 = (id_clean >> 5) & 0x01;
uint8_t p0 = id0 ^ id1 ^ id2 ^ id4;
uint8_t p1 = !(id1 ^ id3 ^ id4 ^ id5);
return (uint8_t)((p1 << 7) | (p0 << 6) | id_clean);
}
bool syn_lin_verify_pid(uint8_t pid)
{
uint8_t expected_pid = syn_lin_calc_pid(pid & SYN_LIN_ID_MAX);
return (pid == expected_pid);
}
/* ── LIN Checksum Calculation ────────────────────────────────────────────── */
uint8_t syn_lin_calc_checksum(uint8_t pid, const uint8_t *data, uint8_t len,
SYN_LIN_ChecksumMode mode)
{
if (data == NULL || len == 0 || len > SYN_LIN_DATA_MAX) {
return 0;
}
uint8_t id = pid & SYN_LIN_ID_MAX;
/* Diagnostic frames 0x3C and 0x3D always use Classic checksum per LIN 2.1/ISO 17987 */
if (id == SYN_LIN_ID_MASTER_REQ || id == SYN_LIN_ID_SLAVE_RESP) {
mode = SYN_LIN_CHECKSUM_CLASSIC;
}
uint16_t sum = 0;
if (mode == SYN_LIN_CHECKSUM_ENHANCED) {
sum += pid;
if (sum > 0xFF) {
sum -= 0xFF;
}
}
for (uint8_t i = 0; i < len; i++) {
sum += data[i];
if (sum > 0xFF) {
sum -= 0xFF;
}
}
return (uint8_t)(~sum & 0xFF);
}
/* ── LIN Master Implementation ───────────────────────────────────────────── */
SYN_Status syn_lin_master_init(SYN_LIN_Master *master, const SYN_LIN_ScheduleSlot *slots,
size_t count)
{
if (master == NULL || slots == NULL || count == 0) {
return SYN_INVALID_PARAM;
}
master->slots = slots;
master->slot_count = count;
master->current_slot = 0;
master->slot_timer = 0;
master->running = true;
return SYN_OK;
}
bool syn_lin_master_step(SYN_LIN_Master *master, uint32_t dt_ms,
const SYN_LIN_ScheduleSlot **active_slot)
{
if (master == NULL || !master->running || master->slots == NULL || master->slot_count == 0) {
if (active_slot != NULL) {
*active_slot = NULL;
}
return false;
}
master->slot_timer += dt_ms;
const SYN_LIN_ScheduleSlot *current = &master->slots[master->current_slot];
if (master->slot_timer >= current->delay_ms) {
master->slot_timer = 0;
master->current_slot = (master->current_slot + 1) % master->slot_count;
if (active_slot != NULL) {
*active_slot = &master->slots[master->current_slot];
}
return true;
}
if (active_slot != NULL) {
*active_slot = current;
}
return false;
}
/* ── LIN Slave Implementation ────────────────────────────────────────────── */
SYN_Status syn_lin_slave_init(SYN_LIN_Slave *slave, uint8_t nad)
{
if (slave == NULL) {
return SYN_INVALID_PARAM;
}
slave->nad = nad;
slave->frame_count = 0;
slave->state = SYN_LIN_STATE_IDLE;
slave->rx_idx = 0;
slave->expected_len = 0;
slave->expected_checksum_mode = SYN_LIN_CHECKSUM_CLASSIC;
for (size_t i = 0; i < SYN_LIN_SLAVE_MAX_FRAMES; i++) {
slave->frames[i].active = false;
}
return SYN_OK;
}
SYN_Status syn_lin_slave_add_frame(SYN_LIN_Slave *slave, uint8_t id, uint8_t len,
SYN_LIN_SlotDirection dir, SYN_LIN_ChecksumMode mode)
{
if (slave == NULL || id > SYN_LIN_ID_MAX || len == 0 || len > SYN_LIN_DATA_MAX) {
return SYN_INVALID_PARAM;
}
/* Check if frame ID is already registered */
for (size_t i = 0; i < slave->frame_count; i++) {
if (slave->frames[i].active && slave->frames[i].id == id) {
slave->frames[i].len = len;
slave->frames[i].dir = dir;
slave->frames[i].checksum_mode = mode;
return SYN_OK;
}
}
if (slave->frame_count >= SYN_LIN_SLAVE_MAX_FRAMES) {
return SYN_ERROR;
}
SYN_LIN_SlaveFrame *f = &slave->frames[slave->frame_count++];
f->id = id;
f->len = len;
f->dir = dir;
f->checksum_mode = mode;
f->active = true;
for (uint8_t j = 0; j < SYN_LIN_DATA_MAX; j++) {
f->data[j] = 0x00;
}
return SYN_OK;
}
SYN_Status syn_lin_slave_set_publish_data(SYN_LIN_Slave *slave, uint8_t id, const uint8_t *data,
uint8_t len)
{
if (slave == NULL || data == NULL || len == 0 || len > SYN_LIN_DATA_MAX ||
id > SYN_LIN_ID_MAX) {
return SYN_INVALID_PARAM;
}
for (size_t i = 0; i < slave->frame_count; i++) {
if (slave->frames[i].active && slave->frames[i].id == id &&
slave->frames[i].dir == SYN_LIN_SLOT_PUBLISH) {
if (len > slave->frames[i].len) {
len = slave->frames[i].len;
}
for (uint8_t j = 0; j < len; j++) {
slave->frames[i].data[j] = data[j];
}
return SYN_OK;
}
}
return SYN_ERROR;
}
bool syn_lin_slave_process_byte(SYN_LIN_Slave *slave, uint8_t byte, SYN_LIN_Frame *out_frame)
{
if (slave == NULL) {
return false;
}
switch (slave->state) {
case SYN_LIN_STATE_IDLE:
if (byte == SYN_LIN_SYNC_BYTE) {
slave->state = SYN_LIN_STATE_PID;
}
break;
case SYN_LIN_STATE_SYNC:
if (byte == SYN_LIN_SYNC_BYTE) {
slave->state = SYN_LIN_STATE_PID;
} else {
slave->state = SYN_LIN_STATE_IDLE;
}
break;
case SYN_LIN_STATE_PID: {
slave->rx_frame.pid = byte;
slave->rx_frame.id = byte & SYN_LIN_ID_MAX;
slave->rx_frame.valid_pid = syn_lin_verify_pid(byte);
slave->rx_idx = 0;
if (!slave->rx_frame.valid_pid) {
slave->state = SYN_LIN_STATE_IDLE;
break;
}
/* Look up expected frame in configuration table */
bool found = false;
for (size_t i = 0; i < slave->frame_count; i++) {
if (slave->frames[i].active && slave->frames[i].id == slave->rx_frame.id) {
slave->expected_len = slave->frames[i].len;
slave->expected_checksum_mode = slave->frames[i].checksum_mode;
found = true;
break;
}
}
if (!found) {
/* Unknown frame ID; default to 8 bytes classic checksum */
slave->expected_len = 8;
slave->expected_checksum_mode = SYN_LIN_CHECKSUM_CLASSIC;
}
slave->state = SYN_LIN_STATE_DATA;
break;
}
case SYN_LIN_STATE_DATA:
if (slave->rx_idx < SYN_LIN_DATA_MAX) {
slave->rx_frame.data[slave->rx_idx++] = byte;
}
if (slave->rx_idx >= slave->expected_len || slave->rx_idx >= SYN_LIN_DATA_MAX) {
slave->rx_frame.len = slave->rx_idx;
slave->state = SYN_LIN_STATE_CHECKSUM;
}
break;
case SYN_LIN_STATE_CHECKSUM: {
slave->rx_frame.checksum = byte;
uint8_t calc_cs = syn_lin_calc_checksum(slave->rx_frame.pid, slave->rx_frame.data,
slave->rx_frame.len, slave->expected_checksum_mode);
slave->rx_frame.valid_checksum = (byte == calc_cs);
slave->state = SYN_LIN_STATE_IDLE;
if (out_frame != NULL) {
*out_frame = slave->rx_frame;
}
return slave->rx_frame.valid_pid && slave->rx_frame.valid_checksum;
}
default:
slave->state = SYN_LIN_STATE_IDLE;
break;
}
return false;
}
#endif /* SYN_USE_LIN */