316 lines
11 KiB
C
316 lines
11 KiB
C
#include "mozen_protocol.h"
|
|
#include <string.h>
|
|
#include "bsp_uart.h"
|
|
|
|
// --- Helper Functions ---
|
|
/*
|
|
* Calculate CRC16 for data
|
|
* @param data: Pointer to data
|
|
* @param length: Length of data
|
|
* @return: CRC16 value
|
|
* @note: This function is used for Modbus CRC16
|
|
*/
|
|
uint16_t mozen_protocol_crc16_modbus(const uint8_t *data, uint16_t length)
|
|
{
|
|
uint16_t crc = 0xFFFF;
|
|
for (uint16_t i = 0; i < length; i++)
|
|
{
|
|
crc ^= data[i];
|
|
for (uint8_t j = 0; j < 8; j++)
|
|
{
|
|
if (crc & 0x0001) crc = (crc >> 1) ^ 0xA001;
|
|
else crc >>= 1;
|
|
}
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
/*
|
|
* Calculate checksum for data
|
|
* @param data: Pointer to data
|
|
* @param len: Length of data
|
|
* @return: Checksum value
|
|
* @note: This function is used for legacy checksum protocols
|
|
*/
|
|
uint16_t mozen_protocol_checksum(const uint8_t *data, uint32_t len)
|
|
{
|
|
uint16_t sum = 0;
|
|
for (uint32_t i = 0; i < len; ++i)
|
|
{
|
|
sum += data[i];
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
// --- Internal Functions ---
|
|
|
|
/*
|
|
* Reset RX state
|
|
* @param prot: Pointer to mozen_protocol_t structure
|
|
* @note: This function is called automatically by mozen_protocol_process_rx
|
|
*/
|
|
static void mozen_protocol_reset_rx(mozen_protocol_t *prot)
|
|
{
|
|
prot->rx_state = MOZEN_RX_STATE_WAIT_SOF1; // Reset to wait for SOF1
|
|
prot->rx_index = 0; // Reset index
|
|
prot->expected_data_len = 0; // Reset expected data length
|
|
}
|
|
|
|
/*
|
|
* Dispatch received frame
|
|
* @param prot: Pointer to mozen_protocol_t structure
|
|
* @note: This function is called automatically by mozen_protocol_process_rx
|
|
* when a complete frame is received
|
|
*/
|
|
static void mozen_protocol_dispatch(mozen_protocol_t *prot)
|
|
{
|
|
if (prot->rx_index < sizeof(mozen_frame_header_t) + 2) return; // Too short
|
|
|
|
uint16_t received_crc; // Extract CRC from frame
|
|
mozen_frame_header_t header;
|
|
memcpy(&header, prot->rx_buffer, sizeof(mozen_frame_header_t));
|
|
|
|
if (header.sof != MOZEN_PROTOCOL_SOF) return;
|
|
|
|
uint16_t full_frame_len = header.frame_length;
|
|
uint32_t total_len = (uint32_t)full_frame_len + 2; // +2 for SOF and CRC
|
|
if (total_len > prot->rx_index || total_len > prot->rx_buffer_size) return;
|
|
|
|
|
|
uint16_t data_len = full_frame_len - sizeof(mozen_frame_header_t); // Exclude header
|
|
const uint8_t *data_ptr = prot->rx_buffer + sizeof(mozen_frame_header_t); // Point to data
|
|
|
|
memcpy(&received_crc, prot->rx_buffer + full_frame_len, sizeof(uint16_t)); // Extract CRC from frame
|
|
|
|
// 1. Raw frame handler check (e.g. for legacy checksum protocols)
|
|
if (prot->raw_frame_handler)
|
|
{
|
|
if (prot->raw_frame_handler(prot->user_context, prot->rx_buffer, total_len)) return; // Handled by custom raw handler
|
|
}
|
|
|
|
// 2. Standard CRC16 check
|
|
uint16_t calculated_crc = mozen_protocol_crc16_modbus(prot->rx_buffer, full_frame_len);
|
|
if (received_crc != calculated_crc) return; // CRC Error
|
|
|
|
// 3. Dispatch to registered handlers
|
|
for (uint8_t i = 0; i < prot->handler_count; i++)
|
|
{
|
|
if (prot->handlers[i].cmd_id == header.command_id)
|
|
{
|
|
prot->handlers[i].handler(prot->user_context, header.command_id, data_ptr, data_len);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 4. Default handler fallback
|
|
if (prot->default_handler)
|
|
{
|
|
prot->default_handler(prot->user_context, header.command_id, data_ptr, data_len);
|
|
}
|
|
}
|
|
|
|
// --- API Functions ---
|
|
/*
|
|
* Initialize mozen_protocol_t structure
|
|
* @param prot: Pointer to mozen_protocol_t structure
|
|
* @param rx_buf: Pointer to RX buffer
|
|
* @param rx_buf_size: Size of RX buffer
|
|
* @param tx_buf_main: Pointer to TX buffer
|
|
* @param tx_buf_irq: Pointer to IRQ TX buffer (optional)
|
|
* @param tx_buf_size: Size of TX buffer
|
|
* @param tx_fn: TX function
|
|
* @param user_context: User context for TX function
|
|
* @note: This function should be called once before using the protocol
|
|
*/
|
|
void mozen_protocol_init(mozen_protocol_t *prot,
|
|
uint8_t *rx_buf, uint16_t rx_buf_size,
|
|
uint8_t *tx_buf_main, uint8_t *tx_buf_irq, uint16_t tx_buf_size,
|
|
mozen_tx_fn_t tx_fn, void *user_context)
|
|
{
|
|
if (!prot) return;
|
|
|
|
memset(prot, 0, sizeof(mozen_protocol_t));
|
|
|
|
prot->rx_buffer = rx_buf;
|
|
prot->rx_buffer_size = rx_buf_size;
|
|
prot->tx_buffer_main = tx_buf_main;
|
|
prot->tx_buffer_irq = tx_buf_irq ? tx_buf_irq : tx_buf_main;
|
|
prot->tx_buffer_size = tx_buf_size;
|
|
prot->tx_fn = tx_fn;
|
|
prot->user_context = user_context;
|
|
|
|
mozen_protocol_reset_rx(prot);
|
|
}
|
|
|
|
/*
|
|
* Register a command handler
|
|
* @param prot: Pointer to mozen_protocol_t structure
|
|
* @param cmd_id: Command ID
|
|
* @param handler: Command handler function
|
|
* @return: 0 on success, -1 on failure
|
|
* @note: This function can be called multiple times to register multiple handlers
|
|
*/
|
|
int mozen_protocol_register_handler(mozen_protocol_t *prot, uint8_t cmd_id, mozen_cmd_handler_t handler)
|
|
{
|
|
if (!prot || !handler) return -1;
|
|
if (prot->handler_count >= MOZEN_MAX_HANDLERS) return -1;
|
|
|
|
prot->handlers[prot->handler_count].cmd_id = cmd_id;
|
|
prot->handlers[prot->handler_count].handler = handler;
|
|
prot->handler_count++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Process RX buffer
|
|
* @param prot: Pointer to mozen_protocol_t structure
|
|
* @param current_time_ms: Current time in milliseconds
|
|
* @note: This function should be called periodically to process incoming data
|
|
*/
|
|
void mozen_protocol_set_default_handler(mozen_protocol_t *prot, mozen_cmd_handler_t handler)
|
|
{
|
|
if (prot) prot->default_handler = handler;
|
|
}
|
|
|
|
/*
|
|
* Set raw frame handler
|
|
* @param prot: Pointer to mozen_protocol_t structure
|
|
* @param handler: Raw frame handler function
|
|
* @note: This function can be used to handle legacy checksum protocols or custom protocols
|
|
*/
|
|
void mozen_protocol_set_raw_handler(mozen_protocol_t *prot, mozen_raw_frame_handler_t handler)
|
|
{
|
|
if (prot) prot->raw_frame_handler = handler;
|
|
}
|
|
|
|
/*
|
|
* Feed a byte to the protocol
|
|
* @param prot: Pointer to mozen_protocol_t structure
|
|
* @param byte: Byte to feed
|
|
* @param current_time_ms: Current time in milliseconds
|
|
* @note: This function should be called whenever a new byte is received
|
|
*/
|
|
void mozen_protocol_feed_byte(mozen_protocol_t *prot, uint8_t byte, uint32_t current_time_ms)
|
|
{
|
|
if (!prot || !prot->rx_buffer) return;
|
|
|
|
prot->last_rx_time_ms = current_time_ms;
|
|
|
|
switch (prot->rx_state)
|
|
{
|
|
case MOZEN_RX_STATE_WAIT_SOF1:
|
|
if (byte == (MOZEN_PROTOCOL_SOF & 0xFF))
|
|
{
|
|
prot->rx_buffer[0] = byte;
|
|
prot->rx_index = 1;
|
|
prot->rx_state = MOZEN_RX_STATE_WAIT_SOF2;
|
|
}
|
|
break;
|
|
|
|
case MOZEN_RX_STATE_WAIT_SOF2:
|
|
if (byte == (MOZEN_PROTOCOL_SOF >> 8))
|
|
{
|
|
prot->rx_buffer[1] = byte;
|
|
prot->rx_index = 2;
|
|
prot->rx_state = MOZEN_RX_STATE_WAIT_HEADER;
|
|
}
|
|
else { mozen_protocol_reset_rx(prot);}
|
|
break;
|
|
|
|
case MOZEN_RX_STATE_WAIT_HEADER: // Wait for frame header
|
|
prot->rx_buffer[prot->rx_index++] = byte;
|
|
if (prot->rx_index >= sizeof(mozen_frame_header_t))
|
|
{
|
|
mozen_frame_header_t header;
|
|
memcpy(&header, prot->rx_buffer, sizeof(mozen_frame_header_t));
|
|
|
|
prot->expected_data_len = header.frame_length - sizeof(mozen_frame_header_t) + 2; // +2 for CRC
|
|
|
|
if ((sizeof(mozen_frame_header_t) + prot->expected_data_len) > prot->rx_buffer_size)
|
|
{
|
|
mozen_protocol_reset_rx(prot);
|
|
return;
|
|
}
|
|
prot->rx_state = MOZEN_RX_STATE_WAIT_DATA;
|
|
}
|
|
break;
|
|
|
|
case MOZEN_RX_STATE_WAIT_DATA:
|
|
prot->rx_buffer[prot->rx_index++] = byte;
|
|
if (prot->rx_index >= (sizeof(mozen_frame_header_t) + prot->expected_data_len))
|
|
{
|
|
mozen_protocol_dispatch(prot);
|
|
mozen_protocol_reset_rx(prot);
|
|
} else if (prot->rx_index >= prot->rx_buffer_size) {
|
|
// Should not happen due to header check, but safe guard against buffer overflow
|
|
mozen_protocol_reset_rx(prot);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reset the protocol state
|
|
* @param prot: Pointer to mozen_protocol_t structure
|
|
* current_time_ms: Current time in milliseconds
|
|
* timeout_ms: Timeout in milliseconds
|
|
* @note: This function should be called when a timeout occurs or when a frame is received
|
|
*/
|
|
void mozen_protocol_tick(mozen_protocol_t *prot, uint32_t current_time_ms, uint32_t timeout_ms)
|
|
{
|
|
if (!prot) return;
|
|
|
|
if (prot->rx_state != MOZEN_RX_STATE_WAIT_SOF1)
|
|
{
|
|
// Calculate diff taking into account potential integer overflow/wraparound
|
|
uint32_t time_diff = current_time_ms - prot->last_rx_time_ms;
|
|
if (time_diff > timeout_ms)
|
|
{
|
|
mozen_protocol_reset_rx(prot);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send a frame
|
|
* @param prot: Pointer to mozen_protocol_t structure
|
|
* @param command_id: Command ID
|
|
* @param data: Data to send
|
|
* @param length: Length of data
|
|
* @param use_irq_buffer: Use IRQ buffer for sending
|
|
* @return: 0 on success, -1 on error
|
|
* @note: This function sends a frame using the provided data and command ID
|
|
*/
|
|
int mozen_protocol_send_frame(mozen_protocol_t *prot, uint8_t command_id, const uint8_t* data, uint16_t length, bool use_irq_buffer)
|
|
{
|
|
if (!prot || !prot->tx_fn) return -1;
|
|
|
|
uint16_t frame_length = sizeof(mozen_frame_header_t) + length; //Total length after removing CRC
|
|
uint16_t total_length = frame_length + 2; // Total length including CRC
|
|
|
|
if (total_length > prot->tx_buffer_size) return -1;
|
|
|
|
// During the transmission stage, double buffering is employed to prevent the data frames of the main loop from overlapping with the interrupt ACKs.
|
|
// The sensor data frames and ACKs may be sent concurrently in different contexts.
|
|
// The two static buffers can avoid overwriting and competition.
|
|
// The ACKs use an independent buffer to prevent interference with large packet transmission.
|
|
uint8_t *active_buffer = use_irq_buffer ? prot->tx_buffer_irq : prot->tx_buffer_main;
|
|
|
|
mozen_frame_header_t header;
|
|
header.sof = MOZEN_PROTOCOL_SOF;
|
|
header.command_id = command_id;
|
|
header.frame_length = frame_length;
|
|
|
|
memcpy(active_buffer, &header, sizeof(mozen_frame_header_t));
|
|
if (data && length > 0)
|
|
{
|
|
memcpy(active_buffer + sizeof(mozen_frame_header_t), data, length);
|
|
}
|
|
|
|
uint16_t crc = mozen_protocol_crc16_modbus(active_buffer, frame_length);
|
|
memcpy(active_buffer + frame_length, &crc, sizeof(uint16_t));
|
|
|
|
return prot->tx_fn(active_buffer, total_length);
|
|
}
|