Обновлен протокол, изменение колбека приема, реализация вывода информации о модуле.

parent cc944181
......@@ -6,27 +6,33 @@
//!
//! @details Протокол предназначен для тестирования и диагностики встроенного ПО устройств,
//! подключенных к ПК через UART (COM-порт). ПК выступает мастером, устройство — подчинённым.
//! Команды передаются в текстовом виде, данные кодируются в HEX.
//! Команды передаются в текстовом виде
//!
//! Расширение протокола: новые команды регистрируются через cli_register_command().
//!
//! Максимальный размер поля данных: 64 байта.
//! Максимальный размер поля данных: задается в конфигурации
//!
//! @note Для работы необходимо наличие LMCAL, ring_buffer, а также tsp_dev_info из модуля tcp.h.
//***********************************************************************************************************
#include "cli_protocol.h"
#include "flash_mem.h"
#include "ring_buffer.h"
#include "lmcal.h"
#include "tcp.h"
#include "fpga_cmn.h"
#include "flash_mem.h"
#include "submodule.h"
#include <string.h>
#include <stdio.h>
// #include <string.h>
//******************************************** Определения типов ********************************************
// Коды ошибок по протоколу
typedef enum {
CLI_ERR_NONE = 0x00U, // Нет ошибки
CLI_ERR_UNKNOWN_CMD = 0x01U, // Неизвестная команда
CLI_ERR_INVALID_ARG = 0x02U, // Неверный формат или значение аргумента
CLI_ERR_INTERNAL = 0x03U, // Внутренняя ошибка устройства
CLI_ERR_CON = 0x04U, // Устройство не подключено
} cli_error_t;
// Включаем заголовок для доступа к регистрам USART (только для ожидания TC)
#include "gd32f4xx_usart.h"
//************************************ Локальные константы **************************************************
#define CLI_CMD_PREFIX 'T' // Префикс команды
......@@ -43,42 +49,21 @@
static ringbuf_t fifo_rx; // Кольцевой буфер приёма байт из UART
static u8 fifo_rx_buf[CLI_CMD_BUFFER_SIZE]; // Память для кольцевого буфера
static char cli_cmd_buffer[CLI_CMD_BUFFER_SIZE]; // Буфер для накопления строки команды
static u16 cli_cmd_index = 0; // Текущая длина строки в буфере
static u8 cli_connected = 0;
static bool cli_connected = false; // Флаг активного соединения (после успешной T00)
static u32 cli_last_activity = 0; // Время последней активности (тики)
static u32 (*cli_get_tick)(void) = NULL; // Счётчик
static cli_device_info_t cli_device_info;
static cli_device_info_t cli_device_info; // Информация об устройстве (имя, серийный номер, версия, board_id)
static cli_cmd_handler_t cmd_handlers[CLI_MAX_COMMANDS];
// Пароль хранится как байтовый массив с завершающим нулём (для удобства отладки)
static u8 cli_password[CLI_PASSWORD_MAX_LEN + 1];
static u8 cli_password_len = 0; // Фактическая длина пароля в байтах
static char buf_tmp[CLI_DATA_MAX_SIZE];
static u8 data_size;
// Таблица обработчиков команд. Индекс = код команды (0..255)
static cli_cmd_handler_t cmd_handlers[CLI_MAX_COMMANDS];
//************************************ Прототипы локальных функций ******************************************
static inline void byte_to_hex(u8 byte, char* hex);
static u8 hex_to_byte(const char* hex);
static void safe_strncpy(char* dst, const char* src, size_t dst_size);
static void send_response(u8 cmd, const u8* data, u8 len);
static void send_error(u8 cmd, u8 err);
static void uart_wait_tx_done(void);
static void handle_cmd_open(const u8* data, u8 len);
static void handle_cmd_close(const u8* data, u8 len);
static void handle_cmd_ping(const u8* data, u8 len);
static void handle_cmd_get_info(const u8* data, u8 len);
static void handle_cmd_read_mem(const u8* data, u8 len);
static void handle_cmd_write_mem(const u8* data, u8 len);
static void parse_and_execute(void);
static void uart_rx_cb(void);
static void register_builtin_commands(void);
//-----------------------------------------------------------------------------------------------------------
//***********************************************************************************************************
//******************************** Определения локальных (приватных) функций ********************************
//***********************************************************************************************************
// Преобразование байта в два HEX-символа (старший и младший полубайт)
//-----------------------------------------------------------------------------------------------------------
static inline void byte_to_hex(u8 byte, char* hex)
{
const char* d = "0123456789ABCDEF";
......@@ -86,9 +71,9 @@ static inline void byte_to_hex(u8 byte, char* hex)
hex[1] = d[byte & 0x0F];
}
//-----------------------------------------------------------------------------------------------------------
// Преобразование двух HEX-символов в байт. Возвращает 0xFF при ошибке.
//-----------------------------------------------------------------------------------------------------------
static u8 hex_to_byte(const char* hex)
{
u8 h = HEX_CHAR_TO_NIBBLE(hex[0]);
......@@ -96,10 +81,10 @@ static u8 hex_to_byte(const char* hex)
return (h == 0xFF || l == 0xFF) ? 0xFF : (h << 4) | l;
}
//-----------------------------------------------------------------------------------------------------------
// Безопасное копирование строки с гарантированным завершающим нулём.
// Аналог strncpy, но всегда добавляет '\0' в конце буфера.
//-----------------------------------------------------------------------------------------------------------
static void safe_strncpy(char* dst, const char* src, size_t dst_size)
{
if (dst == NULL || src == NULL || dst_size == 0) return;
......@@ -107,497 +92,522 @@ static void safe_strncpy(char* dst, const char* src, size_t dst_size)
dst[dst_size - 1] = '\0';
}
//-----------------------------------------------------------------------------------------------------------
// Отправка строки через UART (публичная обёртка)
//-----------------------------------------------------------------------------------------------------------
void cli_send_string(const char* str)
{
if (!str) return;
while (*str) {
lmcal_uart_write(CLI_UART_CHANNEL, (u16)*str++);
}
// Ждём завершения передачи, чтобы не смешивать вывод
uart_wait_tx_done();
}
//-----------------------------------------------------------------------------------------------------------
// Ожидание завершения передачи UART (флаг TC)
// Используем прямой доступ к регистрам, т.к. LMCAL не предоставляет такой функции.
//-----------------------------------------------------------------------------------------------------------
static void uart_wait_tx_done(void)
{
#if CLI_UART_CHANNEL == LMCAL_UART_CHANNEL0
#define CLI_USART USART5
#elif CLI_UART_CHANNEL == LMCAL_UART_CHANNEL1
#define CLI_USART USART0
#else
#define CLI_USART USART0
#endif
volatile u32 timeout = 100000;
while (usart_flag_get(CLI_USART, USART_FLAG_TC) == RESET && --timeout);
}
//-----------------------------------------------------------------------------------------------------------
// Формирование и отправка успешного ответа на команду.
// Формат: t[cmd][board_id][data...]\r
//-----------------------------------------------------------------------------------------------------------
// Формат: xx[cmd][data...]\r
static void send_response(u8 cmd, const u8* data, u8 len)
{
u8 buf[CLI_CMD_BUFFER_SIZE];
u16 idx = 0;
buf[idx++] = CLI_RESPONSE_PREFIX; // 't'
buf[idx++] = CLI_RESPONSE_PREFIX; // 't'
byte_to_hex(cmd, (char*)&buf[idx]); idx += 2; // код команды
byte_to_hex(cli_device_info.board_id, (char*)&buf[idx]); idx += 2; // board_id
for (u8 i = 0; i < len; i++) {
byte_to_hex(data[i], (char*)&buf[idx]); // данные в HEX
idx += 2;
buf[idx++] = data[i];
// byte_to_hex(data[i], (char*)&buf[idx]); // данные в HEX
// idx += 2;
}
buf[idx++] = CLI_MSG_TERMINATOR; // '\r'
for (u16 i = 0; i < idx; i++) {
lmcal_uart_write(CLI_UART_CHANNEL, buf[i]);
}
uart_wait_tx_done(); // гарантируем, что ответ отправлен полностью
}
//-----------------------------------------------------------------------------------------------------------
// Формирование и отправка сообщения об ошибке.
// Формат: e[cmd][board_id][err]\r
//-----------------------------------------------------------------------------------------------------------
// Формат: xx[cmd][err]\r
static void send_error(u8 cmd, u8 err)
{
u8 buf[8];
u16 idx = 0;
buf[idx++] = CLI_ERROR_PREFIX; // 'e'
buf[idx++] = CLI_ERROR_PREFIX; // 'e'
byte_to_hex(cmd, (char*)&buf[idx]); idx += 2;
byte_to_hex(cli_device_info.board_id, (char*)&buf[idx]); idx += 2;
byte_to_hex(err, (char*)&buf[idx]); idx += 2; // код ошибки
buf[idx++] = CLI_MSG_TERMINATOR;
for (u16 i = 0; i < idx; i++) {
lmcal_uart_write(CLI_UART_CHANNEL, buf[i]);
}
uart_wait_tx_done();
}
//-----------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Обработчик команды открытия соединения (CLI_CMD_OPEN = 0x00)
// Данные могут содержать пароль. Если пароль задан, проверяем его.
//-----------------------------------------------------------------------------------------------------------
static void handle_cmd_open(const u8* data, u8 len)
static i8 handle_cmd_open(const u8* data, u8 len)
{
(void)len;
i8 err = CLI_HND_OK_RX;
cli_connected = 1;
return err;
}
if (cli_connected) {
send_error(CLI_CMD_OPEN, CLI_ERR_INTERNAL);
return;
}
if (len != cli_password_len || memcmp(data, cli_password, len) != 0) {
send_error(CLI_CMD_OPEN, CLI_ERR_INTERNAL);
return;
}
cli_connected = true;
if (cli_get_tick) cli_last_activity = cli_get_tick();
send_response(CLI_CMD_OPEN, NULL, 0);
}
//-----------------------------------------------------------------------------------------------------------
// Обработчик команды закрытия соединения (CLI_CMD_CLOSE = 0x01)
//-----------------------------------------------------------------------------------------------------------
static void handle_cmd_close(const u8* data, u8 len)
static i8 handle_cmd_close(const u8* data, u8 len)
{
(void)data; (void)len;
cli_connected = false;
send_response(CLI_CMD_CLOSE, NULL, 0);
cli_cmd_index = 0;
memset(cli_cmd_buffer, 0, sizeof(cli_cmd_buffer));
i8 err = CLI_HND_OK_RX;
cli_connected = 0;
return err;
}
//-----------------------------------------------------------------------------------------------------------
// Обработчик команды проверки соединения (CLI_CMD_PING = 0x02)
// Обработчик команды проверки соединения (CLI_CMD_ECHO = 0x02)
// Возвращает эхо переданных данных.
//-----------------------------------------------------------------------------------------------------------
static void handle_cmd_ping(const u8* data, u8 len)
// data в hex
static i8 handle_cmd_echo(const u8* data, u8 len)
{
if (!cli_connected) {
send_error(CLI_CMD_PING, CLI_ERR_INTERNAL);
return;
}
send_response(CLI_CMD_PING, data, len);
i8 err = CLI_HND_OK_TX;
u8 tmp[CLI_DATA_MAX_SIZE];
memcpy(tmp, data, len);
cli_prepare_tx_data(tmp, len);
return err;
}
//-----------------------------------------------------------------------------------------------------------
// Обработчик команды получения информации об устройстве (CLI_CMD_GET_INFO = 0x0A)
// Формат ответа: board_id (1 байт), длина имени, имя, длина серийного, серийный,
// Формат ответа: длина имени, имя, длина серийного, серийный,
// длина версии, версия (ASCII).
//-----------------------------------------------------------------------------------------------------------
static void handle_cmd_get_info(const u8* data, u8 len)
static i8 handle_cmd_get_info(const u8* data, u8 len)
{
//i8 err = CLI_HND_OK_RX;
//return err;
(void)data; (void)len;
if (!cli_connected) {
send_error(CLI_CMD_GET_INFO, CLI_ERR_INTERNAL);
return;
}
u8 buf[CLI_DATA_MAX_SIZE];
u16 idx = 0;
u8 raw[CLI_DATA_MAX_SIZE]; // временный буфер для бинарных данных
u8 str_len;
u8 raw_idx = 0;
buf[idx++] = cli_device_info.board_id;
// if (!cli_connected) {
// send_error(CLI_CMD_GET_INFO, CLI_ERR_INTERNAL);
// return;
// }
// u8 buf[CLI_DATA_MAX_SIZE];
// u16 idx = 0;
// u8 str_len;
// buf[idx++] = cli_device_info.board_id;
// Имя устройства
str_len = strlen(cli_device_info.name);
if (str_len > 31) str_len = 31;
buf[idx++] = str_len;
memcpy(&buf[idx], cli_device_info.name, str_len);
idx += str_len;
if (str_len > 15) str_len = 15;
raw[raw_idx++] = str_len; // байт длины
memcpy(&raw[raw_idx], cli_device_info.name, str_len); // строка
raw_idx += str_len;
// Серийный номер
str_len = strlen(cli_device_info.serial);
if (str_len > 15) str_len = 15;
buf[idx++] = str_len;
memcpy(&buf[idx], cli_device_info.serial, str_len);
idx += str_len;
raw[raw_idx++] = str_len;
memcpy(&raw[raw_idx], cli_device_info.serial, str_len);
raw_idx += str_len;
char ver[16];
snprintf(ver, sizeof(ver), "%lu", tsp_dev_info.mcu_ver);
str_len = strlen(ver);
if (str_len > 15) str_len = 15;
buf[idx++] = str_len;
memcpy(&buf[idx], ver, str_len);
idx += str_len;
// char ver[16] = "0";
// snprintf(ver, sizeof(ver), "%lu", tcp_dev_info.mcu_ver);
send_response(CLI_CMD_GET_INFO, buf, idx);
}
// Версия
str_len = strlen(cli_device_info.version);
if (str_len > 15) str_len = 15;
raw[raw_idx++] = str_len;
memcpy(&raw[raw_idx], cli_device_info.version, str_len);
raw_idx += str_len;
//-----------------------------------------------------------------------------------------------------------
// Обработчик команды чтения памяти (CLI_CMD_READ_MEM = 0x14)
// Данные: 4 байта адреса (big-endian), 1 байт размера (1-64)
// Исправлена проверка границ: теперь учитывается размер
//-----------------------------------------------------------------------------------------------------------
static void handle_cmd_read_mem(const u8* data, u8 len)
{
if (!cli_connected) {
send_error(CLI_CMD_READ_MEM, CLI_ERR_INTERNAL);
return;
}
if (len != 5) {
send_error(CLI_CMD_READ_MEM, CLI_ERR_INVALID_ARG);
return;
// Преобразование в HEX-строку ===
u8 hex_idx = 0;
for (u8 i = 0; i < raw_idx; i++) {
byte_to_hex(raw[i], &buf_tmp[hex_idx]);
hex_idx += 2;
}
u32 addr = 0;
for (u8 i = 0; i < 4; i++) addr = (addr << 8) | data[i];
u8 size = data[4];
if (size == 0 || size > CLI_DATA_MAX_SIZE) {
send_error(CLI_CMD_READ_MEM, CLI_ERR_INVALID_ARG);
return;
}
data_size = hex_idx;
// send_response(CLI_CMD_GET_INFO, buf, idx);
// Проверка на переполнение адреса
u32 end_addr = addr + size;
if (end_addr < addr) {
send_error(CLI_CMD_READ_MEM, CLI_ERR_INTERNAL);
return;
}
return CLI_HND_OK_TX;
}
// Проверка допустимых диапазонов (GD32F450: Flash 2MB, SRAM 192KB)
bool valid = false;
if ((addr >= 0x08000000) && (end_addr <= 0x081FFFFF)) {
valid = true; // Flash (банк 0 + банк 1 = 2 МБ)
} else if ((addr >= 0x20000000) && (end_addr <= 0x2002FFFF)) {
valid = true; // SRAM (192 КБ)
}
if (valid) {
u8 buf[CLI_DATA_MAX_SIZE];
for (u8 i = 0; i < size; i++) {
buf[i] = *(volatile u8*)(addr + i);
}
send_response(CLI_CMD_READ_MEM, buf, size);
} else {
printf("READ_MEM error: addr=0x%08X, size=%d, end=0x%08X\n", addr, size, end_addr);
send_error(CLI_CMD_READ_MEM, CLI_ERR_INTERNAL);
}
}
// Колбек прерывания по приёму байта из UART.
// static void uart_rx_cb(void)
// {
// u16 rx = lmcal_uart_read(CLI_UART_CHANNEL);
// rb_add_item(&fifo_rx, (u8*)&rx);
// }
//-----------------------------------------------------------------------------------------------------------
// Обработчик команды записи памяти (CLI_CMD_WRITE_MEM = 0x15)
// Данные: 4 байта адреса, затем записываемые байты (1-64)
// Запись разрешена только в SRAM (0x20000000-0x2001FFFF) с учётом размера
//-----------------------------------------------------------------------------------------------------------
static void handle_cmd_write_mem(const u8* data, u8 len)
static void uart_rx_cb(void)
{
if (!cli_connected) {
send_error(CLI_CMD_WRITE_MEM, CLI_ERR_INTERNAL);
return;
}
if (len < 5) {
send_error(CLI_CMD_WRITE_MEM, CLI_ERR_INVALID_ARG);
return;
}
u16 rx = lmcal_uart_read(CLI_UART_CHANNEL);
u8 ch = (u8)(rx & 0xFF);
rb_add_item(&fifo_rx, &ch);
}
u32 addr = 0;
for (u8 i = 0; i < 4; i++) addr = (addr << 8) | data[i];
u8 size = len - 4;
if (size > CLI_DATA_MAX_SIZE) {
send_error(CLI_CMD_WRITE_MEM, CLI_ERR_INVALID_ARG);
return;
}
// Проверка на переполнение
u32 end_addr = addr + size;
if (end_addr < addr) {
send_error(CLI_CMD_WRITE_MEM, CLI_ERR_INTERNAL);
return;
}
// Запись разрешена только в SRAM
if ((addr >= 0x20000000) && (end_addr <= 0x2002FFFF)) {
for (u8 i = 0; i < size; i++) {
*(volatile u8*)(addr + i) = data[4 + i];
// Обработка буфера, 4 первых символа должны содержать код и длину данных
static i8 parse_cmd(char* buf, u8* cmd, u8* data)
{
i8 err = 0;
u8 cmd_tmp = hex_to_byte(&buf[0]);
u8 data_tmp = hex_to_byte(&buf[2]);
if ((cmd_tmp != 0xFF) && (data_tmp != 0xFF))
{
*cmd = cmd_tmp;
*data = data_tmp;
}
send_response(CLI_CMD_WRITE_MEM, NULL, 0);
} else {
printf("WRITE_MEM error: addr=0x%08X, size=%d, end=0x%08X\n", addr, size, end_addr);
send_error(CLI_CMD_WRITE_MEM, CLI_ERR_INTERNAL);
else
{
err = -1;
}
}
//-----------------------------------------------------------------------------------------------------------
// Разбор принятой строки и вызов соответствующего обработчика.
//-----------------------------------------------------------------------------------------------------------
static void parse_and_execute(void)
{
char* buf = cli_cmd_buffer;
u16 len = cli_cmd_index;
return err;
}
if (len < 6) { cli_cmd_index = 0; return; }
if (buf[0] != CLI_CMD_PREFIX) { cli_cmd_index = 0; return; }
if (buf[len - 1] != CLI_MSG_TERMINATOR) return;
u8 cmd = hex_to_byte(&buf[1]);
if (cmd == 0xFF) { cli_cmd_index = 0; return; }
u8 board_id = hex_to_byte(&buf[3]);
if (board_id == 0xFF) { cli_cmd_index = 0; return; }
if (board_id != cli_device_info.board_id) { cli_cmd_index = 0; return; }
// Преобразование буфера символов в буфер с hex
// Принимает: указатель на строку и кол-во символов
// Возвращает: буфер с байтами и кол-во байт
static void parse_data(char* buf, u8* size)
{
u8 tmp;
u8 tmp_size = *size / 2;
u16 data_start = 5;
u16 chars = len - 1 - data_start;
u8 data[CLI_DATA_MAX_SIZE + 4];
u8 data_len = 0;
if (chars > 0) {
if (chars % 2 != 0) { send_error(cmd, CLI_ERR_INVALID_ARG); cli_cmd_index = 0; return; }
data_len = chars / 2;
if (data_len > CLI_DATA_MAX_SIZE + 4) { send_error(cmd, CLI_ERR_INVALID_ARG); cli_cmd_index = 0; return; }
for (u8 i = 0; i < data_len; i++) {
data[i] = hex_to_byte(&buf[data_start + i * 2]);
if (data[i] == 0xFF) { send_error(cmd, CLI_ERR_INVALID_ARG); cli_cmd_index = 0; return; }
}
for (u8 i=0, j=0; i < *size; i+=2, j++)
{
tmp = hex_to_byte(&buf[i]);
buf[j] = tmp;
}
if (cli_get_tick) cli_last_activity = cli_get_tick();
if (cmd_handlers[cmd] != NULL) {
cmd_handlers[cmd](data, data_len);
} else {
send_error(cmd, CLI_ERR_UNKNOWN_CMD);
}
cli_cmd_index = 0;
*size = tmp_size;
}
//-----------------------------------------------------------------------------------------------------------
// Колбек прерывания по приёму байта из UART.
//-----------------------------------------------------------------------------------------------------------
static void uart_rx_cb(void)
{
u16 rx = lmcal_uart_read(CLI_UART_CHANNEL);
u8 ch = (u8)(rx & 0xFF);
rb_add_item(&fifo_rx, &ch);
}
//-----------------------------------------------------------------------------------------------------------
// Регистрация встроенных команд протокола.
//-----------------------------------------------------------------------------------------------------------
static void register_builtin_commands(void)
// Чтение байта из ФИФО. При ошибке возвращает -1
static u8 read_fifo(u8* byte)
{
const cli_command_t builtin[] = {
{ CLI_CMD_OPEN, handle_cmd_open },
{ CLI_CMD_CLOSE, handle_cmd_close },
{ CLI_CMD_PING, handle_cmd_ping },
{ CLI_CMD_GET_INFO, handle_cmd_get_info },
{ CLI_CMD_READ_MEM, handle_cmd_read_mem },
{ CLI_CMD_WRITE_MEM, handle_cmd_write_mem },
};
for (size_t i = 0; i < sizeof(builtin)/sizeof(builtin[0]); i++) {
cli_register_command(&builtin[i]);
i8 err=0;
if (rb_get_items_qty(&fifo_rx) > 0)
{
rb_get_item(&fifo_rx, byte);
}
else
{
err = -1;
}
return err;
}
//************************************ Публичные функции ****************************************************
//-----------------------------------------------------------------------------------------------------------
fun_res_t cli_protocol_init(void)
{
fifo_rx.item_size = sizeof(u8);
rb_init(&fifo_rx, fifo_rx_buf, sizeof(fifo_rx_buf));
rb_clear(&fifo_rx);
lmcal_uart_set_callback(CLI_UART_CHANNEL, CB_INT_RX_CALLBACK, uart_rx_cb);
lmcal_uart_enable(CLI_UART_CHANNEL, 1);
cli_connected = 0;
cli_cmd_index = 0;
cli_connected = false;
cli_last_activity = 0;
memset(cli_cmd_buffer, 0, sizeof(cli_cmd_buffer));
const char* dev_name = NULL;
const char* dev_serial = NULL;
const char* dev_version = "1.0.0";
safe_strncpy(cli_device_info.name, "CLI Device", sizeof(cli_device_info.name));
safe_strncpy(cli_device_info.serial, "00000000", sizeof(cli_device_info.serial));
safe_strncpy(cli_device_info.version, "1.0.0", sizeof(cli_device_info.version));
cli_device_info.board_id = 0;
// Пытаемся взять данные из заводской памяти
extern fm_factory_mem_data_t* fm_factory_mem_data;
if (fm_factory_mem_data != NULL) {
dev_name = fm_factory_mem_data->name;
dev_serial = fm_factory_mem_data->serial;
}
// Версия из макроса сборки
#ifdef PROJECT_VERSION
dev_version = PROJECT_VERSION;
#endif
cli_protocol_set_device_info(dev_name, dev_serial, dev_version);
// Инициализация встроенных обработчиков
memset(cmd_handlers, 0, sizeof(cmd_handlers));
register_builtin_commands();
const u8 default_pass[] = CLI_DEFAULT_PASSWORD_BYTES;
cli_protocol_set_password(default_pass, sizeof(default_pass));
cli_send_string("\r\n********** LTA PROTOCOL ENABLED **********\r\n");
cli_send_string("|> T000131333537 - open with password '1357' (hex bytes 0x31,0x33,0x35,0x37)\r\n");
cli_send_string("|> T0101 - close connection\r\n");
cli_send_string("|> T0201AABB - ping with data 0xAA,0xBB\r\n");
cli_send_string("|> T0A01 - get device info\r\n");
cli_send_string("|> T14010800000004 - read 4 bytes from 0x08000000\r\n");
cli_send_string("|> T14012000000004 - read 4 bytes from 0x20000000\r\n");
cli_send_string("|> T150120000000AABB - write 0xAA,0xBB to SRAM 0x20000000\r\n");
cli_send_string("******************************************************\r\n");
cli_send_string("|> ");
const cli_command_t builtin[] = {
{ CLI_CMD_OPEN, handle_cmd_open },
{ CLI_CMD_CLOSE, handle_cmd_close },
{ CLI_CMD_ECHO, handle_cmd_echo },
{ CLI_CMD_GET_INFO, handle_cmd_get_info },
};
for (size_t i = 0; i < sizeof(builtin)/sizeof(builtin[0]); i++) {
cmd_handlers[builtin[i].code] = builtin[i].handler;
}
return ERR_OK;
}
//-----------------------------------------------------------------------------------------------------------
fun_res_t cli_protocol_deinit(void)
{
lmcal_uart_set_callback(CLI_UART_CHANNEL, CB_INT_RX_CALLBACK, NULL);
cli_connected = false;
cli_connected = 0;
return ERR_OK;
}
//-----------------------------------------------------------------------------------------------------------
void cli_protocol_process(void)
{
static u8 buf_index;
// Текущая команда и размер данных для приема
static u8 cur_cmd;
static enum {
WAIT_CMD, // Ожидание первого символа команды
RECEIVING, // Приём символов команды
EXPECT_NL // Ожидание \n после \r
} state = WAIT_CMD;
RESET, // Сброс
WAIT_START, // Ожидание первого символа команды
WAIT_CMD, // Прием аргументов команды
WAIT_DATA, // Приём данных команды
WAIT_CR, // Ожидание \r
EXECUTE, // Выполнение
} state = RESET;
u8 tmp;
i8 err;
switch (state)
{
case RESET:
buf_index = 0;
cur_cmd = 0;
data_size = 0;
state = WAIT_START;
break;
while (rb_get_items_qty(&fifo_rx) > 0) {
u8 ch;
if (rb_get_item(&fifo_rx, &ch) != RB_RES_OK) continue;
switch (state) {
case WAIT_CMD:
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') continue;
cli_cmd_index = 0;
cli_cmd_buffer[cli_cmd_index++] = ch;
state = RECEIVING;
case WAIT_START:
if (read_fifo(&tmp) == 0)
{
if (tmp == CLI_CMD_PREFIX)
{
state = WAIT_CMD;
buf_index = 0;
}
}
break;
case RECEIVING:
if (ch == '\r' || ch == '\n') {
while (cli_cmd_index > 0 && (cli_cmd_buffer[cli_cmd_index - 1] == ' '
|| cli_cmd_buffer[cli_cmd_index - 1] == '\t'))
case WAIT_CMD:
if (read_fifo(&tmp) == 0)
{
cli_cmd_index--;
buf_tmp[buf_index++] = tmp;
if (buf_index == 4)
{
if (parse_cmd(buf_tmp, &cur_cmd, &data_size) == 0)
{
if ((data_size > 0) &&
(CLI_DATA_MAX_SIZE >= data_size) &&
(data_size % 2 == 0))
state = WAIT_DATA;
else state = WAIT_CR;
buf_index = 0;
}
else
{
// Ошибка в формате команды
state = RESET;
}
}
}
break;
if (cli_cmd_index > 0) {
cli_cmd_buffer[cli_cmd_index] = CLI_MSG_TERMINATOR;
cli_cmd_index++;
parse_and_execute();
cli_send_string("|> ");
case WAIT_DATA:
if (data_size > buf_index)
{
if (read_fifo(&tmp) == 0)
{
buf_tmp[buf_index++] = tmp;
}
state = EXPECT_NL;
} else {
if (cli_cmd_index < CLI_CMD_BUFFER_SIZE - 1) {
cli_cmd_buffer[cli_cmd_index++] = ch;
} else {
cli_cmd_index = 0;
state = WAIT_CMD;
}
else
{
state = WAIT_CR;
}
break;
case EXPECT_NL:
if (ch == '\n') {
state = WAIT_CMD;
} else {
cli_cmd_index = 0;
cli_cmd_buffer[cli_cmd_index++] = ch;
state = RECEIVING;
case WAIT_CR:
if (read_fifo(&tmp) == 0)
{
if (tmp == CLI_MSG_TERMINATOR)
{
if (cmd_handlers[cur_cmd] != NULL)
{
state = EXECUTE;
// Преобразование данных из строки в hex
if (data_size > 0) parse_data(buf_tmp, &data_size);
}
else
{
// Данная команда не зарегестрирована
send_error(cur_cmd, CLI_ERR_UNKNOWN_CMD);
state = RESET;
}
}
else
{
state = RESET;
}
}
break;
case EXECUTE:
#if 0
if (cli_connected == 0)
{
if (cur_cmd != CLI_CMD_OPEN)
{
send_error(cur_cmd, CLI_ERR_CON);
state = WAIT_START;
break;
}
}
#endif
err = cmd_handlers[cur_cmd](buf_tmp, data_size);
switch(err)
{
case(CLI_HND_OK_RX):
send_response(cur_cmd, NULL, 0);
state = WAIT_START;
break;
case(CLI_HND_OK_TX):
send_response(cur_cmd, buf_tmp, data_size);
state = WAIT_START;
break;
case(CLI_HND_ERR_ARG):
send_error(cur_cmd, CLI_ERR_INVALID_ARG);
state = WAIT_START;
break;
case(CLI_HND_ERR_INT):
send_error(cur_cmd, CLI_ERR_INTERNAL);
state = WAIT_START;
break;
case(CLI_HND_BUSY):
// Ждем, когда хзндл выполнится
break;
// Таймаут соединения (работает только если cli_get_tick != NULL)
if (cli_connected && cli_get_tick) {
if ((cli_get_tick() - cli_last_activity) > CLI_CONNECTION_TIMEOUT_MS) {
cli_connected = false;
default:
// хэндл вернул недопустимый ответ
send_error(cur_cmd, CLI_ERR_INTERNAL);
state = WAIT_START;
break;
}
break;
default:
while(1);
break;
}
}
//-----------------------------------------------------------------------------------------------------------
fun_res_t cli_protocol_set_device_info(const char* name, const char* serial, const char* ver, u8 board_id)
{
if (name) safe_strncpy(cli_device_info.name, name, sizeof(cli_device_info.name));
if (serial) safe_strncpy(cli_device_info.serial, serial, sizeof(cli_device_info.serial));
if (ver) safe_strncpy(cli_device_info.version, ver, sizeof(cli_device_info.version));
cli_device_info.board_id = board_id;
return ERR_OK;
}
//-----------------------------------------------------------------------------------------------------------
fun_res_t cli_protocol_set_board_id(u8 board_id)
{
cli_device_info.board_id = board_id;
return ERR_OK;
}
//-----------------------------------------------------------------------------------------------------------
u8 cli_protocol_get_board_id(void)
void cli_prepare_tx_data(u8* hex_data, u8 size)
{
return cli_device_info.board_id;
}
if (hex_data == NULL) return;
//-----------------------------------------------------------------------------------------------------------
void cli_protocol_set_password(const u8* pass, u8 len)
{
if (len > CLI_PASSWORD_MAX_LEN) len = CLI_PASSWORD_MAX_LEN;
memcpy(cli_password, pass, len);
cli_password_len = len;
cli_password[len] = '\0';
u8 tmp[3];
for (u8 i=0, j=0; i < size; i++, j+=2) {
// Преобразуем каждый байт в два hex‑символа (с ведущими нулями)
sprintf(tmp, "%02X", hex_data[i]);
buf_tmp[j] = tmp[0];
buf_tmp[j+1] = tmp[1];
}
data_size = size * 2;
}
//-----------------------------------------------------------------------------------------------------------
bool cli_protocol_verify_password(const u8* pass, u8 len)
fun_res_t cli_protocol_set_device_info(const char* name, const char* serial, const char* ver)
{
return (len == cli_password_len) && (memcmp(pass, cli_password, len) == 0);
if (name) safe_strncpy(cli_device_info.name, name, sizeof(cli_device_info.name));
if (serial) safe_strncpy(cli_device_info.serial, serial, sizeof(cli_device_info.serial));
if (ver) safe_strncpy(cli_device_info.version, ver, sizeof(cli_device_info.version));
return ERR_OK;
}
//-----------------------------------------------------------------------------------------------------------
fun_res_t cli_register_command(const cli_command_t* cmd)
{
if (cmd == NULL || cmd->handler == NULL) {
return ERR_INVALID_ARGUMENT;
}
if ((int)cmd->code >= CLI_MAX_COMMANDS) {
if (cmd == NULL || cmd->handler == NULL || (CLI_INT_CMD_NUM > cmd->code))
{
return ERR_INVALID_ARGUMENT;
}
if (cmd_handlers[cmd->code] != NULL) {
if (cmd_handlers[cmd->code] != NULL)
{
return ERR_RUNTIME_ERROR;
}
cmd_handlers[cmd->code] = cmd->handler;
return ERR_OK;
}
void cli_unregister_command(u8 code)
{
if (CLI_INT_CMD_NUM > code)
{
cmd_handlers[code] = NULL;
}
}
//*********************************************** КОНЕЦ ФАЙЛА ***********************************************
......@@ -3,106 +3,78 @@
#include "cli_protocol_cfg.h"
#include "l_macro_types.h"
#include <stdbool.h>
//************************************ Константы протокола *************************************************
#define CLI_BROADCAST_ID 0xFFU // Широковещательный адрес (команды для всех плат без ответа)
//************************************ Коды команд (по протоколу 1.1) *************************************
typedef enum {
CLI_CMD_OPEN = 0x00U, // T00 - открыть соединение (с паролем)
CLI_CMD_CLOSE = 0x01U, // T01 - закрыть соединение
CLI_CMD_PING = 0x02U, // T02 - проверка соединения (ping)
CLI_CMD_GET_INFO = 0x0AU, // T0A - получить информацию об устройстве (имя, серийный, версия)
CLI_CMD_READ_MEM = 0x14U, // T14 - чтение памяти (адрес 4 байта + размер 1 байт)
CLI_CMD_WRITE_MEM = 0x15U, // T15 - запись памяти (адрес 4 байта + данные, только SRAM)
CLI_CMD_ECHO = 0x02U, // T02 - проверка соединения (ping)
CLI_CMD_GET_INFO = 0x03U, // T03 - получить информацию об устройстве (имя, серийный, версия)
} cli_cmd_t;
//************************************ Коды ошибок (только 00-03 по протоколу) ****************************
// Коды ошибок хэндла команды
typedef enum {
CLI_ERR_NONE = 0x00U, // Нет ошибки
CLI_ERR_UNKNOWN_CMD = 0x01U, // Неизвестная команда
CLI_ERR_INVALID_ARG = 0x02U, // Неверный формат или значение аргумента
CLI_ERR_INTERNAL = 0x03U, // Внутренняя ошибка устройства
} cli_error_t;
//************************************ Информация об устройстве **********************************************
//! @brief Структура с информацией об устройстве, возвращаемая командой T0A
CLI_HND_OK_RX = 0, // Прием данных успешен
CLI_HND_OK_TX, // Хэндл подготовил ответ для передачи
CLI_HND_ERR_ARG, // Неверный формат или значение аргумента
CLI_HND_ERR_INT, // Ошибка выполнения
CLI_HND_BUSY, // Хэндл пока не может быть выполнен
} cli_hnd_err_t;
// Структура с информацией об устройстве
typedef struct {
char name[32]; // Имя устройства (ASCII, до 31 символа + \\0)
char serial[16]; // Серийный номер (ASCII, до 15 символов + \\0)
char version[16]; // Версия ПО (ASCII, до 15 символов + \\0)
u8 board_id; // Уникальный идентификатор платы (0–254)
char name[16]; // Имя устройства (ASCII, до 15 символа + \0)
char serial[16]; // Серийный номер (ASCII, до 15 символов + \0)
char version[16]; // Версия команд CLI (ASCII, до 15 символов + \0)
} cli_device_info_t;
//************************************ Тип обработчика команды **********************************************
//! @brief Прототип функции-обработчика команды
//! @param data Указатель на сырые данные команды (после board_id)
//! @param len Длина данных в байтах
typedef void (*cli_cmd_handler_t)(const u8* data, u8 len);
//************************************ Структура регистрируемой команды **************************************
//! @brief Структура для регистрации пользовательской команды
// Прототип функции-обработчика команды
// - data Указатель на сырые данные команды (после board_id)
// - len Длина данных в байтах
// Выозвращает код ошибки (cli_hnd_err_t)
typedef i8 (*cli_cmd_handler_t)(const u8* data, u8 len);
// Структура для регистрации пользовательской команды
typedef struct {
u8 code; // Код команды (0x00–0xFF)
cli_cmd_handler_t handler; // Указатель на функцию-обработчик
} cli_command_t;
//************************************ Публичные функции ****************************************************
//---------------------------------------------------------------------
//! @brief Инициализация протокола CLI
//! @return ERR_OK при успехе, иначе код ошибки
fun_res_t cli_protocol_init(void);
//---------------------------------------------------------------------
//! @brief Деинициализация протокола CLI
//! @return ERR_OK при успехе, иначе код ошибки
fun_res_t cli_protocol_deinit(void);
//---------------------------------------------------------------------
//! @brief Основной цикл обработки (вызывать в главном цикле программы)
void cli_protocol_process(void);
//---------------------------------------------------------------------
//! @brief Отправка строки через UART (с ожиданием завершения передачи)
//! @param str Указатель на нуль-терминированную строку
void cli_send_string(const char* str);
void cli_prepare_tx_data(u8* hex_data, u8 size);
//---------------------------------------------------------------------
//! @brief Установка информации об устройстве
//! @param name Имя устройства (до 31 символа)
//! @param name Имя устройства (до 15 символа)
//! @param serial Серийный номер (до 15 символов)
//! @param ver Версия ПО (до 15 символов)
//! @param board_id Идентификатор платы (0–254)
//! @return ERR_OK при успехе
fun_res_t cli_protocol_set_device_info(const char* name, const char* serial, const char* ver, u8 board_id);
fun_res_t cli_protocol_set_device_info(const char* name, const char* serial, const char* ver);
//---------------------------------------------------------------------
//! @brief Установка board_id устройства
//! @param board_id Идентификатор платы (0–254)
//! @return ERR_OK при успехе
fun_res_t cli_protocol_set_board_id(u8 board_id);
//---------------------------------------------------------------------
//! @brief Получение текущего board_id устройства
//! @return Идентификатор платы
u8 cli_protocol_get_board_id(void);
//---------------------------------------------------------------------
//! @brief Установка пароля для открытия соединения
//! @param pass Указатель на массив байт пароля (до 15 байт)
//! @param len Длина пароля в байтах
void cli_protocol_set_password(const u8* pass, u8 len);
//---------------------------------------------------------------------
//! @brief Проверка пароля
//! @param pass Указатель на массив байт пароля
//! @param len Длина пароля в байтах
//! @return true – пароль верен, false – неверен
bool cli_protocol_verify_password(const u8* pass, u8 len);
//---------------------------------------------------------------------
//! @brief Регистрация новой команды
//! @param cmd Указатель на структуру с кодом и обработчиком
//! @return ERR_OK при успехе, ERR_INVALID_ARGUMENT – неверные параметры,
......@@ -110,4 +82,9 @@ bool cli_protocol_verify_password(const u8* pass, u8 len);
fun_res_t cli_register_command(const cli_command_t* cmd);
//! @brief Обнулить регистрацию указанной команды
//! @param cmd Код команды
void cli_unregister_command(u8 code);
#endif
......@@ -6,18 +6,26 @@
//************************************ Конфигурация UART ****************************************************
#define CLI_UART_CHANNEL (LMCAL_UART_CHANNEL1)
//************************************ Размеры буферов ******************************************************
#define CLI_CMD_BUFFER_SIZE (128) // Размер буфера команды
#define CLI_DATA_MAX_SIZE (64) // Максимальный размер данных
#define CLI_MAX_COMMANDS 255 // Максимальное количество регистрируемых команд (0x00..0xFF)
#define CLI_INT_CMD_NUM (10) // Количество встроенных команд
//************************************ Таймауты ************************************************************
#define CLI_CONNECTION_TIMEOUT_MS (20000U) // Таймаут соединения 20 секунд
//************************************ Пароль по умолчанию **************************************************
// Пароль по умолчанию "1357"
#define CLI_DEFAULT_PASSWORD_BYTES {0x31, 0x33, 0x35, 0x37}
#define CLI_DEFAULT_PASSWORD_LEN 4
#define CLI_MAX_COMMANDS 256 // Максимальное количество регистрируемых команд (0x00..0xFF)
#define CLI_PASSWORD_MAX_LEN 15 // Максимальная длина пароля в байтах
#endif
......@@ -26,7 +26,6 @@ void main(void)
// Инициализация LTA протокола
cli_protocol_init();
cli_protocol_set_device_info("LTA27 MCU", "LTA27V2-001", "1.0.0", 0x01);
while(1)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment