Skip to content

File syn_cannm.c

File List > proto > syn_cannm.c

Go to the documentation of this file

#include "syn_cannm.h"

#include <string.h>

#if !defined(SYN_USE_CANNM) || SYN_USE_CANNM

void syn_cannm_init(SYN_CanNM_Session *session, const SYN_CanNM_Config *cfg)
{
    if (!session) {
        return;
    }

    memset(session, 0, sizeof(*session));

    if (cfg) {
        session->config = *cfg;
    } else {
        /* Default AUTOSAR CAN NM Timers & IDs */
        session->config.node_id = 0x01U;
        session->config.can_id_base = 0x400U;
        session->config.can_id_mask = 0x7F0U;
        session->config.msg_cycle_ms = 100U;
        session->config.nm_timeout_ms = 1000U;
        session->config.wait_bus_sleep_ms = 1500U;
        session->config.repeat_msg_time_ms = 1600U;
    }

    session->state = SYN_CANNM_STATE_BUS_SLEEP;
    session->prev_state = SYN_CANNM_STATE_BUS_SLEEP;
}

void syn_cannm_request_network(SYN_CanNM_Session *session)
{
    if (!session) {
        return;
    }
    session->node_comm_req = true;
}

void syn_cannm_release_network(SYN_CanNM_Session *session)
{
    if (!session) {
        return;
    }
    session->node_comm_req = false;
}

void syn_cannm_request_repeat_msg(SYN_CanNM_Session *session)
{
    if (!session) {
        return;
    }
    session->repeat_msg_req = true;
}

void syn_cannm_set_user_data(SYN_CanNM_Session *session, const uint8_t *data, size_t len)
{
    if (!session) {
        return;
    }
    memset(session->user_data, 0, sizeof(session->user_data));
    if (data && len > 0) {
        size_t copy_len = len > 6U ? 6U : len;
        memcpy(session->user_data, data, copy_len);
    }
}

bool syn_cannm_process_rx_frame(SYN_CanNM_Session *session, const SYN_CAN_Frame *frame)
{
    if (!session || !frame) {
        return false;
    }

    /* Verify DLC and CAN ID matching */
    if (frame->dlc < 2U) {
        return false;
    }

    uint32_t mask = session->config.can_id_mask;
    if ((frame->id & mask) != (session->config.can_id_base & mask)) {
        return false;
    }

    session->rx_source_node_id = frame->data[0];
    session->rx_cbv = frame->data[1];
    if (frame->dlc >= 8U) {
        memcpy(session->rx_user_data, &frame->data[2], 6U);
    }

    /* Restart timeout timer on any valid CAN NM reception */
    session->timeout_timer = session->config.nm_timeout_ms;

    /* Handle state transitions triggered by Rx */
    switch (session->state) {
    case SYN_CANNM_STATE_BUS_SLEEP:
    case SYN_CANNM_STATE_PRE_BUS_SLEEP:
        session->prev_state = session->state;
        session->state = SYN_CANNM_STATE_REPEAT_MSG;
        session->repeat_msg_timer = session->config.repeat_msg_time_ms;
        session->msg_cycle_timer = 0U; /* Transmit immediately */
        break;

    case SYN_CANNM_STATE_NORMAL_OP:
    case SYN_CANNM_STATE_READY_SLEEP:
        if (session->rx_cbv & SYN_CANNM_CBV_REPEAT_MSG_REQ) {
            session->prev_state = session->state;
            session->state = SYN_CANNM_STATE_REPEAT_MSG;
            session->repeat_msg_timer = session->config.repeat_msg_time_ms;
            session->msg_cycle_timer = 0U;
        }
        break;

    case SYN_CANNM_STATE_REPEAT_MSG:
    default:
        break;
    }

    return true;
}

static void pack_nm_tx_pdu(SYN_CanNM_Session *session, SYN_CAN_Frame *tx_frame)
{
    tx_frame->id = session->config.can_id_base + session->config.node_id;
    tx_frame->extended = false;
    tx_frame->dlc = 8U;
    tx_frame->data[0] = session->config.node_id;

    uint8_t cbv = 0U;
    if (session->repeat_msg_req) {
        cbv |= SYN_CANNM_CBV_REPEAT_MSG_REQ;
    }
    if (session->node_comm_req) {
        cbv |= SYN_CANNM_CBV_ACTIVE_WAKEUP_REQ;
    }
    tx_frame->data[1] = cbv;

    memcpy(&tx_frame->data[2], session->user_data, 6U);
}

bool syn_cannm_step(SYN_CanNM_Session *session, uint32_t delta_ms, SYN_CAN_Frame *tx_frame)
{
    if (!session) {
        return false;
    }

    bool generate_tx = false;

    /* Bus Wakeup checks */
    if ((session->state == SYN_CANNM_STATE_BUS_SLEEP ||
         session->state == SYN_CANNM_STATE_PRE_BUS_SLEEP) &&
        session->node_comm_req) {
        session->prev_state = session->state;
        session->state = SYN_CANNM_STATE_REPEAT_MSG;
        session->repeat_msg_timer = session->config.repeat_msg_time_ms;
        session->timeout_timer = session->config.nm_timeout_ms;
        session->msg_cycle_timer = 0U;
    }

    /* Execute active state logic */
    switch (session->state) {
    case SYN_CANNM_STATE_BUS_SLEEP:
        break;

    case SYN_CANNM_STATE_PRE_BUS_SLEEP:
        if (session->wait_bus_sleep_timer <= delta_ms) {
            session->wait_bus_sleep_timer = 0U;
            session->prev_state = session->state;
            session->state = SYN_CANNM_STATE_BUS_SLEEP;
        } else {
            session->wait_bus_sleep_timer -= delta_ms;
        }
        break;

    case SYN_CANNM_STATE_REPEAT_MSG:
        /* Timer countdowns */
        if (session->repeat_msg_timer <= delta_ms) {
            session->repeat_msg_timer = 0U;
            session->prev_state = session->state;
            if (session->node_comm_req) {
                session->state = SYN_CANNM_STATE_NORMAL_OP;
            } else {
                session->state = SYN_CANNM_STATE_READY_SLEEP;
            }
        } else {
            session->repeat_msg_timer -= delta_ms;
        }

        if (session->msg_cycle_timer <= delta_ms) {
            session->msg_cycle_timer = session->config.msg_cycle_ms;
            session->timeout_timer = session->config.nm_timeout_ms;
            if (tx_frame) {
                pack_nm_tx_pdu(session, tx_frame);
                generate_tx = true;
            }
        } else {
            session->msg_cycle_timer -= delta_ms;
        }
        break;

    case SYN_CANNM_STATE_NORMAL_OP:
        if (session->repeat_msg_req) {
            session->prev_state = session->state;
            session->state = SYN_CANNM_STATE_REPEAT_MSG;
            session->repeat_msg_timer = session->config.repeat_msg_time_ms;
            session->msg_cycle_timer = 0U;
            break;
        }

        if (!session->node_comm_req) {
            session->prev_state = session->state;
            session->state = SYN_CANNM_STATE_READY_SLEEP;
            break;
        }

        if (session->msg_cycle_timer <= delta_ms) {
            session->msg_cycle_timer = session->config.msg_cycle_ms;
            session->timeout_timer = session->config.nm_timeout_ms;
            if (tx_frame) {
                pack_nm_tx_pdu(session, tx_frame);
                generate_tx = true;
            }
        } else {
            session->msg_cycle_timer -= delta_ms;
        }
        break;

    case SYN_CANNM_STATE_READY_SLEEP:
        if (session->repeat_msg_req) {
            session->prev_state = session->state;
            session->state = SYN_CANNM_STATE_REPEAT_MSG;
            session->repeat_msg_timer = session->config.repeat_msg_time_ms;
            session->msg_cycle_timer = 0U;
            break;
        }

        if (session->node_comm_req) {
            session->prev_state = session->state;
            session->state = SYN_CANNM_STATE_NORMAL_OP;
            session->msg_cycle_timer = 0U;
            break;
        }

        if (session->timeout_timer <= delta_ms) {
            session->timeout_timer = 0U;
            session->prev_state = session->state;
            session->state = SYN_CANNM_STATE_PRE_BUS_SLEEP;
            session->wait_bus_sleep_timer = session->config.wait_bus_sleep_ms;
        } else {
            session->timeout_timer -= delta_ms;
        }
        break;
    }

    session->repeat_msg_req = false;
    return generate_tx;
}

#endif /* SYN_USE_CANNM */