File syn_j1939.c¶
File List > proto > syn_j1939.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_J1939) || SYN_USE_J1939
#include "../util/syn_assert.h"
#include "../util/syn_pack.h"
#include "syn_j1939.h"
#include <string.h>
uint32_t syn_j1939_calc_pgn(uint8_t dp, uint8_t pf, uint8_t ps)
{
if (pf < 240U) {
return (((uint32_t)dp & 0x01U) << 16) | (((uint32_t)pf) << 8);
}
return (((uint32_t)dp & 0x01U) << 16) | (((uint32_t)pf) << 8) | (uint32_t)ps;
}
uint32_t syn_j1939_id_pack(uint8_t priority, uint32_t pgn, uint8_t sa, uint8_t da)
{
uint8_t dp = (uint8_t)((pgn >> 16) & 0x01U);
uint8_t pf = (uint8_t)((pgn >> 8) & 0xFFU);
uint8_t ps = (uint8_t)(pgn & 0xFFU);
if (pf < 240U) {
ps = da; /* Destination-specific PDU1 format */
}
uint32_t id = 0;
id |= ((uint32_t)(priority & 0x07U)) << 26;
id |= ((uint32_t)(dp & 0x01U)) << 24;
id |= ((uint32_t)pf) << 16;
id |= ((uint32_t)ps) << 8;
id |= ((uint32_t)sa);
return id;
}
SYN_Status syn_j1939_id_unpack(uint32_t can_id, SYN_J1939_Header *header)
{
if (!header)
return SYN_INVALID_PARAM;
header->priority = (uint8_t)((can_id >> 26) & 0x07U);
header->dp = (uint8_t)((can_id >> 24) & 0x01U);
header->pf = (uint8_t)((can_id >> 16) & 0xFFU);
header->ps = (uint8_t)((can_id >> 8) & 0xFFU);
header->sa = (uint8_t)(can_id & 0xFFU);
header->is_pdu1 = (header->pf < 240U);
if (header->is_pdu1) {
header->da = header->ps;
header->pgn = syn_j1939_calc_pgn(header->dp, header->pf, 0);
} else {
header->da = SYN_J1939_ADDR_GLOBAL;
header->pgn = syn_j1939_calc_pgn(header->dp, header->pf, header->ps);
}
return SYN_OK;
}
void syn_j1939_name_encode(const SYN_J1939_Name *name, uint8_t buf[8])
{
if (!name || !buf)
return;
memset(buf, 0, 8);
uint64_t n = 0;
n |= ((uint64_t)(name->identity_number & 0x1FFFFFU));
n |= ((uint64_t)(name->manufacturer_code & 0x7FFU)) << 21;
n |= ((uint64_t)(name->ecu_instance & 0x07U)) << 32;
n |= ((uint64_t)(name->function_instance & 0x1FU)) << 35;
n |= ((uint64_t)(name->function)) << 40;
n |= ((uint64_t)(name->vehicle_system & 0x7FU)) << 49;
n |= ((uint64_t)(name->vehicle_system_inst & 0x0FU)) << 56;
n |= ((uint64_t)(name->industry_group & 0x07U)) << 60;
n |= ((uint64_t)(name->arbitrary_addr_cap ? 1U : 0U)) << 63;
size_t pos = 0;
syn_pack_u64_le(buf, &pos, n);
}
void syn_j1939_name_decode(const uint8_t buf[8], SYN_J1939_Name *name)
{
if (!name || !buf)
return;
memset(name, 0, sizeof(*name));
size_t pos = 0;
uint64_t n = syn_unpack_u64_le(buf, &pos);
name->identity_number = (uint32_t)(n & 0x1FFFFFU);
name->manufacturer_code = (uint16_t)((n >> 21) & 0x7FFU);
name->ecu_instance = (uint8_t)((n >> 32) & 0x07U);
name->function_instance = (uint8_t)((n >> 35) & 0x1FU);
name->function = (uint8_t)((n >> 40) & 0xFFU);
name->vehicle_system = (uint8_t)((n >> 49) & 0x7FU);
name->vehicle_system_inst = (uint8_t)((n >> 56) & 0x0FU);
name->industry_group = (uint8_t)((n >> 60) & 0x07U);
name->arbitrary_addr_cap = ((n >> 63) & 0x01U) != 0;
}
SYN_Status syn_j1939_node_init(SYN_J1939_Node *node, uint8_t sa, const SYN_J1939_Name *name)
{
if (!node)
return SYN_INVALID_PARAM;
memset(node, 0, sizeof(*node));
node->sa = sa;
if (name) {
node->name = *name;
}
node->state = SYN_J1939_STATE_UNCLAIMED;
return SYN_OK;
}
SYN_Status syn_j1939_build_address_claim(const SYN_J1939_Node *node, SYN_CAN_Frame *frame)
{
if (!node || !frame)
return SYN_INVALID_PARAM;
frame->id = syn_j1939_id_pack(6, SYN_J1939_PGN_ADDR_CLAIM, node->sa, SYN_J1939_ADDR_GLOBAL);
frame->dlc = 8;
frame->extended = true;
syn_j1939_name_encode(&node->name, frame->data);
return SYN_OK;
}
SYN_Status syn_j1939_build_request(uint8_t sa, uint8_t da, uint32_t requested_pgn,
SYN_CAN_Frame *frame)
{
if (!frame)
return SYN_INVALID_PARAM;
frame->id = syn_j1939_id_pack(6, SYN_J1939_PGN_REQUEST, sa, da);
frame->dlc = 3;
frame->extended = true;
frame->data[0] = (uint8_t)(requested_pgn & 0xFFU);
frame->data[1] = (uint8_t)((requested_pgn >> 8) & 0xFFU);
frame->data[2] = (uint8_t)((requested_pgn >> 16) & 0xFFU);
return SYN_OK;
}
SYN_Status syn_j1939_build_tp_bam(uint8_t sa, uint32_t pgn, uint16_t total_bytes,
SYN_CAN_Frame *frame)
{
if (!frame || total_bytes < 9 || total_bytes > 1785)
return SYN_INVALID_PARAM;
frame->id = syn_j1939_id_pack(6, SYN_J1939_PGN_TP_CM, sa, SYN_J1939_ADDR_GLOBAL);
frame->dlc = 8;
frame->extended = true;
uint8_t total_packets = (uint8_t)((total_bytes + 6U) / 7U);
frame->data[0] = SYN_J1939_TP_CTRL_BAM;
syn_poke_u16_le(total_bytes, frame->data, 1);
frame->data[3] = total_packets;
frame->data[4] = 0xFFU; /* Reserved */
frame->data[5] = (uint8_t)(pgn & 0xFFU);
frame->data[6] = (uint8_t)((pgn >> 8) & 0xFFU);
frame->data[7] = (uint8_t)((pgn >> 16) & 0xFFU);
return SYN_OK;
}
SYN_Status syn_j1939_build_tp_dt(uint8_t sa, uint8_t sequence, const uint8_t *payload,
size_t payload_len, SYN_CAN_Frame *frame)
{
if (!frame || !payload || sequence == 0 || payload_len == 0 || payload_len > 7) {
return SYN_INVALID_PARAM;
}
frame->id = syn_j1939_id_pack(6, SYN_J1939_PGN_TP_DT, sa, SYN_J1939_ADDR_GLOBAL);
frame->dlc = 8;
frame->extended = true;
frame->data[0] = sequence;
memset(&frame->data[1], 0xFF, 7);
memcpy(&frame->data[1], payload, payload_len);
return SYN_OK;
}
size_t syn_j1939_encode_dm1(uint8_t *buf, size_t buf_size, const SYN_J1939_DTC *dtc_list,
size_t dtc_count, uint8_t mil_lamp_status)
{
if (!buf || buf_size < 2)
return 0;
buf[0] = (uint8_t)(mil_lamp_status & 0xFFU);
buf[1] = 0xFFU; /* Reserved / Flash Lamp status */
if (dtc_count == 0 || !dtc_list) {
if (buf_size >= 6) {
memset(&buf[2], 0, 4);
return 6;
}
return 2;
}
size_t offset = 2;
for (size_t i = 0; i < dtc_count; i++) {
if (offset + 4 > buf_size)
break;
uint32_t spn = dtc_list[i].spn & 0x7FFFFU;
uint8_t fmi = dtc_list[i].fmi & 0x1FU;
uint8_t oc = dtc_list[i].occurrence_count & 0x7FU;
uint8_t cm = (uint8_t)(dtc_list[i].conversion_method & 0x01U);
syn_poke_u16_le((uint16_t)spn, buf, offset);
buf[offset + 2] = (uint8_t)(((spn >> 11) & 0xE0U) | fmi);
buf[offset + 3] = (uint8_t)((cm << 7) | oc);
offset += 4;
}
return offset;
}
SYN_Status syn_j1939_process_frame(SYN_J1939_Node *node, const SYN_CAN_Frame *frame,
uint32_t *out_pgn, const uint8_t **out_data, size_t *out_len)
{
if (!node || !frame || !out_pgn || !out_data || !out_len) {
return SYN_INVALID_PARAM;
}
SYN_J1939_Header hdr;
if (syn_j1939_id_unpack(frame->id, &hdr) != SYN_OK) {
return SYN_INVALID_PARAM;
}
/* Handle Multi-Packet Transport Protocol Connection Management (TP.CM) */
if (hdr.pgn == SYN_J1939_PGN_TP_CM && frame->dlc == 8) {
uint8_t ctrl = frame->data[0];
if (ctrl == SYN_J1939_TP_CTRL_BAM || ctrl == SYN_J1939_TP_CTRL_RTS) {
uint16_t total_bytes = syn_peek_u16_le(frame->data, 1);
uint8_t total_packets = frame->data[3];
uint32_t pgn =
(uint32_t)syn_peek_u16_le(frame->data, 5) | ((uint32_t)frame->data[7] << 16);
uint8_t expected_packets = (uint8_t)((total_bytes + 6U) / 7U);
if (total_bytes <= sizeof(node->tp_rx.data) && total_packets > 0 &&
total_packets == expected_packets) {
node->tp_rx.active = true;
node->tp_rx.is_bam = (ctrl == SYN_J1939_TP_CTRL_BAM);
node->tp_rx.sa = hdr.sa;
node->tp_rx.da = hdr.da;
node->tp_rx.pgn = pgn;
node->tp_rx.total_bytes = total_bytes;
node->tp_rx.total_packets = total_packets;
node->tp_rx.next_seq = 1;
node->tp_rx.received_packets = 0;
}
}
return SYN_BUSY;
}
/* Handle Multi-Packet Transport Protocol Data Transfer (TP.DT) */
if (hdr.pgn == SYN_J1939_PGN_TP_DT && node->tp_rx.active && hdr.sa == node->tp_rx.sa) {
uint8_t seq = frame->data[0];
if (seq == node->tp_rx.next_seq) {
size_t offset = (size_t)(seq - 1) * 7U;
size_t copy_len = 7U;
if (offset + copy_len > node->tp_rx.total_bytes) {
copy_len = node->tp_rx.total_bytes - offset;
}
if (offset + copy_len <= sizeof(node->tp_rx.data)) {
memcpy(&node->tp_rx.data[offset], &frame->data[1], copy_len);
node->tp_rx.next_seq++;
node->tp_rx.received_packets++;
if (node->tp_rx.received_packets >= node->tp_rx.total_packets) {
*out_pgn = node->tp_rx.pgn;
*out_data = node->tp_rx.data;
*out_len = node->tp_rx.total_bytes;
node->tp_rx.active = false;
return SYN_OK;
}
}
}
return SYN_BUSY;
}
/* Single-frame packet handling */
*out_pgn = hdr.pgn;
*out_data = frame->data;
*out_len = frame->dlc;
return SYN_OK;
}
size_t syn_j1939_encode_dm2(uint8_t *buf, size_t buf_size, const SYN_J1939_DTC *dtc_list,
size_t dtc_count, uint8_t mil_lamp_status)
{
return syn_j1939_encode_dm1(buf, buf_size, dtc_list, dtc_count, mil_lamp_status);
}
void syn_j1939_dtc_log_init(SYN_J1939_DTCLog *log)
{
if (log != NULL) {
(void)memset(log, 0, sizeof(*log));
}
}
SYN_Status syn_j1939_dtc_add_active(SYN_J1939_DTCLog *log, uint32_t spn, uint8_t fmi)
{
if (log == NULL) {
return SYN_INVALID_PARAM;
}
/* Check if already active */
for (size_t i = 0; i < log->active_count; i++) {
if (log->active_dtcs[i].spn == spn && log->active_dtcs[i].fmi == fmi) {
if (log->active_dtcs[i].occurrence_count < 126U) {
log->active_dtcs[i].occurrence_count++;
}
return SYN_OK;
}
}
if (log->active_count >= SYN_J1939_MAX_LOGGED_DTCS) {
return SYN_ERROR;
}
log->active_dtcs[log->active_count].spn = spn;
log->active_dtcs[log->active_count].fmi = fmi;
log->active_dtcs[log->active_count].occurrence_count = 1;
log->active_dtcs[log->active_count].conversion_method = 0;
log->active_count++;
return SYN_OK;
}
SYN_Status syn_j1939_dtc_clear_active(SYN_J1939_DTCLog *log, uint32_t spn, uint8_t fmi)
{
if (log == NULL) {
return SYN_INVALID_PARAM;
}
for (size_t i = 0; i < log->active_count; i++) {
if (log->active_dtcs[i].spn == spn && log->active_dtcs[i].fmi == fmi) {
/* Copy to prev_dtcs if space exists */
if (log->prev_count < SYN_J1939_MAX_LOGGED_DTCS) {
log->prev_dtcs[log->prev_count] = log->active_dtcs[i];
log->prev_count++;
}
/* Remove from active_dtcs */
for (size_t j = i; j + 1 < log->active_count; j++) {
log->active_dtcs[j] = log->active_dtcs[j + 1];
}
log->active_count--;
return SYN_OK;
}
}
return SYN_ERROR;
}
void syn_j1939_dtc_clear_dm3(SYN_J1939_DTCLog *log)
{
if (log != NULL) {
log->prev_count = 0;
(void)memset(log->prev_dtcs, 0, sizeof(log->prev_dtcs));
}
}
void syn_j1939_dtc_clear_dm11(SYN_J1939_DTCLog *log)
{
if (log != NULL) {
log->active_count = 0;
(void)memset(log->active_dtcs, 0, sizeof(log->active_dtcs));
}
}
#endif /* SYN_USE_J1939 */