Files
root ca13734bf0 Add full automotive RTOS project
Add kernel (Cortex-M0/M3/M4, Tricore, S32K, RISC-V ports), drivers,
middleware (CAN stack, diagnostics, safety), applications, board
support, build/test tooling, and documentation.
2026-08-23 03:35:29 -04:00

331 lines
9.4 KiB
C

/**
* @file can_nm.c
* @brief CAN Network Management implementation
*/
#include "can_nm.h"
#include <string.h>
/* CAN NM State */
typedef struct {
bool initialized;
CanNmConfig_t config;
CanNmState_t current_state;
CanNmNodeInfo_t nodes[CAN_NM_MAX_NODES];
CanNmNetworkStateChangedCallback_t network_callback;
CanNmNodeStateChangedCallback_t node_callback;
CanNmBusSleepRequestCallback_t sleep_callback;
CanNmWakeupIndicationCallback_t wakeup_callback;
uint32_t state_timer;
uint32_t repeat_message_timer;
uint8_t repeat_message_count;
Mutex_t mutex;
} CanNmState_t;
static CanNmState_t can_nm_state;
/* Initialize CAN NM */
KernelStatus_t can_nm_init(const CanNmConfig_t* config) {
if (config == NULL || can_nm_state.initialized) {
return KERNEL_ERROR;
}
/* Copy configuration */
memcpy(&can_nm_state.config, config, sizeof(CanNmConfig_t));
/* Initialize state */
can_nm_state.current_state = CAN_NM_OFFLINE;
can_nm_state.state_timer = 0;
can_nm_state.repeat_message_timer = 0;
can_nm_state.repeat_message_count = 0;
/* Initialize nodes */
for (int i = 0; i < CAN_NM_MAX_NODES; i++) {
can_nm_state.nodes[i].node_id = i;
can_nm_state.nodes[i].is_present = false;
can_nm_state.nodes[i].is_awake = false;
can_nm_state.nodes[i].last_message_time = 0;
can_nm_state.nodes[i].state = CAN_NM_OFFLINE;
}
/* Create mutex */
mutex_create(&can_nm_state.mutex, false);
can_nm_state.initialized = true;
return KERNEL_OK;
}
/* Start CAN NM */
KernelStatus_t can_nm_start(void) {
if (!can_nm_state.initialized) {
return KERNEL_ERROR;
}
mutex_lock(&can_nm_state.mutex, 100);
can_nm_state.current_state = CAN_NM_REPEAT_MESSAGE;
can_nm_state.repeat_message_count = 0;
can_nm_state.repeat_message_timer = kernel_get_tick_count();
mutex_unlock(&can_nm_state.mutex);
/* Send first alive message */
can_nm_send_message(CAN_NM_MSG_ALIVE);
return KERNEL_OK;
}
/* Stop CAN NM */
KernelStatus_t can_nm_stop(void) {
if (!can_nm_state.initialized) {
return KERNEL_ERROR;
}
mutex_lock(&can_nm_state.mutex, 100);
can_nm_state.current_state = CAN_NM_OFFLINE;
mutex_unlock(&can_nm_state.mutex);
return KERNEL_OK;
}
/* Request Bus Sleep */
KernelStatus_t can_nm_request_bus_sleep(void) {
if (!can_nm_state.initialized) {
return KERNEL_ERROR;
}
mutex_lock(&can_nm_state.mutex, 100);
/* Send sleep indication */
can_nm_send_message(CAN_NM_MSG_SLEEP_ACK);
can_nm_state.current_state = CAN_NM_READY_SLEEP;
mutex_unlock(&can_nm_state.mutex);
return KERNEL_OK;
}
/* Network Release */
KernelStatus_t can_nm_network_release(void) {
if (!can_nm_state.initialized) {
return KERNEL_ERROR;
}
mutex_lock(&can_nm_state.mutex, 100);
/* Check if all nodes are ready to sleep */
bool all_ready = true;
for (int i = 0; i < CAN_NM_MAX_NODES; i++) {
if (can_nm_state.nodes[i].is_present &&
can_nm_state.nodes[i].state != CAN_NM_READY_SLEEP) {
all_ready = false;
break;
}
}
if (all_ready || can_nm_state.config.is_coordinator) {
can_nm_state.current_state = CAN_NM_PREPARE_BUS_SLEEP;
/* Send sleep confirmation */
can_nm_send_message(CAN_NM_MSG_SLEEP_CONF);
/* Call sleep callback */
if (can_nm_state.sleep_callback != NULL) {
can_nm_state.sleep_callback();
}
can_nm_state.current_state = CAN_NM_BUS_SLEEP;
/* Call network state changed callback */
if (can_nm_state.network_callback != NULL) {
can_nm_state.network_callback(CAN_NM_BUS_SLEEP);
}
}
mutex_unlock(&can_nm_state.mutex);
return KERNEL_OK;
}
/* Network Request */
KernelStatus_t can_nm_network_request(void) {
if (!can_nm_state.initialized) {
return KERNEL_ERROR;
}
mutex_lock(&can_nm_state.mutex, 100);
if (can_nm_state.current_state == CAN_NM_BUS_SLEEP) {
can_nm_state.current_state = CAN_NM_REPEAT_MESSAGE;
can_nm_state.repeat_message_count = 0;
/* Send wakeup indication */
can_nm_send_message(CAN_NM_MSG_ALIVE);
/* Call wakeup callback */
if (can_nm_state.wakeup_callback != NULL) {
can_nm_state.wakeup_callback();
}
}
mutex_unlock(&can_nm_state.mutex);
return KERNEL_OK;
}
/* Process Received NM Message */
void can_nm_process_rx_message(const CanMessage_t* message) {
if (!can_nm_state.initialized || message == NULL) {
return;
}
/* Check if NM message */
if (message->id.id < CAN_NM_MESSAGE_ID_BASE ||
message->id.id >= CAN_NM_MESSAGE_ID_BASE + CAN_NM_MAX_NODES) {
return;
}
/* Extract node ID and message type */
uint8_t node_id = message->id.id - CAN_NM_MESSAGE_ID_BASE;
uint8_t message_type = message->data[0] & 0x0F;
mutex_lock(&can_nm_state.mutex, 100);
/* Update node information */
can_nm_state.nodes[node_id].is_present = true;
can_nm_state.nodes[node_id].last_message_time = kernel_get_tick_count();
/* Process message type */
switch (message_type) {
case CAN_NM_MSG_ALIVE:
can_nm_state.nodes[node_id].is_awake = true;
can_nm_state.nodes[node_id].state = CAN_NM_NORMAL_OPERATION;
if (can_nm_state.node_callback != NULL) {
can_nm_state.node_callback(node_id, true);
}
break;
case CAN_NM_MSG_RING:
/* Ring message - keep awake */
can_nm_state.nodes[node_id].is_awake = true;
break;
case CAN_NM_MSG_SLEEP_ACK:
can_nm_state.nodes[node_id].state = CAN_NM_READY_SLEEP;
break;
case CAN_NM_MSG_SLEEP_CONF:
can_nm_state.nodes[node_id].state = CAN_NM_BUS_SLEEP;
can_nm_state.nodes[node_id].is_awake = false;
if (can_nm_state.node_callback != NULL) {
can_nm_state.node_callback(node_id, false);
}
break;
default:
break;
}
mutex_unlock(&can_nm_state.mutex);
}
/* CAN NM Main Function */
void can_nm_main_function(void) {
if (!can_nm_state.initialized) {
return;
}
uint32_t current_time = kernel_get_tick_count();
mutex_lock(&can_nm_state.mutex, 100);
switch (can_nm_state.current_state) {
case CAN_NM_REPEAT_MESSAGE:
/* Send repeat messages */
if ((current_time - can_nm_state.repeat_message_timer) >=
can_nm_state.config.repeat_message_time_ms) {
can_nm_send_message(CAN_NM_MSG_RING);
can_nm_state.repeat_message_timer = current_time;
can_nm_state.repeat_message_count++;
/* Transition to normal operation after repeat messages */
if (can_nm_state.repeat_message_count >= 3) {
can_nm_state.current_state = CAN_NM_NORMAL_OPERATION;
if (can_nm_state.network_callback != NULL) {
can_nm_state.network_callback(CAN_NM_NORMAL_OPERATION);
}
}
}
break;
case CAN_NM_NORMAL_OPERATION:
/* Send periodic ring messages */
if ((current_time - can_nm_state.state_timer) >=
can_nm_state.config.timeout_ms / 2) {
can_nm_send_message(CAN_NM_MSG_RING);
can_nm_state.state_timer = current_time;
}
break;
case CAN_NM_READY_SLEEP:
/* Check if still ready to sleep */
if ((current_time - can_nm_state.state_timer) >=
can_nm_state.config.sleep_ack_timeout_ms) {
can_nm_state.current_state = CAN_NM_NORMAL_OPERATION;
}
break;
case CAN_NM_PREPARE_BUS_SLEEP:
/* Transition to bus sleep */
if ((current_time - can_nm_state.state_timer) >=
can_nm_state.config.timeout_ms) {
can_nm_state.current_state = CAN_NM_BUS_SLEEP;
if (can_nm_state.network_callback != NULL) {
can_nm_state.network_callback(CAN_NM_BUS_SLEEP);
}
}
break;
default:
break;
}
/* Check node timeouts */
for (int i = 0; i < CAN_NM_MAX_NODES; i++) {
if (can_nm_state.nodes[i].is_present &&
can_nm_state.nodes[i].is_awake) {
if ((current_time - can_nm_state.nodes[i].last_message_time) >
can_nm_state.config.timeout_ms) {
/* Node timeout */
can_nm_state.nodes[i].is_present = false;
can_nm_state.nodes[i].is_awake = false;
if (can_nm_state.node_callback != NULL) {
can_nm_state.node_callback(i, false);
}
}
}
}
mutex_unlock(&can_nm_state.mutex);
}
/* Send NM Message (internal) */
static void can_nm_send_message(uint8_t message_type) {
CanMessage_t message;
message.id.id = can_nm_state.config.message_id +
can_nm_state.config.node_id;
message.id.is_extended = false;
message.length = 8;
message.data[0] = message_type;
message.data[1] = can_nm_state.config.node_id;
message.data[2] = can_nm_state.current_state;
can_send_message(&message, 100);
}