diff --git a/.cproject b/.cproject
index d637684..ec25431 100644
--- a/.cproject
+++ b/.cproject
@@ -14,7 +14,7 @@
-
+
@@ -60,13 +60,18 @@
-
+
@@ -120,7 +125,9 @@
-
+
+
+
@@ -145,6 +152,8 @@
+
+
@@ -166,4 +175,5 @@
+
diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml
index 6343541..f1457d8 100644
--- a/.settings/language.settings.xml
+++ b/.settings/language.settings.xml
@@ -5,7 +5,7 @@
-
+
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..7e6d04e
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,22 @@
+eclipse.preferences.version=1
+encoding//APP/bsp_wf5803.c=GBK
+encoding//APP/include/bsp_wf5803.h=GBK
+encoding//APP/peripheral_main.c=GBK
+encoding//HAL/MCU.c=GBK
+encoding//HAL/SLEEP.c=GBK
+encoding//HAL/include/CONFIG.h=GBK
+encoding//LIB/CH58xBLE_LIB.h=GBK
+encoding//LIB/CH58xBLE_ROM.h=GBK
+encoding//Ld/Link.ld=GBK
+encoding//RVMSIS/core_riscv.h=GBK
+encoding//Startup/startup_CH585.S=GBK
+encoding//StdPeriphDriver/CH58x_sys.c=GBK
+encoding//StdPeriphDriver/CH58x_uart1.c=GBK
+encoding//StdPeriphDriver/inc/CH585SFR.h=GBK
+encoding//bsp/inc/bsp_ml307r.h=GBK
+encoding//bsp/inc/bsp_uart.h=GBK
+encoding//bsp/src/bsp_ml307r.c=GBK
+encoding//bsp/src/bsp_tim.c=GBK
+encoding//bsp/src/bsp_uart.c=GBK
+encoding//common/lwrb/lwrb.c=GBK
+encoding//common/lwrb/lwrb.h=GBK
diff --git a/APP/peripheral_main.c b/APP/peripheral_main.c
index d2e1ccb..5da4bce 100644
--- a/APP/peripheral_main.c
+++ b/APP/peripheral_main.c
@@ -16,8 +16,19 @@
#include "HAL.h"
#include "gattprofile.h"
#include "peripheral.h"
-
#include "bsp_wf5803.h"
+#include "bsp_ml307r.h"
+#include "bsp_uart.h"
+#include "log.h"
+
+#undef LOG_ENABLE
+#define LOG_ENABLE 1
+
+#define SYSTICK_INTERVAL (1)
+
+
+uint8_t TxBuff[] = "This is a uart3 tx example\r\n";
+
/*********************************************************************
* GLOBAL TYPEDEFS
@@ -39,10 +50,11 @@ __HIGH_CODE
__attribute__((noinline))
void Main_Circulation()
{
- PRINT("Main_Circulation\n");
+ logDebug("Main_Circulation\n");
while(1)
{
TMOS_SystemProcess();
+
}
}
@@ -60,45 +72,37 @@ int main(void)
#endif
HSECFG_Capacitance(HSECap_18p);
SetSysClock(CLK_SOURCE_HSE_PLL_62_4MHz);
-#if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE)
+ SysTick_Config( GetSysClock() / 1000 * SYSTICK_INTERVAL); //设定嘀嗒时间1ms
+#if(defined(HAL_SLEEP)) && (HAL_SLEEP == FALSE)
GPIOA_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
GPIOB_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
#endif
#ifdef DEBUG
- // GPIOA_SetBits(GPIO_Pin_14);
- // GPIOPinRemap(ENABLE, RB_PIN_UART0);
- // GPIOA_ModeCfg(GPIO_Pin_14, GPIO_ModeOut_PP_5mA);
- // UART0_DefInit();
- // GPIOA_SetBits(GPIO_Pin_9);
- // // GPIOPinRemap(ENABLE, RB_PIN_UART1);
- // GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
- // // UART0_DefInit();
- // UART1_DefInit();
- /* 配置串口1:先配置IO口模式,再配置串口 */
- GPIOA_SetBits(GPIO_Pin_9);
- GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); // RXD-配置上拉输入
- GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); // TXD-配置推挽输出,注意先让IO口输出高电平
- UART1_DefInit();
+ BSP_UART3_Init();
+
+ logDebug("%s", TxBuff);
#endif
- PRINT("Start @ChipID=%02X\n", R8_CHIP_ID);
- PRINT("%s\n", VER_LIB);
- CH58x_BLEInit();
- PRINT("BLE init ok\n");
- HAL_Init();
- PRINT("HAL init ok\n");
- GAPRole_PeripheralInit();
- PRINT("GAP init ok\n");
- Peripheral_Init();
- PRINT("Peripheral init ok\n");
+ BSP_Ml307r_Init();
+ logDebug("Start @ChipID=%02X\n", R8_CHIP_ID);
+ logDebug("%s\n", VER_LIB);
- BSP_PRESS_Init();
- PRINT("BSP init ok\n");
+// CH58x_BLEInit();
+// logDebug("BLE init ok\n");
+// HAL_Init();
+// logDebug("HAL init ok\n");
+// GAPRole_PeripheralInit();
+// logDebug("GAP init ok\n");
+// Peripheral_Init();
+// logDebug("Peripheral init ok\n");
+
+// BSP_PRESS_Init();
+// logDebug("BSP init ok\n");
Main_Circulation();
- PRINT("Main_Circulation\n");
+ logDebug("Main_Circulation\n");
}
/******************************** endfile @ main ******************************/
diff --git a/HAL/MCU.c b/HAL/MCU.c
index c2f2c0f..82faba7 100644
--- a/HAL/MCU.c
+++ b/HAL/MCU.c
@@ -13,6 +13,7 @@
/******************************************************************************/
/* 头文件包含 */
#include "HAL.h"
+#include "bsp_uart.h"
tmosTaskID halTaskID;
uint32_t g_LLE_IRQLibHandlerLocation;
@@ -233,6 +234,8 @@ void HAL_Init()
#if(defined HAL_SLEEP) && (HAL_SLEEP == TRUE)
HAL_SleepInit();
#endif
+
+
#if(defined HAL_LED) && (HAL_LED == TRUE)
HAL_LedInit();
#endif
diff --git a/HAL/SLEEP.c b/HAL/SLEEP.c
index 0fb0abd..c89a07b 100644
--- a/HAL/SLEEP.c
+++ b/HAL/SLEEP.c
@@ -28,7 +28,7 @@
__HIGH_CODE
uint32_t CH58x_LowPower(uint32_t time)
{
-#if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE)
+#if(defined(HAL_SLEEP)) && (HAL_SLEEP == FALSE)
volatile uint32_t i;
uint32_t time_tign, time_sleep, time_curr;
unsigned long irq_status;
diff --git a/Ld/Link.ld b/Ld/Link.ld
index f1e221e..7577851 100644
--- a/Ld/Link.ld
+++ b/Ld/Link.ld
@@ -66,6 +66,9 @@ SECTIONS
*(.text.*)
*(.rodata)
*(.rodata*)
+ _shell_command_start = .;
+ KEEP (*(shellCommand))
+ _shell_command_end = .;
*(.sdata2.*)
*(.glue_7)
*(.glue_7t)
diff --git a/StdPeriphDriver/CH58x_sys.c b/StdPeriphDriver/CH58x_sys.c
index b44b1d1..ec954a6 100644
--- a/StdPeriphDriver/CH58x_sys.c
+++ b/StdPeriphDriver/CH58x_sys.c
@@ -11,6 +11,8 @@
*******************************************************************************/
#include "CH58x_common.h"
+#include "bsp_uart.h"
+
volatile MachineMode_Call_func gs_machine_mode_func;
@@ -543,31 +545,37 @@ void mDelaymS(uint16_t t)
}while(--t);
}
-#ifdef DEBUG
+//#ifdef DEBUG
+//int _write(int fd, char *buf, int size)
+//{
+// int i;
+// for(i = 0; i < size; i++)
+// {
+//#if DEBUG == Debug_UART0
+// while(R8_UART0_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
+// R8_UART0_THR = *buf++; /* 发送数据 */
+//#elif DEBUG == Debug_UART1
+// while(R8_UART1_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
+// R8_UART1_THR = *buf++; /* 发送数据 */
+//#elif DEBUG == Debug_UART2
+// while(R8_UART2_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
+// R8_UART2_THR = *buf++; /* 发送数据 */
+//#elif DEBUG == Debug_UART3
+// while(R8_UART3_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
+// R8_UART3_THR = *buf++; /* 发送数据 */
+//#endif
+// }
+// return size;
+//}
+
+//#endif
+
int _write(int fd, char *buf, int size)
{
- int i;
- for(i = 0; i < size; i++)
- {
-#if DEBUG == Debug_UART0
- while(R8_UART0_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
- R8_UART0_THR = *buf++; /* 发送数据 */
-#elif DEBUG == Debug_UART1
- while(R8_UART1_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
- R8_UART1_THR = *buf++; /* 发送数据 */
-#elif DEBUG == Debug_UART2
- while(R8_UART2_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
- R8_UART2_THR = *buf++; /* 发送数据 */
-#elif DEBUG == Debug_UART3
- while(R8_UART3_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
- R8_UART3_THR = *buf++; /* 发送数据 */
-#endif
- }
+ BSP_Uart3_Send_Data(buf, size);
return size;
}
-#endif
-
/*********************************************************************
* @fn _sbrk
*
diff --git a/bsp/inc/bsp_ml307r.h b/bsp/inc/bsp_ml307r.h
new file mode 100644
index 0000000..9fddfdf
--- /dev/null
+++ b/bsp/inc/bsp_ml307r.h
@@ -0,0 +1,40 @@
+#ifndef __BSP_ML307R_H__
+#define __BSP_ML307R_H__
+
+
+#include "CH58x_common.h"
+
+/*4G 模块控制引脚*/
+#define ENABLE_3_8_V GPIO_Pin_3 //PB3
+
+#define USIM_DECT_PIN GPIO_Pin_5 //PB5
+#define ML307_PWR_PIN GPIO_Pin_6 //PB6
+#define ML307_RST_PIN GPIO_Pin_7 //PB7
+
+#define ML307_UART_TX_PIN GPIO_Pin_13 //PB13
+#define ML307_UART_RX_PIN GPIO_Pin_12 //PB12
+
+void BSP_Ml307r_Init(void);
+void Ml307r_Loop(void);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#endif //!@__BSP_ML307R_H__
+
+
+
+
diff --git a/bsp/inc/bsp_tim.h b/bsp/inc/bsp_tim.h
new file mode 100644
index 0000000..23eeb7d
--- /dev/null
+++ b/bsp/inc/bsp_tim.h
@@ -0,0 +1,28 @@
+#ifndef __BSP_TIM_H__
+#define __BSP_TIM_H__
+
+
+#include "CH58x_common.h"
+
+
+
+
+
+
+
+uint32_t BSP_Get_Tick(void);
+
+
+
+
+
+
+
+
+
+
+
+
+
+#endif //!@__BSP_TIM_H__
+
diff --git a/bsp/inc/bsp_uart.h b/bsp/inc/bsp_uart.h
new file mode 100644
index 0000000..5685e34
--- /dev/null
+++ b/bsp/inc/bsp_uart.h
@@ -0,0 +1,37 @@
+#ifndef __BSP_UART_H__
+#define __BSP_UART_H__
+
+
+#include "CH585SFR.h"
+
+
+#define UART3_RX_PIN GPIO_Pin_20 //PB20 日志打印
+#define UART3_TX_PIN GPIO_Pin_21 // PB21
+
+
+void BSP_UART1_Init(void);
+unsigned int BSP_Uart1_Receive_Data(void *buf, unsigned int len);
+unsigned int BSP_Uart1_Send_Data(const void *buf, unsigned int len);
+
+void BSP_UART3_Init(void);
+unsigned int BSP_Uart3_Receive_Data(void *buf, unsigned int len);
+unsigned int BSP_Uart3_Send_Data(const void *buf, unsigned int len);
+void BSP_Shell_Loop(void);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#endif
+
diff --git a/bsp/src/bsp_ml307r.c b/bsp/src/bsp_ml307r.c
new file mode 100644
index 0000000..7b34ef3
--- /dev/null
+++ b/bsp/src/bsp_ml307r.c
@@ -0,0 +1,266 @@
+#include "bsp_ml307r.h"
+#include "at_chat.h"
+#include "at_port.h"
+#include "bsp_uart.h"
+#include "log.h"
+#include "stdio.h"
+#include "CONFIG.h"
+
+
+
+#undef LOG_ENABLE
+#define LOG_ENABLE 1
+
+#define ML307_PERIODIC_EVT (0x0001 << 0)
+
+static tmosTaskID ml307_task_id = INVALID_TASK_ID;
+
+at_obj_t *ml307r_obj;
+
+void At_Debug(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+
+}
+
+void Ml307_Delay_Ms(uint16_t ms)
+{
+ DelayMs(ms);
+}
+
+void Ml307_Delay_us(uint16_t us)
+{
+ DelayUs(us);
+}
+
+static const at_adapter_t at_adapter =
+{
+ .write = BSP_Uart1_Send_Data,
+ .read = BSP_Uart1_Receive_Data,
+ .debug = At_Debug,
+ .recv_bufsize = 256,
+ .urc_bufsize = 512
+};
+
+//模块准备就绪
+static int URC_Module_Ready_Cb(at_urc_info_t *info)
+{
+ logDebug("module ready complete\r\n");
+
+ return 0;
+}
+
+
+/**
+ * @brief urc订阅表
+ */
+static const urc_item_t urc_table[] =
+{
+ {.prefix = "+CPIN: READY", .endmark = '\n', .handler = URC_Module_Ready_Cb},//模块准备就绪
+};
+
+static void Ml307_Power_On(void)
+{
+#define ML307_POWER_ON_TIME 3
+
+ GPIOB_SetBits(ML307_PWR_PIN);
+ Ml307_Delay_Ms(ML307_POWER_ON_TIME * 1000);
+ GPIOB_ResetBits(ML307_PWR_PIN);
+}
+
+static void Ml307_Power_Off(void)
+{
+#define M307_POWER_OFF_TIME 4
+ GPIOB_SetBits(ML307_PWR_PIN);
+ Ml307_Delay_Ms(M307_POWER_OFF_TIME * 1000);
+ GPIOB_ResetBits(ML307_PWR_PIN);
+ Ml307_Delay_Ms(100);
+}
+
+int Ml307r_Gpio_Init(void)
+{
+ GPIOB_SetBits(ENABLE_3_8_V);
+ GPIOB_ModeCfg(ENABLE_3_8_V, GPIO_ModeOut_PP_5mA);
+
+ GPIOB_ResetBits(ML307_RST_PIN);
+ GPIOB_ModeCfg(ML307_RST_PIN, GPIO_ModeOut_PP_5mA);
+
+ GPIOB_ModeCfg(USIM_DECT_PIN, GPIO_ModeIN_Floating);
+
+ GPIOB_ModeCfg(ML307_PWR_PIN, GPIO_ModeOut_PP_5mA);
+
+
+ return 0;
+}
+
+static int Module_Read_State(at_env_t *e)
+{
+ switch (e->state)
+ {
+ case 0:
+ e->obj->adap->debug("Check if the Module is ready\r\n");
+ e->println(e, "AT");
+ e->reset_timer(e);
+ e->state++;
+ break;
+ case 1:
+ if (e->contains(e, "OK"))
+ {
+ e->recvclr(e);
+ e->finish(e, AT_RESP_OK);
+ e->obj->adap->debug("Module is ready\r\n");
+ }
+ if (e->is_timeout(e, 5000))
+ {
+ e->state--;
+ if (++e->i > 3)
+ {
+ e->obj->adap->debug("Module error\r\n");
+ e->finish(e, AT_RESP_ERROR);
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+static void Module_Get_Imei_Cb(at_response_t *r)
+{
+ char imei[16] = {0};
+ if (r->code == AT_RESP_OK)
+ {
+ if (sscanf(r->prefix, "+GSN:%s", imei) == 1)
+ {
+ r->obj->adap->debug("imei: %s\r\n", r->prefix);
+ }
+ }
+ else
+ {
+ r->obj->adap->debug("'AT+GSN=1' command response failed!\r\n");
+ }
+}
+
+/*
+ * @brief 获取IMEI数据
+ * @return
+ */
+static void Module_Get_Imei(void)
+{
+ at_attr_t attr;
+ at_attr_deinit(&attr);
+ attr.prefix = "+GSN:";
+ attr.cb = Module_Get_Imei_Cb;
+ at_send_singlline(ml307r_obj, &attr, "AT+GSN=1");
+}
+
+static void BSP_Read_Module(void)
+{
+ at_do_work(ml307r_obj, NULL, Module_Read_State); // 重启后生效
+}
+
+static void Module_Get_Imsi_Cb(at_response_t *r)
+{
+ char imsi[60] = {0};
+ if (r->code == AT_RESP_OK)
+ {
+ if (sscanf(r->prefix, "%s\r\n", imsi) == 1)
+ {
+ r->obj->adap->debug("imsi:%s\r\n", imsi);
+ }
+ }
+ else
+ {
+ r->obj->adap->debug("'AT+CIMI' command response failed!\r\n");
+ }
+}
+
+/*
+ * @brief 获取IMSI数据
+ * @return
+ */
+static void Module_Get_Imsi(void)
+{
+ at_attr_t attr;
+ at_attr_deinit(&attr);
+ attr.prefix = "46";
+ attr.suffix = "\r\n";
+ attr.cb = Module_Get_Imsi_Cb;
+ at_send_singlline(ml307r_obj, &attr, "AT+CIMI");
+}
+
+static void Module_Get_Iccid_Cb(at_response_t *r)
+{
+ char iccid[21] = {0};
+
+ if (r->code == AT_RESP_OK)
+ {
+ if (sscanf(r->prefix, "+MCCID: %s\r\n", iccid) == 1)
+ {
+ r->obj->adap->debug("iccid: %s\r\n", iccid);
+ }
+ }
+ else
+ {
+ r->obj->adap->debug("'AT+QCCID' command response failed!\r\n");
+ }
+}
+
+/*
+ * @brief 获取ICCID数据
+ * @return
+ */
+static void Module_Get_Iccid(void)
+{
+ at_attr_t attr;
+ at_attr_deinit(&attr);
+ attr.prefix = "+MCCID:";
+ attr.cb = Module_Get_Iccid_Cb;
+ at_send_singlline(ml307r_obj, &attr, "AT+MCCID");
+}
+
+
+void Ml307r_Loop(void)
+{
+ at_obj_process(ml307r_obj);
+}
+
+
+__HIGH_CODE
+__attribute__((noinline))
+uint16_t Ml307r_Handle(uint8_t task_id, uint16_t events)
+{
+ if (events & ML307_PERIODIC_EVT)
+ {
+ Ml307r_Loop();
+ tmos_start_task(ml307_task_id, ML307_PERIODIC_EVT, MS1_TO_SYSTEM_TIME(5));
+ return (events ^ ML307_PERIODIC_EVT);
+
+ }
+ return 0;
+}
+
+
+void BSP_Ml307r_Init(void)
+{
+ Ml307r_Gpio_Init();
+ Ml307_Power_On();
+ BSP_UART1_Init();
+
+ ml307r_obj = at_obj_create(&at_adapter);//Create AT communication object
+ at_obj_set_urc(ml307r_obj, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
+
+ ml307_task_id = TMOS_ProcessEventRegister(Ml307r_Handle);
+ tmos_set_event(ml307_task_id, ML307_PERIODIC_EVT);
+
+ BSP_Read_Module();
+ Module_Get_Imei();
+ Module_Get_Imsi();
+ Module_Get_Iccid();
+
+}
+
+
+
diff --git a/bsp/src/bsp_tim.c b/bsp/src/bsp_tim.c
new file mode 100644
index 0000000..ba2db63
--- /dev/null
+++ b/bsp/src/bsp_tim.c
@@ -0,0 +1,30 @@
+#include "bsp_tim.h"
+
+
+// tick_1ms_cnt在SysTick_Handler()中1ms +1
+volatile uint32_t tick_1ms_cnt = 0;
+
+/**
+ * @description: 以SysTick(1ms)为基础
+ * @return {uint32_t}
+ */
+uint32_t BSP_Get_Tick(void)
+{
+ /* Platform implementation */
+ return tick_1ms_cnt;
+}
+
+
+// SysTick中断函数
+__INTERRUPT
+__HIGH_CODE
+void SysTick_Handler()
+{
+ static uint8_t cnt_ms = 0;
+
+ SysTick->SR = 0; // 清除中断标志
+ tick_1ms_cnt++;
+
+ cnt_ms++;
+}
+
diff --git a/bsp/src/bsp_uart.c b/bsp/src/bsp_uart.c
new file mode 100644
index 0000000..d027775
--- /dev/null
+++ b/bsp/src/bsp_uart.c
@@ -0,0 +1,227 @@
+#include "bsp_uart.h"
+#include "bsp_ml307r.h"
+#include "lwrb.h"
+#include "CH58x_uart.h"
+#include "shell_port.h"
+
+
+
+
+#define UART1_RX_BUFFER_LENGTH 512U
+#define UART1_TX_BUFFER_LENGTH 512U
+
+#define UART3_RX_BUFFER_LENGTH 1024U
+#define UART3_TX_BUFFER_LENGTH 1024U
+
+
+lwrb_t uart1_rx_t;
+lwrb_t uart1_tx_t;
+uint8_t uart1_tx_buf[UART1_TX_BUFFER_LENGTH] = {0};
+uint8_t uart1_rx_buf[UART1_RX_BUFFER_LENGTH] = {0};
+
+lwrb_t uart3_rx_t;
+lwrb_t uart3_tx_t;
+uint8_t uart3_tx_buf[UART3_TX_BUFFER_LENGTH] = {0};
+uint8_t uart3_rx_buf[UART3_RX_BUFFER_LENGTH] = {0};
+
+
+//串口接收
+unsigned int BSP_Uart1_Receive_Data(void *buf, unsigned int len)
+{
+ return lwrb_read(&uart1_rx_t, buf, len);
+}
+
+//串口发送数据
+unsigned int BSP_Uart1_Send_Data(const void *buf, unsigned int len)
+{
+ unsigned int ret;
+
+ ret = lwrb_write(&uart1_tx_t, buf, len);
+ UART1_INTCfg(ENABLE, RB_IER_THR_EMPTY);
+ return ret;
+}
+
+//串口接收
+unsigned int BSP_Uart3_Receive_Data(void *buf, unsigned int len)
+{
+ return lwrb_write(&uart3_rx_t, buf, len);
+}
+
+//串口发送数据
+unsigned int BSP_Uart3_Send_Data(const void *buf, unsigned int len)
+{
+ unsigned int ret;
+
+ ret = lwrb_write(&uart3_tx_t, buf, len);
+ UART3_INTCfg(ENABLE, RB_IER_THR_EMPTY);
+ return ret;
+}
+
+
+/**
+ * \brief Buffer event function
+ */
+static void Uart1_evt_fn(struct lwrb* buff, lwrb_evt_type_t evt, lwrb_sz_t bp)
+{
+ switch (evt)
+ {
+ case LWRB_EVT_RESET:
+ printf("[EVT] Buffer reset event!\r\n");
+ break;
+ case LWRB_EVT_READ:
+ printf("[EVT] Buffer read event: %d byte(s)!\r\n", (int)bp);
+ break;
+ case LWRB_EVT_WRITE:
+ printf("[EVT] Buffer write event: %d byte(s)!\r\n", (int)bp);
+ break;
+ default: break;
+ }
+}
+
+void UART1_FifoInit(void)
+{
+ lwrb_init(&uart1_tx_t, uart1_tx_buf, sizeof(uart1_tx_buf));
+ lwrb_init(&uart1_rx_t, uart1_rx_buf, sizeof(uart1_rx_buf));
+
+// lwrb_set_evt_fn(&uart1_rx_t, Uart1_evt_fn);
+}
+
+void UART3_FifoInit(void)
+{
+ lwrb_init(&uart3_tx_t, uart3_tx_buf, sizeof(uart3_tx_buf));
+ lwrb_init(&uart3_rx_t, uart3_rx_buf, sizeof(uart3_rx_buf));
+
+ lwrb_set_evt_fn(&uart1_rx_t, Uart1_evt_fn);
+}
+
+
+void BSP_UART1_Init(void)
+{
+ GPIOPinRemap(ENABLE, RB_PIN_UART1);
+
+ /* 配置串口1:先配置IO口模式,再配置串口 */
+ GPIOB_SetBits(ML307_UART_RX_PIN);
+ GPIOB_ModeCfg(ML307_UART_RX_PIN, GPIO_ModeIN_PU); // RXD-配置上拉输入
+ GPIOB_ModeCfg(ML307_UART_TX_PIN, GPIO_ModeOut_PP_5mA); // TXD-配置推挽输出,注意先让IO口输出高电平
+ UART1_DefInit();
+ UART1_ByteTrigCfg(UART_1BYTE_TRIG);
+ // 中断方式接收数据
+ UART1_INTCfg(ENABLE, RB_IER_LINE_STAT | RB_IER_RECV_RDY | RB_IER_THR_EMPTY);
+ PFIC_EnableIRQ(UART1_IRQn);
+
+ UART1_FifoInit();
+}
+
+
+
+
+void BSP_UART3_Init(void)
+{
+ GPIOPinRemap(ENABLE, RB_PIN_UART3);
+ /* 配置串口3:先配置IO口模式,再配置串口 */
+ GPIOB_SetBits(UART3_TX_PIN);
+ GPIOB_ModeCfg(UART3_RX_PIN, GPIO_ModeIN_PU); // RXD-配置上拉输入
+ GPIOB_ModeCfg(UART3_TX_PIN, GPIO_ModeOut_PP_5mA); // TXD-配置推挽输出,注意先让IO口输出高电平
+ UART3_DefInit();
+ UART3_ByteTrigCfg(UART_1BYTE_TRIG);
+ // 中断方式接收数据
+ UART3_INTCfg(ENABLE, RB_IER_LINE_STAT | RB_IER_RECV_RDY | RB_IER_THR_EMPTY);
+ PFIC_EnableIRQ(UART3_IRQn);
+
+ UART3_FifoInit();
+ userShellInit();
+
+}
+
+/*********************************************************************
+ * @fn UART1_IRQHandler
+ *
+ * @brief UART1中断函数
+ *
+ * @return none
+ */
+__INTERRUPT
+__HIGH_CODE
+void UART1_IRQHandler(void)
+{
+ uint8_t data;
+ switch(UART1_GetITFlag())
+ {
+ case UART_II_LINE_STAT: // 线路状态错误
+ {
+// UART1_GetLinSTA();
+ break;
+ }
+ case UART_II_RECV_RDY:
+ data = UART1_RecvByte();
+ lwrb_write(&uart1_rx_t, &data, 1);
+ break;
+ case UART_II_RECV_TOUT: // 接收数据
+ break;
+
+ case UART_II_THR_EMPTY: // 发送缓存区空,可继续发送
+ if(lwrb_read(&uart1_tx_t, &data, 1))
+ {
+ UART1_SendByte(data);
+ }
+ else
+ {
+ UART1_INTCfg(DISABLE, RB_IER_THR_EMPTY);
+ }
+
+ break;
+
+ case UART_II_MODEM_CHG: // 只支持串口0
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*********************************************************************
+ * @fn UART3_IRQHandler
+ *
+ * @brief UART1中断函数
+ *
+ * @return none
+ */
+__INTERRUPT
+__HIGH_CODE
+void UART3_IRQHandler(void)
+{
+ uint8_t data;
+ switch(UART3_GetITFlag())
+ {
+ case UART_II_LINE_STAT: // 线路状态错误
+ {
+// UART1_GetLinSTA();
+ break;
+ }
+ case UART_II_RECV_RDY:
+ case UART_II_RECV_TOUT: //接收超时
+ while(R8_UART3_RFC)
+ {
+ shellHandler(&shell, R8_UART3_RBR);
+ }
+ break;
+ case UART_II_THR_EMPTY: // 发送缓存区空,可继续发送
+ if(lwrb_get_full(&uart3_tx_t))
+ {
+ lwrb_read(&uart3_tx_t, &data, 1);
+ UART3_SendByte(data);
+ }
+ else
+ {
+ UART3_INTCfg(DISABLE, RB_IER_THR_EMPTY);
+ }
+
+ break;
+
+ case UART_II_MODEM_CHG: // 只支持串口0
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/common/AT-Command-master/include/at_chat.h b/common/AT-Command-master/include/at_chat.h
new file mode 100644
index 0000000..367444f
--- /dev/null
+++ b/common/AT-Command-master/include/at_chat.h
@@ -0,0 +1,347 @@
+/******************************************************************************
+ * @brief AT command communication management V2
+ *
+ * Copyright (c) 2020~2022,
+ *
+ * SPDX-License-Identifier: Apathe-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-01-02 roger.luo Initial version
+ * 2021-01-20 roger.luo Add debugging interface, and at the same time
+ * solve the problem of uninitialized linked list
+ * leading to segment fault.
+ * 2021-03-03 roger.luo Fix the problem of frequent print receive timeout
+ * caused by not clearing the URC counter.
+ * 2022-10-01 roger.luo Redesign the entire AT framework and release V2.0.
+ * 2023-01-09 roger.luo Optimized the processing logic when multi-line
+ * command execution fails.
+ * 2023-01-10 roger.luo Resolve URC message containing ':' character
+ * causing parsing failure.
+ * 2023-02-23 roger.luo Added a temporary disable interface for URC to
+ * solve the problem of URC matching exception when
+ * receiving binary data from the modem.
+ * 2023-10-27 roger.luo Added the function of transparent data transmission.
+ ******************************************************************************/
+#ifndef _AT_CHAT_H_
+#define _AT_CHAT_H_
+
+#include "at_port.h"
+#include
+#include
+
+struct at_obj;
+struct at_adapter;
+struct at_response;
+
+/**
+ *@brief AT work running state.
+ */
+typedef enum {
+ AT_WORK_STAT_IDLE,
+ AT_WORK_STAT_READY,
+ AT_WORK_STAT_RUN,
+ AT_WORK_STAT_FINISH,
+ AT_WORK_STAT_ABORT,
+} at_work_state;
+
+/**
+ *@brief AT command response code
+ */
+typedef enum {
+ AT_RESP_OK = 0,
+ AT_RESP_ERROR,
+ AT_RESP_TIMEOUT,
+ AT_RESP_ABORT
+} at_resp_code;
+
+/**
+ *@brief AT command request priority
+ */
+typedef enum {
+ AT_PRIORITY_LOW = 0,
+ AT_PRIORITY_HIGH
+} at_cmd_priority;
+
+/**
+ *@brief URC frame receiving status
+ */
+typedef enum {
+ URC_RECV_OK = 0, /* URC frame received successfully. */
+ URC_RECV_TIMEOUT /* Receive timeout (The frame prefix is matched but the suffix is not matched within AT_URC_TIMEOUT) */
+} urc_recv_status;
+
+/**
+ * @brief URC frame info.
+ */
+typedef struct {
+ urc_recv_status status; /* URC frame receiving status.*/
+ char *urcbuf; /* URC frame buffer*/
+ int urclen; /* URC frame buffer length*/
+} at_urc_info_t;
+
+/**
+ * @brief URC subscription item.
+ */
+typedef struct {
+ const char *prefix; /* URC frame prefix,such as '+CSQ:'*/
+ const char endmark; /* URC frame end mark (can only be selected from AT_URC_END_MARKS)*/
+ /**
+ * @brief URC handler (triggered when matching prefix and mark are match)
+ * @params info - URC frame info.
+ * @return Indicates the remaining unreceived bytes of the current URC frame.
+ * @retval 0 Indicates that the current URC frame has been completely received
+ * @retval n It still needs to wait to receive n bytes (AT manager continues
+ * to receive the remaining data and continues to call back this interface).
+ */
+ int (*handler)(at_urc_info_t *info);
+} urc_item_t;
+
+/**
+ * @brief AT response information
+ */
+typedef struct {
+ struct at_obj *obj; /* AT object*/
+ void *params; /* User parameters (referenced from ->at_attr_t.params)*/
+ at_resp_code code; /* AT command response code.*/
+ unsigned short recvcnt; /* Receive data length*/
+ char *recvbuf; /* Receive buffer (raw data)*/
+ /* Pointer to the receiving content prefix, valid when code=AT_RESP_OK,
+ if no prefix is specified, it pointer to recvbuf
+ */
+ char *prefix;
+ /* Pointer to the receiving content suffix, valid when code=AT_RESP_OK,
+ if no suffix is specified, it pointer to recvbuf
+ */
+ char *suffix;
+} at_response_t;
+
+/**
+ * @brief The configuration for transparent transmission mode.
+ */
+typedef struct {
+ /**
+ * @brief Exit command(Example:AT+TRANS=0). When this command is matched through the
+ * read interface, the on_exit event is generated.
+ */
+ const char *exit_cmd;
+ /**
+ * @brief Exit event, triggered when the exit command is currently matched. At this time,
+ * you can invoke 'at_raw_transport_exit' to exit the transparent transport mode.
+ */
+ void (*on_exit)(void);
+ /**
+ * @brief Writting interface for transparent data transmission
+ * @param buf Data buffer
+ * @param len data length
+ * @return Indicates the length of the written data
+ */
+ unsigned int (*write)(const void *buf, unsigned int len);
+ /**
+ * @brief Reading interface for transparent data transmission
+ * @param buf Data buffer
+ * @param len data length
+ * @return The length of the data actually read
+ */
+ unsigned int (*read) (void *buf, unsigned int len);
+} at_raw_trans_conf_t;
+
+/**
+ * @brief AT interface adapter
+ */
+typedef struct {
+ //Lock, used in OS environment, fill in NULL if not required.
+ void (*lock)(void);
+ //Unlock, used in OS environment, fill in NULL if not required.
+ void (*unlock)(void);
+ /**
+ * @brief Data write operation (non-blocking)
+ * @param buf Data buffer
+ * @param len data length
+ * @return Indicates the length of the written data
+ */
+ unsigned int (*write)(const void *buf, unsigned int len);
+ /**
+ * @brief Data read operation (non-blocking)
+ * @param buf Data buffer
+ * @param len data length
+ * @return The length of the data actually read
+ */
+ unsigned int (*read)(void *buf, unsigned int len);
+ /**
+ * @brief AT error event ( if not required, fill in NULL)
+ */
+ void (*error)(at_response_t *);
+ /**
+ * @brief Log output interface, which can print the complete AT interaction
+ * process, fill in NULL if not required.
+ */
+ void (*debug)(const char *fmt, ...);
+#if AT_URC_WARCH_EN
+ //URC buffer size, set according to the actual maximum URC frame when used.
+ unsigned short urc_bufsize;
+#endif
+ //Command response receiving buffer size, set according to the actual maximum command response length
+ unsigned short recv_bufsize;
+} at_adapter_t;
+
+/**
+ * @brief Public environment for AT work
+ */
+typedef struct at_env {
+ struct at_obj *obj;
+ //Public variables (add as needed), these values are reset every time a new work starts.
+ int i, j, state;
+ //User parameters (referenced from ->at_attr_t.params)
+ void *params;
+ //Set the next polling wait interval (only takes effect once)
+ void (*next_wait)(struct at_env *self, unsigned int ms);
+ //Reset timer
+ void (*reset_timer)(struct at_env *self);
+ //Timeout indication
+ bool (*is_timeout)(struct at_env *self, unsigned int ms);
+ //Formatted printout with newlines
+ void (*println)(struct at_env *self, const char *fmt, ...);
+ //Find a keyword from the received content
+ char * (*contains)(struct at_env *self, const char *str);
+ //Get receives buffer
+ char * (*recvbuf)(struct at_env *self);
+ //Get receives buffer length
+ unsigned int(*recvlen)(struct at_env *self);
+ //Clear the receives buffer
+ void (*recvclr)(struct at_env *self);
+ //Indicates whether the current work has been abort
+ bool (*disposing)(struct at_env *self);
+ //End the work and set the response code
+ void (*finish)(struct at_env *self, at_resp_code code);
+} at_env_t;
+
+/**
+ *@brief AT execution callback
+ *@param r AT response information (including execution results, content information returned by the device)
+ */
+typedef void (*at_callback_t)(at_response_t *r);
+
+/**
+ *@brief AT work polling handler
+ *@param env The public operating environment for AT work, including some common
+ * variables and relevant interfaces needed to communicate AT commands.
+ *@return Work processing status, which determines whether to continue running the
+ * work on the next polling cycle.
+ *
+ * @retval true Indicates that the current work processing has been finished,
+ * and the work response code is set to AT_RESP_OK
+ * @retval false Indicates unfinished work processing, keep running.
+ *@note It must be noted that if the env->finish() operation is invoked in the
+ * current work, the work will be forcibly terminated regardless of the return value.
+ */
+typedef int (*at_work_t)(at_env_t *env);
+
+#if AT_WORK_CONTEXT_EN
+
+/**
+ *@brief AT work item context (used to monitor the entire life cycle of AT work item)
+ */
+typedef struct {
+ at_work_state work_state; /* Indicates the state at which the AT work item is running. */
+ at_resp_code code; /* Indicates the response code after the AT command has been run.*/
+ unsigned short bufsize; /* Indicates receive buffer size*/
+ unsigned short resplen; /* Indicates the actual response valid data length*/
+ unsigned char *respbuf; /* Point to the receive buffer*/
+} at_context_t;
+
+#endif
+
+/**
+ *@brief AT attributes
+ */
+typedef struct {
+#if AT_WORK_CONTEXT_EN
+ at_context_t *ctx; /* Pointer to the Work context. */
+#endif
+ void *params; /* User parameter, fill in NULL if not required. */
+ const char *prefix; /* Response prefix, fill in NULL if not required. */
+ const char *suffix; /* Response suffix, fill in NULL if not required. */
+ at_callback_t cb; /* Response callback handler, Fill in NULL if not required. */
+ unsigned short timeout; /* Response timeout(ms).. */
+ unsigned char retry; /* Response error retries. */
+ at_cmd_priority priority; /* Command execution priority. */
+} at_attr_t;
+
+/**
+ *@brief AT object.
+ */
+typedef struct at_obj {
+ const at_adapter_t *adap;
+#if AT_RAW_TRANSPARENT_EN
+ const at_raw_trans_conf_t *raw_conf;
+#endif
+ void *user_data;
+} at_obj_t;
+
+at_obj_t *at_obj_create(const at_adapter_t *);
+
+void at_obj_destroy(at_obj_t *at);
+
+bool at_obj_busy(at_obj_t *at);
+
+void at_obj_set_user_data(at_obj_t *at, void *user_data);
+
+void *at_obj_get_user_data(at_obj_t *at);
+#if AT_URC_WARCH_EN
+void at_obj_set_urc(at_obj_t *at, const urc_item_t *tbl, int count);
+
+int at_obj_get_urcbuf_count(at_obj_t *at);
+
+void at_obj_urc_set_enable(at_obj_t *at, int enable, unsigned short timeout);
+
+#endif
+
+void at_obj_process(at_obj_t *at);
+
+void at_attr_deinit(at_attr_t *attr);
+
+bool at_exec_cmd(at_obj_t *at, const at_attr_t *attr, const char *cmd, ...);
+
+bool at_exec_vcmd(at_obj_t *at, const at_attr_t *attr, const char *cmd, va_list va);
+
+bool at_send_singlline(at_obj_t *at, const at_attr_t *attr, const char *singlline);
+
+bool at_send_multiline(at_obj_t *at, const at_attr_t *attr, const char **multiline);
+
+bool at_send_data(at_obj_t *at, const at_attr_t *attr, const void *databuf, unsigned int bufsize);
+
+bool at_custom_cmd(at_obj_t *at, const at_attr_t *attr, void (*sender)(at_env_t *env));
+
+bool at_do_work(at_obj_t *at, void *params, at_work_t work);
+
+void at_work_abort_all(at_obj_t *at);
+
+#if AT_MEM_WATCH_EN
+unsigned int at_max_used_memory(void);
+
+unsigned int at_cur_used_memory(void);
+#endif
+
+#if AT_WORK_CONTEXT_EN
+
+void at_context_init(at_context_t *ctx, void *respbuf, unsigned bufsize);
+
+void at_context_attach(at_attr_t *attr, at_context_t *ctx);
+
+at_work_state at_work_get_state(at_context_t *ctx);
+
+bool at_work_is_finish(at_context_t *ctx);
+
+at_resp_code at_work_get_result(at_context_t *ctx);
+
+#endif //End of AT_WORK_CONTEXT_EN
+
+#if AT_RAW_TRANSPARENT_EN
+void at_raw_transport_enter(at_obj_t *obj, const at_raw_trans_conf_t *conf);
+
+void at_raw_transport_exit(at_obj_t *obj);
+#endif //End of AT_RAW_TRANSPARENT_EN
+
+#endif //End of _AT_CHAT_H_
+
diff --git a/common/AT-Command-master/include/at_port.h b/common/AT-Command-master/include/at_port.h
new file mode 100644
index 0000000..8f52ef0
--- /dev/null
+++ b/common/AT-Command-master/include/at_port.h
@@ -0,0 +1,80 @@
+/**
+ * @Brief: The AT component drives the interface implementation
+ * @Author: roger.luo
+ * @Date: 2021-04-04
+ * @Last Modified by: roger.luo
+ * @Last Modified time: 2021-11-27
+ */
+#ifndef __AT_PORT_H__
+#define __AT_PORT_H__
+
+/**
+ *@brief Default correct response identifier.
+ */
+#define AT_DEF_RESP_OK "OK"
+/**
+ *@brief Default error response identifier.
+ */
+#define AT_DEF_RESP_ERR "ERROR"
+
+/**
+ *@brief Default command timeout (ms)
+ */
+#define AT_DEF_TIMEOUT 500
+
+/**
+ *@brief Number of retries when a command timeout/error occurs.
+ */
+#define AT_DEF_RETRY 3
+
+/**
+ *@brief Default URC frame receive timeout (ms).
+ */
+#define AT_URC_TIMEOUT 500
+
+/**
+ *@brief Maximum AT command send data length (only for variable parameter commands).
+ */
+#define AT_MAX_CMD_LEN 256
+
+/**
+ *@brief Maximum number of work in queue (limit memory usage).
+ */
+#define AT_LIST_WORK_COUNT 32
+
+/**
+ *@brief Enable URC watcher.
+ */
+#define AT_URC_WARCH_EN 1
+
+/**
+ *@brief A list of specified URC end marks (fill in as needed, the fewer the better).
+ */
+#define AT_URC_END_MARKS ":,\n"
+/**
+ *@brief Enable memory watcher.
+ */
+#define AT_MEM_WATCH_EN 1u
+
+/**
+ *@brief Maximum memory usage limit (Valid when AT_MEM_WATCH_EN is enabled)
+ */
+#define AT_MEM_LIMIT_SIZE (8 * 1024)
+
+/**
+ *@brief Enable AT work context interfaces.
+ */
+#define AT_WORK_CONTEXT_EN 1u
+
+/**
+ * @brief Supports raw data transparent transmission
+ */
+#define AT_RAW_TRANSPARENT_EN 1u
+
+void *at_malloc(unsigned int nbytes);
+
+void at_free(void *ptr);
+
+unsigned int at_get_ms(void);
+
+#endif
diff --git a/common/AT-Command-master/include/linux_list.h b/common/AT-Command-master/include/linux_list.h
new file mode 100644
index 0000000..53dddc4
--- /dev/null
+++ b/common/AT-Command-master/include/linux_list.h
@@ -0,0 +1,705 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#include
+
+#if !defined(__GNUC__)
+ #define typeof (struct list_head)
+#endif
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+
+#if !defined(__GNUC__)
+#define container_of(ptr, type, member) ( \
+ (type *)( (char *)(ptr) - offsetof(type,member) ))
+#else
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+/* copy from , */
+/*
+ * used to verify that nobody uses non-initialized list entries.
+ */
+#define LIST_POISON1 ((void *) 0x0)
+#define LIST_POISON2 ((void *) 0x0)
+
+
+#ifndef ARCH_HAS_PREFETCH
+#define ARCH_HAS_PREFETCH
+static inline void prefetch(const void *x) {;}
+#endif
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+ struct list_head *new)
+{
+ new->next = old->next;
+ new->next->prev = new;
+ new->prev = old->prev;
+ new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+ struct list_head *new)
+{
+ list_replace(old, new);
+ INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+ const struct list_head *head)
+{
+ return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+ struct list_head *next = head->next;
+ return (next == head) && (next == head->prev);
+}
+
+/**
+ * list_is_singular - tests whether a list has just one entry.
+ * @head: the list to test.
+ */
+static inline int list_is_singular(const struct list_head *head)
+{
+ return !list_empty(head) && (head->next == head->prev);
+}
+
+static inline void __list_cut_position(struct list_head *list,
+ struct list_head *head, struct list_head *entry)
+{
+ struct list_head *new_first = entry->next;
+ list->next = head->next;
+ list->next->prev = list;
+ list->prev = entry;
+ entry->next = list;
+ head->next = new_first;
+ new_first->prev = head;
+}
+
+/**
+ * list_cut_position - cut a list into two
+ * @list: a new list to add all removed entries
+ * @head: a list with entries
+ * @entry: an entry within head, could be the head itself
+ * and if so we won't cut the list
+ *
+ * This helper moves the initial part of @head, up to and
+ * including @entry, from @head to @list. You should
+ * pass on @entry an element you know is on @head. @list
+ * should be an empty list or a list you do not care about
+ * losing its data.
+ *
+ */
+static inline void list_cut_position(struct list_head *list,
+ struct list_head *head, struct list_head *entry)
+{
+ if (list_empty(head))
+ return;
+ if (list_is_singular(head) &&
+ (head->next != entry && head != entry))
+ return;
+ if (entry == head)
+ INIT_LIST_HEAD(list);
+ else
+ __list_cut_position(list, head, entry);
+}
+
+static inline void __list_splice(const struct list_head *list,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+
+ first->prev = prev;
+ prev->next = first;
+
+ last->next = next;
+ next->prev = last;
+}
+
+/**
+ * list_splice - join two lists, this is designed for stacks
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(const struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head, head->next);
+}
+
+/**
+ * list_splice_tail - join two lists, each list being a queue
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice_tail(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head->prev, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head, head->next);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_splice_tail_init - join two lists and reinitialise the emptied list
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * Each of the lists is a queue.
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_tail_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head->prev, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; prefetch(pos->next), pos != (head); \
+ pos = pos->next)
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
+ pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+ for (pos = (head)->prev, n = pos->prev; \
+ prefetch(pos->prev), pos != (head); \
+ pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ prefetch(pos->member.next), &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member); \
+ prefetch(pos->member.prev), &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos: the type * to use as a start point
+ * @head: the head of the list
+ * @member: the name of the list_struct within the struct.
+ *
+ * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+ ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member); \
+ prefetch(pos->member.next), &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member) \
+ for (pos = list_entry(pos->member.prev, typeof(*pos), member); \
+ prefetch(pos->member.prev), &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member) \
+ for (; prefetch(pos->member.next), &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member) \
+ for (n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member), \
+ n = list_entry(pos->member.prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+static inline void INIT_HLIST_NODE(struct hlist_node *h)
+{
+ h->next = NULL;
+ h->pprev = NULL;
+}
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+ return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+ return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+ *pprev = next;
+ if (next)
+ next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->next = LIST_POISON1;
+ n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+ if (!hlist_unhashed(n)) {
+ __hlist_del(n);
+ INIT_HLIST_NODE(n);
+ }
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+ n->pprev = &h->first;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ n->pprev = next->pprev;
+ n->next = next;
+ next->pprev = &n->next;
+ *(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ next->next = n->next;
+ n->next = next;
+ next->pprev = &n->next;
+
+ if(next->next)
+ next->next->pprev = &next->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+ pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+ pos = n)
+
+/**
+ * hlist_for_each_entry - iterate over list of given type
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member) \
+ for (pos = (pos)->next; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from current point
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member) \
+ for (; pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @n: another &struct hlist_node to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ n = pos->next; 1; }) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = n)
+
+#endif
diff --git a/common/AT-Command-master/src/at_chat.c b/common/AT-Command-master/src/at_chat.c
new file mode 100644
index 0000000..32970d0
--- /dev/null
+++ b/common/AT-Command-master/src/at_chat.c
@@ -0,0 +1,1324 @@
+/******************************************************************************
+ * @brief AT command communication management V2
+ *
+ * Copyright (c) 2020~2022,
+ *
+ * SPDX-License-Identifier: Apathe-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-01-02 roger.luo Initial version
+ * 2021-01-20 roger.luo Add debugging interface, and at the same time
+ * solve the problem of uninitialized linked list
+ * leading to segment fault.
+ * 2021-03-03 roger.luo Fix the problem of frequent print receive timeout
+ * caused by not clearing the URC counter.
+ * 2022-10-01 roger.luo Redesign the entire AT framework and release V2.0.
+ * 2023-01-09 roger.luo Optimized the processing logic when multi-line
+ * command execution fails.
+ * 2023-01-10 roger.luo Resolve URC message containing ':' character
+ * causing parsing failure.
+ * 2023-02-23 roger.luo Added a temporary disable interface for URC to
+ * solve the problem of URC matching exception when
+ * receiving binary data from the modem.
+ * 2023-10-27 roger.luo Added the function of transparent data transmission.
+ ******************************************************************************/
+#include "at_chat.h"
+#include "at_port.h"
+#include "linux_list.h"
+#include
+#include
+#include
+#include
+
+#define AT_DEBUG(ai, fmt, args...) \
+ do \
+ { \
+ if (__get_adapter(ai)->debug) \
+ __get_adapter(ai)->debug(fmt, ##args); \
+ } while (0)
+
+#if AT_LIST_WORK_COUNT < 2
+ #error "AT_LIST_WORK_COUNT cannot be less than 2"
+#endif
+
+//Used to identify the validity of a work item.
+#define WORK_ITEM_TAG 0x2532
+
+#define AT_IS_TIMEOUT(start, time) (at_get_ms() - (start) > (time))
+
+/**AT work type (corresponding to different state machine polling handler.) */
+typedef enum {
+ WORK_TYPE_GENERAL = 0, /* General work */
+ WORK_TYPE_SINGLLINE, /* Singlline command */
+ WORK_TYPE_MULTILINE, /* Multiline command */
+ WORK_TYPE_CMD, /* Standard command */
+ WORK_TYPE_CUSTOM, /* Custom command */
+ WORK_TYPE_BUF, /* Buffer */
+ WORK_TYPE_MAX
+} work_type;
+
+/**
+ * @brief AT command execution state.
+ */
+typedef enum {
+ AT_STAT_SEND = 0,
+ AT_STAT_RECV,
+ AT_STAT_RETRY,
+} at_cmd_state;
+
+/**
+ * @brief AT receive match mask
+ */
+#define MATCH_MASK_PREFIX 0x01
+#define MATCH_MASK_SUFFIX 0x02
+#define MATCH_MASK_ERROR 0x04
+
+/**
+ * @brief AT work item object
+ */
+typedef struct {
+ struct list_head node; /* list node */
+ at_attr_t attr; /* AT attributes */
+ unsigned int magic : 16; /* AT magic*/
+ unsigned int state : 3; /* State of work */
+ unsigned int type : 3; /* Type of work */
+ unsigned int code : 3; /* Response code*/
+ unsigned int life : 6; /* Life cycle countdown(s)*/
+ unsigned int dirty : 1; /* Dirty flag*/
+ union {
+ const void *info;
+ at_work_t work; /* Custom work */
+ const char * singlline;
+ const char **multiline;
+ void (*sender)(at_env_t *env); /* Custom sender */
+ struct {
+ unsigned int bufsize;
+ char buf[0];
+ };
+ };
+} work_item_t;
+
+/**
+ * @brief AT Object infomation.
+ */
+typedef struct {
+ at_obj_t obj; /* Inherit at_obj*/
+ at_env_t env; /* Public work environment*/
+ work_item_t *cursor; /* Currently running work*/
+ struct list_head hlist, llist; /* High and low priority queue*/
+ struct list_head *clist; /* Queue currently in use*/
+ unsigned int timer; /* General purpose timer*/
+ unsigned int next_delay; /* Next cycle delay time*/
+ unsigned int delay_timer; /* Delay timer*/
+ char *recvbuf; /* Command response receive buffer*/
+ char *prefix; /* Point to prefix match*/
+ char *suffix; /* Point to suffix match*/
+#if AT_URC_WARCH_EN
+ const urc_item_t *urc_tbl;
+ const urc_item_t *urc_item; /* The currently matched URC item*/
+ char *urcbuf;
+ unsigned int urc_timer;
+ unsigned short urc_bufsize;
+ unsigned short urc_cnt;
+ unsigned short urc_target; /* The target data length of the current URC frame*/
+ unsigned short urc_tbl_size;
+ unsigned short urc_disable_time;
+#endif
+ unsigned short list_cnt;
+ unsigned short recv_bufsize;
+ unsigned short recv_cnt; /* Command response receives counter*/
+ unsigned short match_len; /* Response information matching length*/
+ unsigned char match_mask; /* Response information matching mask*/
+ unsigned urc_enable: 1;
+ unsigned urc_match : 1;
+ unsigned enable : 1; /* Enable the work */
+ unsigned disposing : 1;
+ unsigned err_occur : 1;
+ unsigned raw_trans : 1;
+} at_info_t;
+
+/**
+ * @brief Default command attributes.
+ */
+static const at_attr_t at_def_attr = {
+ .params = NULL,
+ .prefix = NULL,
+ .suffix = AT_DEF_RESP_OK,
+ .cb = NULL,
+ .timeout= AT_DEF_TIMEOUT,
+ .retry = AT_DEF_RETRY,
+ .priority = AT_PRIORITY_LOW
+};
+/*Private static function declarations------------------------------------*/
+static void at_send_line(at_info_t *ai, const char *fmt, va_list args);
+static void *at_core_malloc(unsigned int nbytes);
+static void at_core_free(void *ptr);
+
+#if AT_MEM_WATCH_EN
+static unsigned int at_max_mem; /* Maximum memory used*/
+static unsigned int at_cur_mem; /* Currently used memory*/
+#endif
+
+/**
+ * @brief at_obj_t * -> at_info_t *
+ */
+static inline at_info_t *obj_map(at_obj_t *at)
+{
+ return (at_info_t *)at;
+}
+
+
+static inline const at_adapter_t *__get_adapter(at_info_t *ai)
+{
+ return ai->obj.adap;
+}
+
+static inline void at_lock(at_info_t *ai)
+{
+ if (__get_adapter(ai)->lock != NULL)
+ __get_adapter(ai)->lock();
+}
+
+static inline void at_unlock(at_info_t *ai)
+{
+ if (__get_adapter(ai)->unlock != NULL)
+ __get_adapter(ai)->unlock();
+}
+
+static inline void send_data(at_info_t *at, const void *buf, unsigned int len)
+{
+ __get_adapter(at)->write(buf, len);
+}
+
+/**
+ * @brief Send command with newline.
+ */
+static void send_cmdline(at_info_t *at, const char *cmd)
+{
+ int len;
+ if (cmd == NULL)
+ return;
+ len = strlen(cmd);
+ __get_adapter(at)->write(cmd, len);
+ __get_adapter(at)->write("\r\n", 2);
+ AT_DEBUG(at,"->\r\n%s", cmd);
+}
+
+/**
+ * @brief Formatted print with newline.
+ */
+static void println(at_env_t *env, const char *cmd, ...)
+{
+ va_list args;
+ va_start(args, cmd);
+ at_send_line(obj_map(env->obj), cmd, args);
+ va_end(args);
+}
+
+static unsigned int get_recv_count(at_env_t *env)
+{
+ return obj_map(env->obj)->recv_cnt;
+}
+
+static char *get_recvbuf(at_env_t *env)
+{
+ return obj_map(env->obj)->recvbuf;
+}
+
+static void recvbuf_clear(at_env_t *env)
+{
+ obj_map(env->obj)->recvbuf[0] = '\0';
+ obj_map(env->obj)->recv_cnt = 0;
+}
+
+static char *find_substr(at_env_t *env, const char *str)
+{
+ return strstr(obj_map(env->obj)->recvbuf, str);
+}
+
+/**
+ * @brief Indicates whether the current work was abort.
+ */
+static bool at_isabort(at_env_t *env)
+{
+ at_info_t *ai = obj_map(env->obj);
+ if (ai->cursor == NULL)
+ return true;
+ return ai->cursor->state == AT_WORK_STAT_ABORT;
+}
+
+/**
+ * @brief Indication timeout
+ */
+static bool at_is_timeout(at_env_t *env, unsigned int ms)
+{
+ return AT_IS_TIMEOUT(obj_map(env->obj)->timer, ms);
+}
+
+
+/**
+ * @brief Reset the currently working timer.
+ */
+static void at_reset_timer(at_env_t *env)
+{
+ obj_map(env->obj)->timer = at_get_ms();
+}
+/**
+ * @brief Set the next poll wait time for the current work.
+ */
+static void at_next_wait(struct at_env *env, unsigned int ms)
+{
+ obj_map(env->obj)->next_delay = ms;
+ AT_DEBUG(obj_map(env->obj), "Next wait:%d\r\n", ms);
+}
+
+static void update_work_state(work_item_t *wi, at_work_state state, at_resp_code code)
+{
+ wi->state = state;
+ wi->code = code;
+#if AT_WORK_CONTEXT_EN
+ at_context_t *ctx = wi->attr.ctx;
+ if (ctx != NULL) {
+ ctx->code = (at_resp_code)wi->code;
+ ctx->work_state = (at_work_state)wi->state;
+ }
+#endif
+}
+
+/**
+ * @brief End the currently running work.
+ */
+static void at_finish(struct at_env *env, at_resp_code code)
+{
+ work_item_t *it = obj_map(env->obj)->cursor;
+ update_work_state(it, AT_WORK_STAT_FINISH, code);
+}
+
+/**
+ * @brief AT execution callback handler.
+ */
+static void do_at_callback(at_info_t *ai, work_item_t *wi, at_resp_code code)
+{
+ at_response_t r;
+ AT_DEBUG(ai, "<-\r\n%s", ai->recvbuf);
+ //Exception notification
+ if ((code == AT_RESP_ERROR || code == AT_RESP_TIMEOUT) && __get_adapter(ai)->error != NULL) {
+ __get_adapter(ai)->error(&r);
+ ai->err_occur = 1;
+ AT_DEBUG(ai, "AT Respose :%s", code == AT_RESP_TIMEOUT ? "timeout" : "error");
+ } else {
+ ai->err_occur = 0;
+ }
+#if AT_WORK_CONTEXT_EN
+ at_context_t *ctx = wi->attr.ctx;
+ if (ctx != NULL ) {
+ if (ctx->respbuf != NULL/* && ctx->bufsize */) {
+ ctx->resplen = ai->recv_cnt >= ctx->bufsize ? ctx->bufsize - 1 : ai->recv_cnt;
+ memcpy(ctx->respbuf, ai->recvbuf, ctx->resplen);
+ }
+ }
+#endif
+ update_work_state(wi, AT_WORK_STAT_FINISH, code);
+ //Submit response data and status.
+ if (wi->attr.cb) {
+ r.obj = &ai->obj;
+ r.params = wi->attr.params;
+ r.recvbuf = ai->recvbuf;
+ r.recvcnt = ai->recv_cnt;
+ r.code = code;
+ r.prefix = ai->prefix != NULL ? ai->prefix : ai->recvbuf;
+ r.suffix = ai->suffix != NULL ? ai->suffix : ai->recvbuf;
+ wi->attr.cb(&r);
+ }
+}
+
+/**
+ * @brief Create a basic work item.
+ */
+static work_item_t *work_item_create(int extend_size)
+{
+ work_item_t *it;
+ it = at_core_malloc(sizeof(work_item_t) + extend_size);
+ if (it != NULL)
+ memset(it, 0, sizeof(work_item_t) + extend_size);
+ return it;
+}
+
+/**
+ * @brief Destroy work item.
+ * @param it Pointer to an item to destroy.
+ */
+static void work_item_destroy(work_item_t *it)
+{
+ if (it != NULL) {
+ it->magic = 0;
+ at_core_free(it);
+ }
+}
+
+/**
+ * @brief Destroys all work items in the specified queue.
+ */
+static void work_item_destroy_all(at_info_t *ai, struct list_head *head)
+{
+ struct list_head *pos, *n;
+ work_item_t *it;
+ at_lock(ai);
+ list_for_each_safe(pos, n, head) {
+ it = list_entry(pos, work_item_t, node);
+ list_del(&it->node);
+ work_item_destroy(it);
+ }
+ at_unlock(ai);
+}
+
+/**
+ * @brief Work item recycling.
+ */
+static void work_item_recycle(at_info_t *ai, work_item_t *it)
+{
+ at_lock(ai);
+ if (ai->list_cnt)
+ ai->list_cnt--;
+
+ list_del(&it->node);
+ work_item_destroy(it);
+ at_unlock(ai);
+}
+/**
+ * @brief Create and initialize a work item.
+ * @param type Type of work item.
+ * @param attr Item attributes
+ * @param info additional information.
+ * @param size the extended size.
+ */
+static work_item_t *create_work_item(at_info_t *ai, int type, const at_attr_t *attr, const void *info, int extend_size)
+{
+ work_item_t *it = work_item_create(extend_size);
+ if (it == NULL) {
+ AT_DEBUG(ai, "Insufficient memory, list count:%d\r\n", ai->list_cnt);
+ return NULL;
+ }
+ if (ai->list_cnt > AT_LIST_WORK_COUNT) {
+ AT_DEBUG(ai, "Work queue full\r\n");
+ work_item_destroy(it);
+ return NULL;
+ }
+ if (attr == NULL)
+ attr = &at_def_attr;
+
+ if (type == WORK_TYPE_CMD || type == WORK_TYPE_BUF) {
+ memcpy(it->buf, info, extend_size);
+ it->bufsize = extend_size;
+ } else {
+ it->info = info;
+ }
+ it->magic = WORK_ITEM_TAG;
+ it->attr = *attr;
+ it->type = type;
+ it->state = AT_WORK_STAT_READY;
+#if AT_WORK_CONTEXT_EN
+ if (attr->ctx) {
+ attr->ctx->code = AT_RESP_OK;
+ attr->ctx->work_state = AT_WORK_STAT_READY;
+ }
+#endif
+ return it;
+}
+
+static work_item_t *sumit_work_item(at_info_t *ai, work_item_t *it)
+{
+ if (it != NULL) {
+ at_lock(ai);
+ list_add_tail(&it->node, it->attr.priority == AT_PRIORITY_HIGH ? &ai->hlist : &ai->llist);
+ ai->list_cnt++; //Statistics
+ at_unlock(ai);
+ }
+ return it;
+}
+
+/**
+ * @brief Create work and put it in the queue
+ * @param type work of type
+ * @param attr AT attributes
+ * @param info Extra info
+ * @param size Extra info size
+ */
+static work_item_t *add_work_item(at_info_t *ai, int type, const at_attr_t *attr, const void *info, int extend_size)
+{
+ work_item_t *it = create_work_item(ai, type, attr, info, extend_size);
+ if (it == NULL)
+ return NULL;
+ return sumit_work_item(ai, it);
+}
+
+/**
+ * @brief Initialize matching info
+ */
+static void match_info_init(at_info_t *ai, at_attr_t *attr)
+{
+ ai->prefix = ai->suffix = NULL;
+ ai->match_len = 0;
+ ai->match_mask = 0;
+ if (attr->prefix == NULL || strlen(attr->prefix) == 0)
+ ai->match_mask |= MATCH_MASK_PREFIX;
+ if (attr->suffix == NULL || strlen(attr->suffix) == 0)
+ ai->match_mask |= MATCH_MASK_SUFFIX;
+}
+
+/**
+ * @brief Custom work processing
+ */
+static int do_work_handler(at_info_t *ai)
+{
+ work_item_t *i = ai->cursor;
+ if (ai->next_delay > 0) {
+ if (!AT_IS_TIMEOUT(ai->delay_timer, ai->next_delay))
+ return 0;
+ ai->next_delay = 0;
+ }
+ return ((int (*)(at_env_t * e)) i->work)(&ai->env);
+}
+
+/**
+ * @brief Generic commands processing
+ */
+static int do_cmd_handler(at_info_t *ai)
+{
+ work_item_t *wi = ai->cursor;
+ at_env_t *env = &ai->env;
+ at_attr_t *attr = &wi->attr;
+ switch (env->state)
+ {
+ case AT_STAT_SEND:
+ if (wi->type == WORK_TYPE_CUSTOM && wi->sender != NULL) {
+ wi->sender(env);
+ } else if (wi->type == WORK_TYPE_BUF) {
+ __get_adapter(ai)->write(wi->buf, wi->bufsize);
+ } else if (wi->type == WORK_TYPE_SINGLLINE) {
+ send_cmdline(ai, wi->singlline);
+ } else {
+ send_cmdline(ai, wi->buf);
+ }
+ env->state = AT_STAT_RECV;
+ env->reset_timer(env);
+ env->recvclr(env);
+ match_info_init(ai, attr);
+ break;
+ case AT_STAT_RECV: /*Receive information and matching processing.*/
+ if (ai->match_len != ai->recv_cnt) {
+ ai->match_len = ai->recv_cnt;
+ //Matching response content prefix.
+ if ( !(ai->match_mask & MATCH_MASK_PREFIX) ) {
+ ai->prefix = strstr(ai->recvbuf, attr->prefix);
+ ai->match_mask |= ai->prefix ? MATCH_MASK_PREFIX : 0x00;
+ }
+ //Matching response content suffix.
+ if ( ai->match_mask & MATCH_MASK_PREFIX ) {
+ ai->suffix = strstr(ai->prefix ? ai->prefix : ai->recvbuf, attr->suffix);
+ ai->match_mask |= ai->suffix ? MATCH_MASK_SUFFIX : 0x00;
+ }
+ ai->match_mask |= strstr(ai->recvbuf, AT_DEF_RESP_ERR) ? MATCH_MASK_ERROR : 0x00;
+ }
+ if (ai->match_mask & MATCH_MASK_ERROR) {
+ AT_DEBUG(ai, "<-\r\n%s\r\n", ai->recvbuf);
+ if (env->i++ >= attr->retry) {
+ do_at_callback(ai, wi, AT_RESP_ERROR);
+ return true;
+ }
+
+ env->state = AT_STAT_RETRY; //If the command responds incorrectly, it will wait for a while and try again.
+ env->reset_timer(env);
+ }
+ if (ai->match_mask & MATCH_MASK_SUFFIX) {
+ do_at_callback(ai, wi, AT_RESP_OK);
+ return true;
+ } else if (env->is_timeout(env, attr->timeout)) {
+ AT_DEBUG(ai, "Command response timeout, retry:%d\r\n", env->i);
+ if (env->i++ >= attr->retry) {
+ do_at_callback(ai, wi, AT_RESP_TIMEOUT);
+ return true;
+ }
+ env->state = AT_STAT_SEND;
+ }
+ break;
+ case AT_STAT_RETRY:
+ if (env->is_timeout(env, 100))
+ env->state = AT_STAT_SEND; /*Go back to the send state*/
+ break;
+ default:
+ env->state = AT_STAT_SEND;
+ }
+ return false;
+}
+
+/**
+ * @brief Multi-line command sending processing.
+ */
+static int send_multiline_handler(at_info_t *ai)
+{
+ work_item_t *wi = ai->cursor;
+ at_env_t *env = &ai->env;
+ at_attr_t *attr = &wi->attr;
+ const char **cmds = wi->multiline;
+
+ switch (env->state)
+ {
+ case AT_STAT_SEND:
+ if (cmds[env->i] == NULL) { /* All commands are sent.*/
+ do_at_callback(ai, wi, env->params ? AT_RESP_OK : AT_RESP_ERROR);
+ return true;
+ }
+ send_cmdline(ai, cmds[env->i]);
+ env->recvclr(env);
+ env->reset_timer(env);
+ env->state = AT_STAT_RECV;
+ match_info_init(ai, attr);
+ break;
+ case AT_STAT_RECV:
+ if (find_substr(env, attr->suffix)) {
+ env->state = 0;
+ env->i++;
+ env->j = 0;
+ env->params = (void *)true; /*Mark execution status*/
+ AT_DEBUG(ai, "<-\r\n%s", ai->recvbuf);
+ } else if (find_substr(env, AT_DEF_RESP_ERR)) {
+ AT_DEBUG(ai, "<-\r\n%s", ai->recvbuf);
+ env->j++;
+ AT_DEBUG(ai, "CMD:'%s' failed to executed, retry:%d", cmds[env->i], env->j);
+ if (env->j >= attr->retry) {
+ env->state = 0;
+ env->j = 0;
+ env->i++;
+ } else {
+ env->state = AT_STAT_RETRY; //After the command responds incorrect, try again after a period of time.
+ env->reset_timer(env);
+ }
+ } else if (env->is_timeout(env, AT_DEF_TIMEOUT)) {
+ do_at_callback(ai, wi, AT_RESP_TIMEOUT);
+ return true;
+ }
+ break;
+ case AT_STAT_RETRY:
+ if (env->is_timeout(env, 100))
+ env->state = AT_STAT_SEND;/*Go back to the send state and resend.*/
+ break;
+ default:
+ env->state = AT_STAT_SEND;
+ }
+ return 0;
+}
+
+/**
+ * @brief Send command line
+ */
+static void at_send_line(at_info_t *ai, const char *fmt, va_list args)
+{
+ int len;
+ char *cmdline;
+ cmdline = at_core_malloc(AT_MAX_CMD_LEN);
+ if (cmdline == NULL) {
+ AT_DEBUG(ai, "Malloc failed when send...\r\n");
+ return;
+ }
+ len = vsnprintf(cmdline, AT_MAX_CMD_LEN, fmt, args);
+ //Clear receive buffer.
+ ai->recv_cnt = 0;
+ ai->recvbuf[0] = '\0';
+ send_data(ai, cmdline, len);
+ send_data(ai, "\r\n", 2);
+ AT_DEBUG(ai,"->\r\n%s\r\n", cmdline);
+
+ at_core_free(cmdline);
+}
+
+#if AT_URC_WARCH_EN
+
+/**
+ * @brief Set the AT urc table.
+ */
+void at_obj_set_urc(at_obj_t *at, const urc_item_t *tbl, int count)
+{
+ at_info_t *ai = obj_map(at);
+ ai->urc_tbl = tbl;
+ ai->urc_tbl_size = count;
+}
+/**
+ * @brief Get the urc recv buffer count.
+ */
+int at_obj_get_urcbuf_count(at_obj_t *at)
+{
+ return obj_map(at)->urc_cnt;
+}
+/**
+ * @brief Temporarily disables the URC matching program
+ * @param enable Enable/disable the URC matching handler
+ * @param timeout Disabled timeout(ms).
+ */
+void at_obj_urc_set_enable(at_obj_t *at, int enable, unsigned short timeout)
+{
+ at_info_t *ai = obj_map(at);
+ ai->urc_enable = enable ? 1 : 0;
+ if (!enable) {
+ ai->urc_timer = at_get_ms();
+ ai->urc_disable_time = timeout;
+ //AT_DEBUG(ai, "Disable the URC, time:%d ms\r\n", timeout);
+ } else {
+ //AT_DEBUG(ai, "Enable the URC\r\n");
+ }
+}
+
+/**
+ * @brief Find a URC handler based on URC receive buffer information.
+ */
+const urc_item_t *find_urc_item(at_info_t *ai, char *urc_buf, unsigned int size)
+{
+ const urc_item_t *tbl = ai->urc_tbl;
+ int i;
+ for (i = 0; i < ai->urc_tbl_size && tbl; i++, tbl++) {
+ if (strstr(urc_buf, tbl->prefix))//It will need to be further optimized in the future.
+ return tbl;
+ }
+ return NULL;
+}
+
+static void urc_reset(at_info_t *ai)
+{
+ ai->urc_target = 0;
+ ai->urc_cnt = 0;
+ ai->urc_item = NULL;
+ ai->urc_match = 0;
+ ai->urc_timer = at_get_ms();
+}
+
+/**
+ * @brief URC(unsolicited code) handler entry.
+ * @param[in] urc - URC receive buffer
+ * @param[in] size - URC receive buffer length
+ * @return none
+ */
+static void urc_handler_entry(at_info_t *ai, urc_recv_status status, char *urc, unsigned int size)
+{
+ int remain;
+ at_urc_info_t ctx = {status, urc, size};
+ if (ai->urc_target > 0)
+ AT_DEBUG(ai, "<=\r\n%.5s..\r\n", urc);
+ else
+ AT_DEBUG(ai, "<=\r\n%s\r\n", urc);
+ /* Send URC event notification. */
+ remain = ai->urc_item ? ai->urc_item->handler(&ctx) : 0;
+ if (remain == 0 && (ai->urc_item || ai->cursor == NULL)) {
+
+ urc_reset(ai);
+ } else {
+ AT_DEBUG(ai,"URC receives %d bytes remaining.\r\n", remain);
+ ai->urc_target = ai->urc_cnt + remain;
+ ai->urc_match = true;
+ }
+}
+
+static void urc_timeout_process(at_info_t *ai)
+{
+ //Receive timeout processing, default (MAX_URC_RECV_TIMEOUT).
+ if (ai->urc_cnt > 0 && AT_IS_TIMEOUT(ai->urc_timer, AT_URC_TIMEOUT)) {
+ if (ai->urc_cnt > 2 && ai->urc_item != NULL) {
+ ai->urcbuf[ai->urc_cnt] = '\0';
+ AT_DEBUG(ai,"urc recv timeout=>%s\r\n", ai->urcbuf);
+ urc_handler_entry(ai, URC_RECV_TIMEOUT, ai->urcbuf, ai->urc_cnt);
+ }
+ urc_reset(ai);
+ }
+}
+
+/**
+ * @brief URC receive processing
+ * @param[in] buf - Receive buffer
+ * @return none
+ */
+static void urc_recv_process(at_info_t *ai, char *buf, unsigned int size)
+{
+ char *urc_buf;
+ int ch;
+ if (ai->urcbuf == NULL)
+ return;
+ if (size == 0) {
+ urc_timeout_process(ai);
+ return;
+ }
+ if (!ai->urc_enable) {
+ if (!AT_IS_TIMEOUT(ai->urc_timer, ai->urc_disable_time))
+ return;
+ ai->urc_enable = 1;
+ AT_DEBUG(ai, "Enable the URC match handler\r\n");
+ }
+ ai->urc_timer = at_get_ms();
+ urc_buf = ai->urcbuf;
+ while (size--) {
+ ch = *buf++;
+ urc_buf[ai->urc_cnt++] = ch;
+ if (ai->urc_cnt >= ai->urc_bufsize) { /* Empty directly on overflow */
+ urc_reset(ai);
+ AT_DEBUG(ai, "Urc buffer full.\r\n");
+ continue;
+ }
+ if (ai->urc_match) {
+ if (ai->urc_cnt >= ai->urc_target){
+ urc_handler_entry(ai, URC_RECV_OK, urc_buf, ai->urc_cnt);
+ }
+ continue;
+ }
+ if (strchr(AT_URC_END_MARKS, ch) == NULL && ch != '\0') // Find the URC end mark.
+ continue;
+ urc_buf[ai->urc_cnt] = '\0';
+ if (ai->urc_item == NULL) { //Find the corresponding URC handler
+ ai->urc_item = find_urc_item(ai, urc_buf, ai->urc_cnt);
+ if (ai->urc_item == NULL && ch == '\n') {
+ if (ai->urc_cnt > 2 && ai->cursor == NULL) //Unrecognized URC message
+ AT_DEBUG(ai, "%s\r\n", urc_buf);
+ urc_reset(ai);
+ continue;
+ }
+ }
+ if (ai->urc_item != NULL && ch == ai->urc_item->endmark)
+ urc_handler_entry(ai, URC_RECV_OK, urc_buf, ai->urc_cnt);
+ }
+
+}
+#endif
+/**
+ * @brief Command response data processing
+ * @return none
+ */
+static void resp_recv_process(at_info_t *ai, const char *buf, unsigned int size)
+{
+ if (size == 0)
+ return;
+ if (ai->recv_cnt + size >= ai->recv_bufsize) //Receive overflow, clear directly.
+ ai->recv_cnt = 0;
+
+ memcpy(ai->recvbuf + ai->recv_cnt, buf, size);
+ ai->recv_cnt += size;
+ ai->recvbuf[ai->recv_cnt] = '\0';
+}
+
+static int (*const work_handler_table[WORK_TYPE_MAX])(at_info_t *) = {
+ [WORK_TYPE_GENERAL] = do_work_handler,
+ [WORK_TYPE_SINGLLINE] = do_cmd_handler,
+ [WORK_TYPE_MULTILINE] = send_multiline_handler,
+ [WORK_TYPE_CMD] = do_cmd_handler,
+ [WORK_TYPE_CUSTOM] = do_cmd_handler,
+ [WORK_TYPE_BUF] = do_cmd_handler,
+};
+
+/**
+ * @brief AT work processing.
+ */
+static void at_work_process(at_info_t *ai)
+{
+ at_env_t *env = &ai->env;
+ if (ai->cursor == NULL) {
+ if (!list_empty(&ai->hlist))
+ ai->clist = &ai->hlist;
+ else if (!list_empty(&ai->llist))
+ ai->clist = &ai->llist;
+ else
+ return; //No work to do.
+
+ at_lock(ai);
+ ai->next_delay = 0;
+ env->obj = (struct at_obj *)ai;
+ env->i = 0;
+ env->j = 0;
+ env->state = 0;
+ ai->cursor = list_first_entry(ai->clist, work_item_t, node);
+ env->params = ai->cursor->attr.params;
+ env->recvclr(env);
+ env->reset_timer(env);
+ /*Enter running state*/
+ if (ai->cursor->state == AT_WORK_STAT_READY) {
+ update_work_state(ai->cursor, AT_WORK_STAT_RUN, (at_resp_code)ai->cursor->code);
+ }
+ at_unlock(ai);
+ }
+ /* When the job execution is complete, put it into the idle work queue */
+ if (ai->cursor->state >= AT_WORK_STAT_FINISH || work_handler_table[ai->cursor->type](ai)) {
+ //Marked the work as done.
+ if (ai->cursor->state == AT_WORK_STAT_RUN) {
+ update_work_state(ai->cursor, AT_WORK_STAT_FINISH, (at_resp_code)ai->cursor->code);
+ }
+ //Recycle Processed work item.
+ work_item_recycle(ai, ai->cursor);
+ ai->cursor = NULL;
+ }
+}
+
+/**
+ * @brief Create an AT object
+ * @param adap AT interface adapter (AT object only saves its pointer, it must be a global resident object)
+ * @return Pointer to a new AT object
+ */
+at_obj_t *at_obj_create(const at_adapter_t *adap)
+{
+ at_env_t *e;
+ at_info_t *ai = at_core_malloc(sizeof(at_info_t));
+ if (ai == NULL)
+ return NULL;
+ memset(ai, 0, sizeof(at_info_t));
+ ai->obj.adap = adap;
+ /* Initialize high and low priority queues*/
+ INIT_LIST_HEAD(&ai->hlist);
+ INIT_LIST_HEAD(&ai->llist);
+ //Allocate at least 32 bytes to the buffer
+ ai->recv_bufsize = adap->recv_bufsize < 32 ? 32 : adap->recv_bufsize;
+ ai->recvbuf = at_core_malloc(ai->recv_bufsize);
+ if (ai->recvbuf == NULL) {
+ at_obj_destroy(&ai->obj);
+ return NULL;
+ }
+#if AT_URC_WARCH_EN
+ if (adap->urc_bufsize != 0) {
+ ai->urc_bufsize = adap->urc_bufsize < 32 ? 32 : adap->urc_bufsize;
+ ai->urcbuf = at_core_malloc(ai->urc_bufsize);
+ if (ai->urcbuf == NULL) {
+ at_obj_destroy(&ai->obj);
+ return NULL;
+ }
+ }
+#endif
+ e = &ai->env;
+ ai->recv_cnt = 0;
+ ai->urc_enable = 1;
+ ai->enable = 1;
+ //Initialization of public work environment.
+ e->is_timeout = at_is_timeout;
+ e->println = println;
+ e->recvbuf = get_recvbuf;
+ e->recvclr = recvbuf_clear;
+ e->recvlen = get_recv_count;
+ e->contains = find_substr;
+ e->disposing = at_isabort;
+ e->finish = at_finish;
+ e->reset_timer = at_reset_timer;
+ e->next_wait = at_next_wait;
+ return &ai->obj;
+}
+/**
+ * @brief Destroy a AT object.
+ */
+void at_obj_destroy(at_obj_t *obj)
+{
+ at_info_t *ai = obj_map(obj);
+
+ if (obj == NULL)
+ return;
+
+ work_item_destroy_all(ai, &ai->hlist);
+ work_item_destroy_all(ai, &ai->llist);
+
+ if (ai->recvbuf != NULL)
+ at_core_free(ai->recvbuf);
+#if AT_URC_WARCH_EN
+ if (ai->urcbuf != NULL)
+ at_core_free(ai->urcbuf);
+#endif
+ at_core_free(ai);
+
+}
+
+/**
+ * @brief Indicates if the AT object is busy.
+ * @return true - command queue is not empty, busy state
+ */
+bool at_obj_busy(at_obj_t *at)
+{
+ return !list_empty(&obj_map(at)->hlist) || !list_empty(&obj_map(at)->llist)
+ || obj_map(at)->urc_cnt != 0;
+}
+
+/**
+ * @brief Enable/Disable the AT work
+ */
+void at_obj_set_enable(at_obj_t *at, int enable)
+{
+ obj_map(at)->enable = enable ? 1 : 0;
+}
+
+/**
+ * @brief Set user data
+ */
+void at_obj_set_user_data(at_obj_t *at, void *user_data)
+{
+ at->user_data = user_data;
+}
+
+/**
+ * @brief Get user data.
+ */
+void *at_obj_get_user_data(at_obj_t *at)
+{
+ return at->user_data;
+}
+
+/**
+ * @brief Default attributes initialization.
+ * Default low priority, other reference AT_DEF_XXX definition.
+ * @param attr AT attributes
+ */
+void at_attr_deinit(at_attr_t *attr)
+{
+ *attr = at_def_attr;
+}
+
+/**
+ * @brief Execute command (with variable argument list)
+ * @param attr AT attributes(NULL to use the default value)
+ * @param cmd Format the command.
+ * @param va Variable parameter list
+ * @return Indicates whether the asynchronous work was enqueued successfully
+ */
+bool at_exec_vcmd(at_obj_t *at, const at_attr_t *attr, const char *cmd, va_list va)
+{
+ char *buf;
+ int len;
+ void *workid = NULL;
+ buf = at_core_malloc(AT_MAX_CMD_LEN);
+ if (buf == NULL) {
+ AT_DEBUG(obj_map(at), "No memory when execute vcmd...\r\n");
+ return NULL;
+ }
+ len = vsnprintf(buf, AT_MAX_CMD_LEN, cmd, va);
+ if (len > 0) {
+ workid = add_work_item(obj_map(at), WORK_TYPE_CMD, attr, buf, len + 1);
+ }
+ at_core_free(buf);
+ return workid != NULL;
+}
+
+/**
+ * @brief Execute one command
+ * @param attr AT attributes(NULL to use the default value)
+ * @param cmd Formatted arguments
+ * @param ... Variable argument list (same usage as printf)
+ * @retval Indicates whether the asynchronous work was enqueued successfully
+ */
+bool at_exec_cmd(at_obj_t *at, const at_attr_t *attr, const char *cmd, ...)
+{
+ bool ret;
+ va_list args;
+ va_start(args, cmd);
+ ret = at_exec_vcmd(at, attr, cmd, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * @brief Execute custom command
+ * @param attr AT attributes(NULL to use the default value)
+ * @param sender Command sending handler (such as sending any type of data through the env->obj->adap-write interface)
+ * @retval Indicates whether the asynchronous work was enqueued successfully
+ */
+bool at_custom_cmd(at_obj_t *at, const at_attr_t *attr, void (*sender)(at_env_t *env))
+{
+ return add_work_item(obj_map(at), WORK_TYPE_CUSTOM, attr, (const void *)sender, 0) != NULL;
+}
+/**
+ * @brief Send (binary) data
+ * @param attr AT attributes(NULL to use the default value)
+ * @param databuf Binary data
+ * @param bufsize Binary data length
+ * @retval Indicates whether the asynchronous work was enqueued successfully
+ */
+bool at_send_data(at_obj_t *at, const at_attr_t *attr, const void *databuf, unsigned int bufsize)
+{
+ return add_work_item(obj_map(at), WORK_TYPE_BUF, attr, databuf, bufsize) != NULL;
+}
+/**
+ * @brief Send a single-line command
+ * @param cb Response callback handler, Fill in NULL if not required
+ * @param timeout command execution timeout(ms)
+ * @param retry command retries( >= 0)
+ * @param singlline command
+ * @retval Indicates whether the asynchronous work was enqueued successfully.
+ * @note Only the address is saved, so the 'singlline' can not be a local variable which will be destroyed.
+ */
+bool at_send_singlline(at_obj_t *at, const at_attr_t *attr, const char *singlline)
+{
+ return add_work_item(obj_map(at), WORK_TYPE_SINGLLINE, attr, singlline, 0) != NULL;
+}
+
+/**
+ * @brief Send multiline commands
+ * @param attr AT attributes(NULL to use the default value)
+ * @param multiline Command table, with the last item ending in NULL.
+ * @example :
+ * const char *multiline = {
+ * "AT+XXX",
+ * "AT+XXX",
+ * NULL
+ * };
+ * at_send_multiline(at_dev, NULL, multiline);
+ *
+ * @retval Indicates whether the asynchronous work was enqueued successfully.
+ * @note Only the address is saved, so the array can not be a local variable which will be destroyed.
+ */
+bool at_send_multiline(at_obj_t *at, const at_attr_t *attr, const char **multiline)
+{
+ return add_work_item(obj_map(at), WORK_TYPE_MULTILINE, attr, multiline, 0) != NULL;
+}
+
+/**
+ * @brief Execute custom work.
+ * @param params User parameter.
+ * @param work AT custom polling work entry, specific usage ref@at_work_t
+ * @retval Indicates whether the asynchronous work was enqueued successfully.
+ */
+bool at_do_work(at_obj_t *at, void *params, at_work_t work)
+{
+ at_attr_t attr;
+ at_attr_deinit(&attr);
+ attr.params = params;
+ return add_work_item(obj_map(at), WORK_TYPE_GENERAL, &attr, (const void *)work, 0) != NULL;
+}
+
+/**
+ * @brief Abort all AT work
+ */
+void at_work_abort_all(at_obj_t *at)
+{
+ struct list_head *pos;
+ work_item_t *it;
+ at_info_t *ai = obj_map(at);
+ at_lock(ai);
+ list_for_each(pos, &ai->hlist) {
+ it = list_entry(pos, work_item_t, node);
+ update_work_state(it, AT_WORK_STAT_ABORT, AT_RESP_ABORT);
+ }
+ list_for_each(pos, &ai->llist) {
+ it = list_entry(pos, work_item_t, node);
+ update_work_state(it, AT_WORK_STAT_ABORT, AT_RESP_ABORT);
+ }
+ at_unlock(ai);
+}
+
+#if AT_MEM_WATCH_EN
+
+static void *at_core_malloc(unsigned int nbytes)
+{
+ if (nbytes + at_cur_mem > AT_MEM_LIMIT_SIZE) { //The maximum memory limit has been exceeded.
+ return NULL;
+ }
+ unsigned long *mem_info = (unsigned long *)at_malloc(nbytes + sizeof(unsigned long));
+ *mem_info = nbytes;
+ at_cur_mem += nbytes; //Statistics of current memory usage.
+ if (at_cur_mem > at_max_mem) //Record maximum memory usage.
+ at_max_mem = at_cur_mem;
+ return mem_info + 1;
+}
+
+static void at_core_free(void *ptr)
+{
+ unsigned long *mem_info = (unsigned long *)ptr;
+ unsigned long nbyte;
+ if (ptr != NULL) {
+ mem_info--;
+ nbyte = *mem_info;
+ at_cur_mem -= nbyte;
+ at_free(mem_info);
+ }
+}
+
+/**
+ * @brief Get the maximum memory usage.
+ */
+unsigned int at_max_used_memory(void)
+{
+ return at_max_mem;
+}
+
+/**
+ * @brief Get current memory usage.
+ */
+unsigned int at_cur_used_memory(void)
+{
+ return at_cur_mem;
+}
+
+#else
+
+static void *at_core_malloc(unsigned int nbytes)
+{
+ return at_malloc(nbytes);
+}
+
+static void at_core_free(void *ptr)
+{
+ at_free(ptr);
+}
+
+#endif
+
+#if AT_WORK_CONTEXT_EN
+
+/**
+ * @brief void * -> work_item_t *
+ */
+static inline work_item_t *work_item_map(void *work_obj)
+{
+ return (work_item_t *)work_obj;
+}
+
+/**
+ * @brief Indicates whether the AT work is valid.
+ */
+bool at_work_isvalid(void *work_item)
+{
+ work_item_t *it = work_item_map(work_item);
+ return it != NULL && it->magic == WORK_ITEM_TAG;
+}
+
+/**
+ * @brief Initialize a work context
+ * @param ctx - Pointer to 'at_context_t'
+ * @param respbuf Command response buffer, fill in NULL if not required
+ * @param bufsize Buffer size (must be long enough to receive all responses)
+ */
+void at_context_init(at_context_t *ctx, void *respbuf, unsigned bufsize)
+{
+ memset(ctx, 0, sizeof(at_context_t));
+ ctx->bufsize = bufsize;
+ ctx->respbuf = respbuf;
+}
+
+/**
+ * @brief Attached to the AT attribute.
+ * @param attr AT attributes
+ * @param ctx AT context
+ */
+void at_context_attach(at_attr_t *attr, at_context_t *ctx)
+{
+ attr->ctx = ctx;
+}
+
+/**
+ * @brief Get work running state
+ * @param ctx AT context
+ * @return State of work.
+ */
+at_work_state at_work_get_state(at_context_t *ctx)
+{
+ return ctx->work_state;
+}
+
+bool at_work_is_busy(at_context_t *ctx)
+{
+ return ctx->work_state == AT_WORK_STAT_RUN || ctx->work_state == AT_WORK_STAT_READY;
+}
+/**
+ * @brief Indicate whether the work has been finished (then you can call `at_work_get_result` to query the result)
+ * @param ctx AT context
+ * @return true - the work finish
+ */
+bool at_work_is_finish(at_context_t *ctx)
+{
+ return ctx->work_state > AT_WORK_STAT_RUN;
+}
+
+/**
+ * @brief Get work running result
+ * @param ctx AT context
+ */
+at_resp_code at_work_get_result(at_context_t *ctx)
+{
+ return ctx->code;
+}
+
+#endif
+
+#if AT_RAW_TRANSPARENT_EN
+/**
+ * @brief Data transparent transmission processing.
+ */
+static void at_raw_trans_process(at_obj_t *obj)
+{
+ unsigned char rbuf[32];
+ int size;
+ int i;
+ at_info_t *ai = obj_map(obj);
+ if (obj->raw_conf == NULL)
+ return;
+ size = obj->adap->read(rbuf, sizeof(rbuf));
+ if (size > 0 ){
+ obj->raw_conf->write(rbuf, size);
+ }
+ size = obj->raw_conf->read(rbuf, sizeof(rbuf));
+ if (size > 0) {
+ obj->adap->write(rbuf, size);
+ }
+ //Exit command detection
+ if (obj->raw_conf->exit_cmd != NULL) {
+ for (i = 0; i < size; i++) {
+ if (ai->recv_cnt >= ai->recv_bufsize)
+ ai->recv_cnt = 0;
+ ai->recvbuf[ai->recv_cnt] = rbuf[i];
+ if (rbuf[i] == '\r' || rbuf[i] == 'n') {
+ ai->recvbuf[ai->recv_cnt] = '\0';
+ ai->recv_cnt = 0;
+ if (strcasecmp(obj->raw_conf->exit_cmd, ai->recvbuf) != 0) {
+ continue;
+ }
+ if (obj->raw_conf->on_exit) {
+ obj->raw_conf->on_exit();
+ }
+ } else {
+ ai->recv_cnt++;
+ }
+ }
+ }
+}
+
+/**
+ * @brief Enter transparent transmission mode.
+ * @param conf The configuration for transparent transmission mode.
+ */
+void at_raw_transport_enter(at_obj_t *obj, const at_raw_trans_conf_t *conf)
+{
+ at_info_t *ai = obj_map(obj);
+ obj->raw_conf = conf;
+ ai->raw_trans = 1;
+ ai->recv_cnt = 0;
+}
+
+/**
+ * @brief Exit transparent transmission mode.
+ */
+void at_raw_transport_exit(at_obj_t *obj)
+{
+ at_info_t *ai = obj_map(obj);
+ ai->raw_trans = 0;
+}
+
+#endif
+
+/**
+ * @brief AT work polling processing.
+ */
+void at_obj_process(at_obj_t *at)
+{
+ char rbuf[64];
+ int read_size;
+ register at_info_t *ai = obj_map(at);
+#if AT_RAW_TRANSPARENT_EN
+ if (ai->raw_trans) {
+ at_raw_trans_process(at);
+ return;
+ }
+#endif
+ read_size = __get_adapter(ai)->read(rbuf, sizeof(rbuf));
+#if AT_URC_WARCH_EN
+ urc_recv_process(ai, rbuf, read_size);
+#endif
+ resp_recv_process(ai, rbuf, read_size);
+ at_work_process(ai);
+}
+
diff --git a/common/AT-Command-master/src/at_port.c b/common/AT-Command-master/src/at_port.c
new file mode 100644
index 0000000..83abb7f
--- /dev/null
+++ b/common/AT-Command-master/src/at_port.c
@@ -0,0 +1,35 @@
+/**
+ * @Brief: The AT component drives the interface implementation
+ * @Author: roger.luo
+ * @Date: 2021-04-04
+ * @Last Modified by: roger.luo
+ * @Last Modified time: 2021-11-27
+ */
+#include
+#include
+#include "bsp_tim.h"
+
+
+/**
+ * @brief Custom malloc for AT component.
+ */
+void *at_malloc(unsigned int nbytes)
+{
+ return malloc(nbytes);
+}
+
+/**
+ * @brief Custom free for AT component.
+ */
+void at_free(void *ptr)
+{
+ free(ptr);
+}
+
+/**
+ * @brief Gets the total number of milliseconds in the system.
+ */
+unsigned int at_get_ms(void)
+{
+ return BSP_Get_Tick();
+}
diff --git a/common/LwUtil/README.md b/common/LwUtil/README.md
new file mode 100644
index 0000000..49646f9
--- /dev/null
+++ b/common/LwUtil/README.md
@@ -0,0 +1,26 @@
+# C language utility library
+
+Set of different functions and macros usually used in the various applications.
+
+## Features
+
+- Support for C99 or later
+- Support for minimal and maximal values
+- Support for absolute values
+- Support for unused macros
+- Support for storing and loading data from array in little- or big- endian formats
+- Support for retrieving number of elements for statically allocated array
+- Support for bitwise operations to set, clear, check or toggle bits
+- Compilation-time assert feature
+- User friendly MIT license
+
+## How to use
+
+Usage is very simply. Add `lwutil.c` file to compilation flag and make sure compiler has access to `lwutil.h` for include paths.
+
+## Contribute
+
+This is a generic library and welcomes different contributions.
+We accept issue reports or, even better, pull-requests with bugs or new features.
+
+Use [c-code-style](https://github.com/MaJerle/c-code-style) rules for coding and keep the same coding consistency.
\ No newline at end of file
diff --git a/common/LwUtil/lwutil.c b/common/LwUtil/lwutil.c
new file mode 100644
index 0000000..dbc5dac
--- /dev/null
+++ b/common/LwUtil/lwutil.c
@@ -0,0 +1,168 @@
+/*
+ * @Author : stark1898y 1658608470@qq.com
+ * @Date : 2024-06-14 13:25:23
+ * @LastEditors : stark1898y 1658608470@qq.com
+ * @LastEditTime : 2024-06-14 13:25:48
+ * @FilePath : \wuxi_alarm_ch32v303rct6_rtt\common\LwUtil\lwutil.c
+ * @Description :
+ *
+ * Copyright (c) 2024 by yzy, All Rights Reserved.
+ */
+/**
+ * \file lwutil.c
+ * \brief Lightweight utility library
+ */
+
+/*
+ * Copyright (c) 2024 Tilen MAJERLE
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * This file is part of LwUTIL - Lightweight utility library.
+ *
+ * Author: Tilen MAJERLE
+ * Version: v1.3.0
+ */
+#include
+#include
+#include
+#include
+#include "lwutil.h"
+
+/**
+ * \brief Makes ascii char array from `unsigned 8-bit` value
+ * \param[in] hex: Hexadecimal data to be converted
+ * \param[out] ascii: Minimum `3-bytes` long array to write value to
+ */
+void
+lwutil_u8_to_2asciis(uint8_t hex, char* ascii) {
+ for (uint8_t idx = 2U; idx != 0; --idx) {
+ uint8_t aux = (uint8_t)(hex >> (4U * (idx - 1U))) & 0x0FU;
+
+ aux = (aux <= 9U) ? (aux + 0x30U) : (aux + 0x57U);
+ ascii[2U - idx] = (char)aux;
+ }
+ ascii[2U] = '\0';
+}
+
+/**
+ * \brief Makes ascii char array from `unsigned 16-bit` value
+ * \param[in] hex: Hexadecimal data to be converted
+ * \param[out] ascii: Minimum `5-bytes` long array to write value to
+ */
+void
+lwutil_u16_to_4asciis(uint16_t hex, char* ascii) {
+ for (uint8_t idx = 4U; idx != 0; --idx) {
+ uint8_t aux = (uint8_t)(hex >> (4U * (idx - 1U))) & 0x0FU;
+
+ aux = (aux <= 9U) ? (aux + 0x30U) : (aux + 0x57U);
+ ascii[4U - idx] = (char)aux;
+ }
+ ascii[4] = '\0';
+}
+
+/**
+ * \brief Makes ascii char array from `unsigned 32-bit` value
+ * \param[in] hex: Hexadecimal data to be converted
+ * \param[out] ascii: Minimum `9-bytes` long array to write value to
+ */
+void
+lwutil_u32_to_8asciis(uint32_t hex, char* ascii) {
+ for (uint8_t idx = 8U; idx != 0; --idx) {
+ uint8_t aux = (uint8_t)(hex >> (4U * (idx - 1U))) & 0x0FU;
+
+ aux = (aux <= 9U) ? (aux + 0x30U) : (aux + 0x57U);
+ ascii[8U - idx] = (char)aux;
+ }
+ ascii[8] = '\0';
+}
+
+/**
+ * \brief Load variable length integer from the byte stream to the variable.
+ *
+ * Variable length integers (short varints) are stored with continuation bit (bit 7, 0x80),
+ * which tells us if there is a next byte to be used as part of the number sequence.
+ *
+ * 32-bit integer can be stored with anything between `1` and `5` bytes.
+ *
+ * \param ptr: Array pointer to load data from
+ * \param ptr_len: Input array length
+ * \param val_out: Pointer to variable to write result value
+ * \return Number of bytes written (stored). `0` in case of an error.
+ */
+uint8_t
+lwutil_ld_u32_varint(const void* ptr, size_t ptr_len, uint32_t* val_out) {
+ size_t cnt = 0;
+ uint32_t val = 0;
+ const uint8_t* p_data = ptr;
+ uint8_t byt;
+
+ if (ptr == NULL || ptr_len == 0 || val_out == NULL) {
+ return 0;
+ }
+ do {
+ byt = *p_data++;
+ val |= ((uint32_t)(byt & 0x7FU)) << (cnt * 7U);
+ ++cnt;
+ } while (--ptr_len > 0 && (byt & 0x80U) > 0);
+
+ /* Check memory length */
+ if ((byt & 0x80U) > 0) {
+ val = 0;
+ cnt = 0;
+ }
+ *val_out = val;
+ return cnt;
+}
+
+/**
+ * \brief Store an integer into variable length byte sequence array.
+ *
+ * Variable length integers (short varints) are stored with continuation bit (bit 7, 0x80),
+ * which tells us if there is a next byte to be used as part of the number sequence.
+ *
+ * 32-bit integer can be stored with anything between `1` and `5` bytes.
+ *
+ * \param val: Value to encode into byte sequence
+ * \param ptr: Array to write output result
+ * \param ptr_len: Length of an input array
+ * \return Number of bytes written (stored). `0` in case of an error.
+ */
+uint8_t
+lwutil_st_u32_varint(uint32_t val, void* ptr, size_t ptr_len) {
+ uint8_t* p_data = ptr;
+ size_t cnt = 0;
+
+ if (ptr == NULL || ptr_len == 0) {
+ return 0;
+ }
+ do {
+ *p_data++ = (val & 0x7FU) | (val > 0x7FU ? 0x80U : 0x00U);
+ val >>= 7U;
+ ++cnt;
+ } while (--ptr_len > 0 && val > 0);
+
+ /* Memory check */
+ if (val > 0) {
+ cnt = 0;
+ }
+ return cnt;
+}
diff --git a/common/LwUtil/lwutil.h b/common/LwUtil/lwutil.h
new file mode 100644
index 0000000..2083214
--- /dev/null
+++ b/common/LwUtil/lwutil.h
@@ -0,0 +1,362 @@
+/*
+ * @Author : stark1898y 1658608470@qq.com
+ * @Date : 2024-06-14 13:25:23
+ * @LastEditors : stark1898y 1658608470@qq.com
+ * @LastEditTime : 2024-06-14 13:25:40
+ * @FilePath : \wuxi_alarm_ch32v303rct6_rtt\common\LwUtil\lwutil.h
+ * @Description :
+ *
+ * Copyright (c) 2024 by yzy, All Rights Reserved.
+ */
+/**
+ * \file lwutil.h
+ * \brief Lightweight utility library
+ */
+
+/*
+ * Copyright (c) 2024 Tilen MAJERLE
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * This file is part of LwUTIL - Lightweight utility library.
+ *
+ * Author: Tilen MAJERLE
+ * Version: v1.3.0
+ */
+#ifndef LWUTIL_HDR_H
+#define LWUTIL_HDR_H
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * \defgroup LWUTIL Lightweight utility
+ * \brief Lightweight utility
+ * \{
+ */
+
+/**
+ * \brief Get size of statically allocated array
+ * Array must be declared in a form of `type var_name[element_count]`
+ *
+ * \param[in] x: Object to get array size of
+ * \return Number of elements in array (`element_count`)
+ */
+#define LWUTIL_ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+/**
+ * \brief Get size of statically allocated array
+ * Array must be declared in a form of `type var_name[element_count]`
+ *
+ * \note This is an alias of \ref LWUTIL_ARRAYSIZE
+ *
+ * \param[in] x: Object to get array size of
+ * \return Number of elements in array (`element_count`)
+ */
+#define LWUTIL_ASZ(x) LWUTIL_ARRAYSIZE(x)
+
+/**
+ * \brief Get larger value out of 2 different inputs
+ * \param[in] x: First input
+ * \param[in] y: Second input
+ * \return Larger of both inputs
+ */
+#define LWUTIL_MAX(x, y) ((x) > (y) ? (x) : (y))
+
+/**
+ * \brief Get smaller value out of 2 different inputs
+ * \param[in] x: First input
+ * \param[in] y: Second input
+ * \return Smaller of both inputs
+ */
+#define LWUTIL_MIN(x, y) ((x) < (y) ? (x) : (y))
+
+/**
+ * \brief Constrains an input number within a range
+ * \param[in] x: Number to constrain
+ * \param[in] a: Minimum allowed number
+ * \param[in] b: Maximum allowed number
+ *
+ * \return `x` if `a < x < b`
+ * \return `a` if `x <= a`
+ * \return `b` if `x >= b`
+ *
+ * \note Function does not check if `a < b`. This must be handled by the user.
+ * \note This is implemented as macro and return data type depends on the input number types.
+ */
+#define LWUTIL_CONSTRAIN(x, a, b) LWUTIL_MIN(LWUTIL_MAX((x), (a)), (b))
+
+/**
+ * \brief Maps the input number with the min and max range to the map of the output min and max range
+ *
+ * Mathematical calculation is:
+ *
+ * (x - in_min) * (out_max - out_min)
+ * y = ------------------------------------ + out_min
+ * (in_max - in_min)
+ *
+ * \note Data types depend on the user inputs. If high precision is required,
+ * user can cast the input variables to appropriate type (double or float),
+ * or use integer types if decimal precision is not required.
+ *
+ * \note Input data is not constrained between out min and out max values.
+ * This may sometimes be useful for the application.
+ * Use \ref LWUTIL_CONSTRAIN macro to constrain the value
+ *
+ * \param[in] x: Input value to map
+ * \param[in] in_min: Minimum value to map from (input boundary)
+ * \param[in] in_max: Maximum value to map from (input boundary)
+ * \param[in] out_min: Minimum value to map to (output boundary)
+ * \param[in] out_max: Maximum value to map to (output boundary)
+ * \return Mapped value
+ */
+#define LWUTIL_MAP(x, in_min, in_max, out_min, out_max) \
+ (((x) - (in_min)) * ((out_max) - (out_min)) / ((in_max) - (in_min)) + (out_min))
+
+/**
+ * \brief Get absolute value of the input
+ * Returns always-positive value of the input.
+ *
+ * \note Special care must be taken when input variable holds
+ * minimum value available for given signed integer type (char, int, long, ...).
+ *
+ * Making absolute value of such input means longer output data type requirement.
+ *
+ * Consider writing result of this function to unsigned type of same integer width.
+ * For example, minimum `signed char` value is `-128` while its absolute value (`128`)
+ * requires variable type of either `unsigned char` or minimum signed 16-bit (or more).
+ *
+ * \param[in] x: Input value
+ * \return Absolute value of the input value
+ */
+#define LWUTIL_ABS(x) ((x) < 0 ? -(x) : (x))
+
+/**
+ * \brief Unused variable to avoid compilation warning if declared but not used
+ * \param[in] x: Input variable to declare unused
+ */
+#define LWUTIL_UNUSED(x) (void)(x)
+
+/**
+ * \brief Dereference the pointer and assign the value to it,
+ * but only if ptr is not NULL.
+ *
+ * \note It is fully up to user to handle to correct variable and data types
+ *
+ * \param[in] ptr: Pointer to check and assign to
+ * \param[in] value: Value to assign
+ */
+#define LWUTIL_SET_VALUE_IF_PTR_NOT_NULL(ptr, value) \
+ do { \
+ if ((ptr) != NULL) { \
+ *(ptr) = (value); \
+ } \
+ } while (0)
+
+#define LWUTIL_CONCAT_BASE(x, y) x##y
+#define LWUTIL_CONCAT(s0, s1) LWUTIL_CONCAT_BASE(s0, s1)
+
+/**
+ * \brief Compile time assert to validate specific expression.
+ * Compilation will fail if expression evaluated to zero
+ * \note Can only be used with the integer types
+ *
+ * \param[in] exp: Expression to test. It must be compile-time evaluation
+ * \param[in] random_variable_name: Meaningful variable name to be used.
+ * Can be whatever until it is valid variable name
+ */
+#define LWUTIL_COMPILE_TIME_ASSERT(exp, random_variable_name) \
+ typedef char LWUTIL_CONCAT2(random_variable_name, __LINE__)[!(exp) ? -1 : 1];
+
+/**
+ * \brief Check if all bits in the `bit_mask` are set in the input value
+ * \note Can only be used with the integer types
+ *
+ * \param[in] val: Value to check for bits in
+ * \param[in] bit_mask: Bit mask to check in value
+ * \return `1` if all bits are set, `0` otherwise
+ */
+#define lwutil_bits_is_set_all(val, bit_mask) (((val) & (bit_mask)) == (bit_mask))
+
+/**
+ * \brief Check if any of the `bit_mask` bits is set in the input value
+ * \note Can only be used with the integer types
+ *
+ * \param[in] val: Value to check for bits in
+ * \param[in] bit_mask: Bit mask to check in value
+ * \return `1` if any bit is set, `0` otherwise
+ */
+#define lwutil_bits_is_set_any(val, bit_mask) (((val) & (bit_mask)) != 0)
+
+/**
+ * \brief Set bit mask in the input value
+ * \note Can only be used with the integer types
+ *
+ * \param[in] val: Value to set bits in.
+ * Original input is not modified. It is pass-by-value.
+ * \param[in] bit_mask: Bit mask indicating which bits to set
+ * \return New value with bitwise OR between input value and bit mask
+ */
+#define lwutil_bits_set(val, bit_mask) ((val) | (bit_mask))
+
+/**
+ * \brief Clear bit mask in the input value
+ * \note Can only be used with the integer types
+ *
+ * \param[in] val: Value to clear bits in.
+ * Original input is not modified. It is pass-by-value.
+ * \param[in] bit_mask: Bit mask indicating which bits to clear
+ * \return New value with bitwise AND and negated bit_mask between input value and bit mask
+ * Value has bits cleared in the bit_mask set
+ */
+#define lwutil_bits_clear(val, bit_mask) ((val) & ~(bit_mask))
+
+/**
+ * \brief Toggle bit mask in the input value
+ * \note Can only be used with the integer types
+ *
+ * \param[in] val: Value to toggle bits in.
+ * Original input is not modified. It is pass-by-value.
+ * \param[in] bit_mask: Bit mask indicating which bits to toggle
+ * \return New value with bitwise AND and negated bit_mask between input value and bit mask
+ * Value has bits cleared in the bit_mask set
+ */
+#define lwutil_bits_toggle(val, bit_mask) ((val) ^ (bit_mask))
+
+/**
+ * \brief Store `16-bit` value to bytes array in little-endian format
+ * \param[in] val: Value to write to output array
+ * \param[out] ptr: Minimum `2-bytes` long output array to write value to
+ */
+static inline void
+lwutil_st_u16_le(uint16_t val, void* ptr) {
+ uint8_t* p = (uint8_t*)ptr;
+
+ p[0] = (uint8_t)((val >> 0) & 0xFF);
+ p[1] = (uint8_t)((val >> 8) & 0xFF);
+}
+
+/**
+ * \brief Store `32-bit` value to bytes array in little-endian format
+ * \param[in] val: Value to write to output array
+ * \param[out] ptr: Minimum `4-bytes` long output array to write value to
+ */
+static inline void
+lwutil_st_u32_le(uint32_t val, void* ptr) {
+ uint8_t* p = (uint8_t*)ptr;
+
+ p[0] = (uint8_t)((val >> 0) & 0xFF);
+ p[1] = (uint8_t)((val >> 8) & 0xFF);
+ p[2] = (uint8_t)((val >> 16) & 0xFF);
+ p[3] = (uint8_t)((val >> 24) & 0xFF);
+}
+
+/**
+ * \brief Load `16-bit` value from bytes array in little-endian format
+ * \param[in] ptr: Minimum `2-bytes` long input array to extract bytes from
+ * \return `16-bit` value extracted from input array
+ */
+static inline uint16_t
+lwutil_ld_u16_le(const void* ptr) {
+ const uint8_t* p = (const uint8_t*)ptr;
+ return p[1] << 8 | p[0];
+}
+
+/**
+ * \brief Load `32-bit` value from bytes array in little-endian format
+ * \param[in] ptr: Minimum `2-bytes` long input array to extract bytes from
+ * \return `32-bit` value extracted from input array
+ */
+static inline uint32_t
+lwutil_ld_u32_le(const void* ptr) {
+ const uint8_t* p = (const uint8_t*)ptr;
+ return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
+}
+
+/**
+ * \brief Store `16-bit` value to bytes array in big-endian format
+ * \param[in] val: Value to write to output array
+ * \param[out] ptr: Minimum `2-bytes` long output array to write value to
+ */
+static inline void
+lwutil_st_u16_be(uint16_t val, void* ptr) {
+ uint8_t* p = (uint8_t*)ptr;
+
+ p[0] = (uint8_t)((val >> 8) & 0xFF);
+ p[1] = (uint8_t)((val >> 0) & 0xFF);
+}
+
+/**
+ * \brief Store `32-bit` value to bytes array in big-endian format
+ * \param[in] val: Value to write to output array
+ * \param[out] ptr: Minimum `4-bytes` long output array to write value to
+ */
+static inline void
+lwutil_st_u32_be(uint32_t val, void* ptr) {
+ uint8_t* p = (uint8_t*)ptr;
+
+ p[0] = (uint8_t)((val >> 24) & 0xFF);
+ p[1] = (uint8_t)((val >> 16) & 0xFF);
+ p[2] = (uint8_t)((val >> 8) & 0xFF);
+ p[3] = (uint8_t)((val >> 0) & 0xFF);
+}
+
+/**
+ * \brief Load `16-bit` value from bytes array in big-endian format
+ * \param[in] ptr: Minimum `2-bytes` long input array to extract bytes from
+ * \return `16-bit` value extracted from input array
+ */
+static inline uint16_t
+lwutil_ld_u16_be(const void* ptr) {
+ const uint8_t* p = (const uint8_t*)ptr;
+ return p[0] << 8 | p[1];
+}
+
+/**
+ * \brief Load `32-bit` value from bytes array in big-endian format
+ * \param[in] ptr: Minimum `4-bytes` long input array to extract bytes from
+ * \return `32-bit` value extracted from input array
+ */
+static inline uint32_t
+lwutil_ld_u32_be(const void* ptr) {
+ const uint8_t* p = (const uint8_t*)ptr;
+ return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+}
+
+void lwutil_u8_to_2asciis(uint8_t hex, char* ascii);
+void lwutil_u16_to_4asciis(uint16_t hex, char* ascii);
+void lwutil_u32_to_8asciis(uint32_t hex, char* ascii);
+uint8_t lwutil_ld_u32_varint(const void* ptr, size_t ptr_len, uint32_t* val_out);
+uint8_t lwutil_st_u32_varint(uint32_t val, void* ptr, size_t ptr_len);
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LWUTIL_HDR_H */
diff --git a/common/letter-shell-master/.gitattributes b/common/letter-shell-master/.gitattributes
new file mode 100644
index 0000000..dfe0770
--- /dev/null
+++ b/common/letter-shell-master/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/common/letter-shell-master/.gitignore b/common/letter-shell-master/.gitignore
new file mode 100644
index 0000000..4d9cd93
--- /dev/null
+++ b/common/letter-shell-master/.gitignore
@@ -0,0 +1,10 @@
+CMakeFiles
+cmake_install.cmake
+CMakeCache.txt
+demo/x86-gcc/Makefile
+demo/x86-gcc/LetterShell
+demo/x86-gcc/LetterShell.map
+demo/x86-gcc/compile_commands.json
+demo/x86-gcc/.cache/
+.vscode
+build
\ No newline at end of file
diff --git a/common/letter-shell-master/LICENSE b/common/letter-shell-master/LICENSE
new file mode 100644
index 0000000..229cea0
--- /dev/null
+++ b/common/letter-shell-master/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Letter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/common/letter-shell-master/README.md b/common/letter-shell-master/README.md
new file mode 100644
index 0000000..5752e28
--- /dev/null
+++ b/common/letter-shell-master/README.md
@@ -0,0 +1,655 @@
+# letter shell 3.x
+
+
+
+
+
+
+涓涓姛鑳藉己澶х殑宓屽叆寮弒hell
+
+
+
+- [letter shell 3.x](#letter-shell-3x)
+ - [绠浠媇(#绠浠)
+ - [鍔熻兘](#鍔熻兘)
+ - [绉绘璇存槑](#绉绘璇存槑)
+ - [浣跨敤鏂瑰紡](#浣跨敤鏂瑰紡)
+ - [鍑芥暟瀹氫箟](#鍑芥暟瀹氫箟)
+ - [main鍑芥暟褰㈠紡](#main鍑芥暟褰㈠紡)
+ - [鏅欳鍑芥暟褰㈠紡](#鏅歝鍑芥暟褰㈠紡)
+ - [鍙橀噺浣跨敤](#鍙橀噺浣跨敤)
+ - [鍦ㄥ嚱鏁颁腑鑾峰彇褰撳墠shell瀵硅薄](#鍦ㄥ嚱鏁颁腑鑾峰彇褰撳墠shell瀵硅薄)
+ - [鎵ц鏈鍑哄嚱鏁癩(#鎵ц鏈鍑哄嚱鏁)
+ - [鍛戒护瀹氫箟](#鍛戒护瀹氫箟)
+ - [瀹氫箟鏂瑰紡](#瀹氫箟鏂瑰紡)
+ - [瀹氫箟瀹忚鏄嶿(#瀹氫箟瀹忚鏄)
+ - [鍛戒护灞炴у瓧娈佃鏄嶿(#鍛戒护灞炴у瓧娈佃鏄)
+ - [浠g悊鍑芥暟鍜屼唬鐞嗗弬鏁拌В鏋怾(#浠g悊鍑芥暟鍜屼唬鐞嗗弬鏁拌В鏋)
+ - [鍑芥暟绛惧悕](#鍑芥暟绛惧悕)
+ - [鑷畾涔夌被鍨嬭В鏋怾(#鑷畾涔夌被鍨嬭В鏋)
+ - [鏉冮檺绯荤粺璇存槑](#鏉冮檺绯荤粺璇存槑)
+ - [閿佽鏄嶿(#閿佽鏄)
+ - [浼寸敓瀵硅薄](#浼寸敓瀵硅薄)
+ - [灏捐妯″紡](#灏捐妯″紡)
+ - [寤鸿缁堢杞欢](#寤鸿缁堢杞欢)
+ - [鍛戒护閬嶅巻宸ュ叿](#鍛戒护閬嶅巻宸ュ叿)
+ - [x86 demo](#x86-demo)
+
+## 绠浠
+
+[letter shell](https://github.com/NevermindZZT/letter-shell)鏄竴涓狢璇█缂栧啓鐨勶紝鍙互宓屽叆鍦ㄧ▼搴忎腑鐨勫祵鍏ュ紡shell锛屼富瑕侀潰鍚戝祵鍏ュ紡璁惧锛屼互C璇█鍑芥暟涓鸿繍琛屽崟浣嶏紝鍙互閫氳繃鍛戒护琛岃皟鐢紝杩愯绋嬪簭涓殑鍑芥暟
+
+鐩稿2.x鐗堟湰锛宭etter shell 3.x澧炲姞浜嗙敤鎴风鐞嗭紝鏉冮檺绠$悊锛屼互鍙婂鏂囦欢绯荤粺鐨勫垵姝ユ敮鎸
+
+姝ゅ3.x鐗堟湰淇敼浜嗗懡浠ゆ牸寮忓拰瀹氫箟锛2.x鐗堟湰鐨勫伐绋嬮渶瑕佺粡杩囩畝鍗曠殑淇敼鎵嶈兘瀹屾垚杩佺Щ
+
+鑻ュ彧闇瑕佷娇鐢ㄥ熀纭鍔熻兘锛屽彲浠ヤ娇鐢╗letter shell 2.x](https://github.com/NevermindZZT/letter-shell/tree/shell2.x)鐗堟湰
+
+浣跨敤璇存槑鍙弬鑰僛Letter shell 3.0 鍏ㄦ柊鍑哄彂](https://nevermindzzt.github.io/2020/01/19/Letter%20shell%203.0%E5%85%A8%E6%96%B0%E5%87%BA%E5%8F%91/)
+
+濡傛灉浠3.0鐗堟湰杩佺Щ鍒3.1浠ヤ笂鐗堟湰锛岃娉ㄦ剰3.1鐗堟湰瀵硅鍐欏嚱鏁板師鍨嬬殑淇敼
+
+## 鍔熻兘
+
+- 鍛戒护鑷姩琛ュ叏
+- 蹇嵎閿姛鑳藉畾涔
+- 鍛戒护鏉冮檺绠$悊
+- 鐢ㄦ埛绠$悊
+- 鍙橀噺鏀寔
+- 浠g悊鍑芥暟鍜屽弬鏁颁唬鐞嗚В鏋
+
+## 绉绘璇存槑
+
+1. 瀹氫箟shell瀵硅薄
+
+ ```C
+ Shell shell;
+ ```
+
+2. 瀹氫箟shell璇伙紝鍐欏嚱鏁
+
+ 瀵逛簬浣跨敤letter shell 3.0鐗堟湰锛岃鍐欏嚱鏁板師鍨嬪涓嬶細
+
+ ```C
+ /**
+ * @brief shell璇诲彇鏁版嵁鍑芥暟鍘熷瀷
+ *
+ * @param char shell璇诲彇鐨勫瓧绗
+ *
+ * @return char 0 璇诲彇鏁版嵁鎴愬姛
+ * @return char -1 璇诲彇鏁版嵁澶辫触
+ */
+ typedef signed char (*shellRead)(char *);
+
+ /**
+ * @brief shell鍐欐暟鎹嚱鏁板師鍨
+ *
+ * @param const char 闇鍐欑殑瀛楃
+ */
+ typedef void (*shellWrite)(const char);
+ ```
+
+ 瀵逛簬浣跨敤letter shell 3.1鐗堟湰锛屼负浜嗕紭鍖栨晥鐜囷紝淇敼浜嗚鍐欏嚱鏁板師鍨嬶紝濡備笅锛
+
+ ```C
+ /**
+ * @brief shell璇诲彇鏁版嵁鍑芥暟鍘熷瀷
+ *
+ * @param data shell璇诲彇鐨勫瓧绗
+ * @param len 璇锋眰璇诲彇鐨勫瓧绗︽暟閲
+ *
+ * @return unsigned short 瀹為檯璇诲彇鍒扮殑瀛楃鏁伴噺
+ */
+ typedef unsigned short (*shellRead)(char *data, unsigned short len);
+
+ /**
+ * @brief shell鍐欐暟鎹嚱鏁板師鍨
+ *
+ * @param data 闇鍐欑殑瀛楃鏁版嵁
+ * @param len 闇瑕佸啓鍏ョ殑瀛楃鏁
+ *
+ * @return unsigned short 瀹為檯鍐欏叆鐨勫瓧绗︽暟閲
+ */
+ typedef unsigned short (*shellWrite)(char *data, unsigned short len);
+ ```
+
+3. 鐢宠涓鐗囩紦鍐插尯
+
+ ```C
+ char shellBuffer[512];
+ ```
+
+4. 璋冪敤shellInit杩涜鍒濆鍖
+
+ ```C
+ shell.read = shellRead;
+ shell.write = shellWrite;
+ shellInit(&shell, shellBuffer, 512);
+ ```
+
+5. 璋冪敤(寤虹珛)shell浠诲姟
+
+ 瀵逛簬杩愯鍦ㄦ搷浣滅郴缁熺殑鎯呭喌锛屽缓绔媊shellTask`浠诲姟(纭繚sell_cfg.h涓殑閰嶇疆鏃犺)锛屼换鍔″弬鏁颁负shell瀵硅薄
+
+ ```C
+ OsTaskCreate(shellTask, &shell, ...);
+ ```
+
+ 瀵逛簬瑁告満鐜锛屽湪涓诲惊鐜腑璋冪敤`shellTask`锛屾垨鑰呭湪鎺ユ敹鍒版暟鎹椂锛岃皟鐢╜shellHandler`
+
+6. 璇存槑
+
+ - 瀵逛簬涓柇鏂瑰紡浣跨敤shell锛屼笉鐢ㄥ畾涔塦shell->read`锛屼絾闇瑕佸湪涓柇涓皟鐢╜shellHandler`
+ - 瀵逛簬浣跨敤鎿嶄綔绯荤粺鐨勬儏鍐碉紝浣胯兘`SHEHLL_TASK_WHILE`瀹忥紝鐒跺悗鍒涘缓shellTask浠诲姟
+
+7. 鍏朵粬閰嶇疆
+
+ - 瀹氫箟瀹廯SHELL_GET_TICK()`涓鸿幏鍙栫郴缁焧ick鍑芥暟锛屼娇鑳絫ab鍙屽嚮鎿嶄綔锛岀敤鎴烽暱甯姪琛ュ叏
+
+8. 閰嶇疆瀹
+
+ shell_cfg.h鏂囦欢涓寘鍚簡鎵鏈夌敤浜庨厤缃畇hell鐨勫畯锛屽湪浣跨敤鍓嶏紝闇瑕佹牴鎹渶瑕佽繘琛岄厤缃
+
+ 寤鸿閲囩敤 overlay 鐨勬柟寮忥紝鏂板缓涓涓ご鏂囦欢锛屼緥濡 `shell_cfg_user.h`锛岀劧鍚庡畾涔夌紪璇戝畯 `SHELL_CFG_USER="shell_cfg_user.h"`锛屽湪杩欎釜澶存枃浠朵腑娣诲姞闇瑕佷慨鏀圭殑閰嶇疆鍗冲彲
+
+ | 瀹 | 鎰忎箟 |
+ | --------------------------- | ------------------------------ |
+ | SHELL_TASK_WHILE | 鏄惁浣跨敤榛樿shell浠诲姟while寰幆 |
+ | SHELL_USING_CMD_EXPORT | 鏄惁浣跨敤鍛戒护瀵煎嚭鏂瑰紡 |
+ | SHELL_USING_COMPANION | 鏄惁浣跨敤shell浼寸敓瀵硅薄鍔熻兘 |
+ | SHELL_SUPPORT_END_LINE | 鏄惁鏀寔shell灏捐妯″紡 |
+ | SHELL_HELP_LIST_USER | 鏄惁鍦ㄨ緭鍏ュ懡浠ゅ垪琛ㄤ腑鍒楀嚭鐢ㄦ埛 |
+ | SHELL_HELP_LIST_VAR | 鏄惁鍦ㄨ緭鍏ュ懡浠ゅ垪琛ㄤ腑鍒楀嚭鍙橀噺 |
+ | SHELL_HELP_LIST_KEY | 鏄惁鍦ㄨ緭鍏ュ懡浠ゅ垪琛ㄤ腑鍒楀嚭鎸夐敭 |
+ | SHELL_ENTER_LF | 浣跨敤LF浣滀负鍛戒护琛屽洖杞﹁Е鍙 |
+ | SHELL_ENTER_CR | 浣跨敤CR浣滀负鍛戒护琛屽洖杞﹁Е鍙 |
+ | SHELL_ENTER_CRLF | 浣跨敤CRLF浣滀负鍛戒护琛屽洖杞﹁Е鍙 |
+ | SHELL_EXEC_UNDEF_FUNC | 浣跨敤鎵ц鏈鍑哄嚱鏁扮殑鍔熻兘 |
+ | SHELL_COMMAND_MAX_LENGTH | shell鍛戒护鏈澶ч暱搴 |
+ | SHELL_PARAMETER_MAX_NUMBER | shell鍛戒护鍙傛暟鏈澶ф暟閲 |
+ | SHELL_HISTORY_MAX_NUMBER | 鍘嗗彶鍛戒护璁板綍鏁伴噺 |
+ | SHELL_DOUBLE_CLICK_TIME | 鍙屽嚮闂撮殧(ms) |
+ | SHELL_QUICK_HELP | 蹇熷府鍔 |
+ | SHELL_MAX_NUMBER | 绠$悊鐨勬渶澶hell鏁伴噺 |
+ | SHELL_GET_TICK() | 鑾峰彇绯荤粺鏃堕棿(ms) |
+ | SHELL_USING_LOCK | 鏄惁浣跨敤閿 |
+ | SHELL_MALLOC(size) | 鍐呭瓨鍒嗛厤鍑芥暟(shell鏈韩涓嶉渶瑕) |
+ | SHELL_FREE(obj) | 鍐呭瓨閲婃斁鍑芥暟(shell鏈韩涓嶉渶瑕) |
+ | SHELL_SHOW_INFO | 鏄惁鏄剧ずshell淇℃伅 |
+ | SHELL_CLS_WHEN_LOGIN | 鏄惁鍦ㄧ櫥褰曞悗娓呴櫎鍛戒护琛 |
+ | SHELL_DEFAULT_USER | shell榛樿鐢ㄦ埛 |
+ | SHELL_DEFAULT_USER_PASSWORD | 榛樿鐢ㄦ埛瀵嗙爜 |
+ | SHELL_LOCK_TIMEOUT | shell鑷姩閿佸畾瓒呮椂 |
+ | SHELL_USING_FUNC_SIGNATURE | 浣跨敤鍑芥暟绛惧悕 |
+
+## 浣跨敤鏂瑰紡
+
+### 鍑芥暟瀹氫箟
+
+letter shell 3.x鍚屾椂鏀寔涓ょ褰㈠紡鐨勫嚱鏁板畾涔夋柟寮忥紝褰㈠main鍑芥暟瀹氫箟鐨刞func(int argc, char *agrv[])`浠ュ強褰㈠鏅欳鍑芥暟鐨勫畾涔塦func(int i, char *str, ...)`锛屼袱绉嶅嚱鏁板畾涔夋柟寮忛傜敤浜庝笉鍚岀殑鍦烘櫙
+
+#### main鍑芥暟褰㈠紡
+
+浣跨敤姝ゆ柟寮忥紝涓涓嚱鏁板畾涔夌殑渚嬪瓙濡備笅锛
+
+```C
+int func(int argc, char *agrv[])
+{
+ printf("%dparameter(s)\r\n", argc);
+ for (char i = 1; i < argc; i++)
+ {
+ printf("%s\r\n", argv[i]);
+ }
+}
+SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), func, func, test);
+```
+
+缁堢璋冪敤
+
+```sh
+letter:/$ func "hello world"
+2 parameter(s)
+hello world
+```
+
+#### 鏅欳鍑芥暟褰㈠紡
+
+浣跨敤姝ゆ柟寮忥紝shell浼氳嚜鍔ㄥ鍙傛暟杩涜杞寲澶勭悊锛岀洰鍓嶆敮鎸佷簩杩涘埗锛屽叓杩涘埗锛屽崄杩涘埗锛屽崄鍏繘鍒舵暣褰紝瀛楃锛屽瓧绗︿覆鐨勮嚜鍔ㄥ鐞嗭紝濡傛灉闇瑕佸叾浠栫被鍨嬬殑鍙傛暟锛岃浣跨敤浠g悊鍙傛暟瑙f瀽鐨勬柟寮(鍙傝僛浠g悊鍑芥暟鍜屼唬鐞嗗弬鏁拌В鏋怾(#浠g悊鍑芥暟鍜屼唬鐞嗗弬鏁拌В鏋))锛屾垨鑰呬娇鐢ㄥ瓧绗︿覆鐨勬柟寮忎綔涓哄弬鏁帮紝鑷杩涜澶勭悊锛屼緥瀛愬涓嬶細
+
+```C
+int func(int i, char ch, char *str)
+{
+ printf("input int: %d, char: %c, string: %s\r\n", i, ch, str);
+}
+SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), func, func, test);
+```
+
+缁堢璋冪敤
+
+```sh
+letter:/$ func 666 'A' "hello world"
+input int: 666, char: A, string: hello world
+```
+
+### 鍙橀噺浣跨敤
+
+letter shell 3.x鏀寔瀵煎嚭鍙橀噺锛岄氳繃鍛戒护琛屾煡鐪嬶紝璁剧疆浠ュ強浣跨敤鍙橀噺鐨勫
+
+- 瀵煎嚭鍙橀噺
+
+ 鍙橀噺瀵煎嚭浣跨敤`SHELL_EXPORT_VAR`瀹忥紝鏀寔鏁村舰(char, short, int)锛屽瓧绗︿覆锛屾寚閽堜互鍙婅妭鐐瑰彉閲忥紝鍙橀噺瀵煎嚭闇瑕佷娇鐢ㄥ紩鐢ㄧ殑鏂瑰紡锛屽鏋滀笉鍏佽瀵瑰彉閲忚繘琛屼慨鏀癸紝鍦ㄥ睘鎬т腑娣诲姞`SHELL_CMD_READ_ONLY`
+
+ ```C
+ int varInt = 0;
+ SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_INT), varInt, &varInt, test);
+
+ char str[] = "test string";
+ SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_STRING), varStr, str, test);
+
+ Log log;
+ SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_POINT), log, &log, test);
+ ```
+
+- 鏌ョ湅鍙橀噺
+
+ 鍦ㄥ懡浠よ鐩存帴杈撳叆瀵煎嚭鐨勫彉閲忓悕鍗冲彲鏌ョ湅鍙橀噺褰撳墠鐨勫
+
+ ```sh
+ letter:/$ varInt
+ varInt = 0, 0x00000000
+
+ letter:/$ varStr
+ varStr = "test string"
+ ```
+
+- 淇敼鍙橀噺
+
+ 浣跨敤`setVar`鍛戒护淇敼鍙橀噺鐨勫硷紝瀵逛簬瀛楃涓插瀷鍙橀噺锛岃纭瀛楃涓叉湁鍒嗛厤瓒冲鐨勭┖闂达紝鎸囬拡绫诲瀷鐨勫彉閲忎笉鍙慨鏀
+
+ ```sh
+ letter:/$ setVar varInt 45678
+ varInt = 45678, 0x0000b26e
+
+ letter:/$ setVar varStr "hello"
+ varStr = "hello"
+ ```
+
+- 浣跨敤鍙橀噺
+
+ letter shell 3.x鐨勫彉閲忓彲浠ュ湪鍛戒护涓綔涓哄弬鏁颁紶閫掞紝瀵逛簬闇瑕佷紶閫掔粨鏋勪綋寮曠敤鍒板懡浠や腑鐨勫満鏅壒鍒傜敤锛屼娇鐢╜$`+鍙橀噺鍚嶇殑鏂瑰紡浼犻
+
+ ```sh
+ letter:/$ shellPrint $shell "hello world\r\n"
+ hello world
+ ```
+
+### 鍦ㄥ嚱鏁颁腑鑾峰彇褰撳墠shell瀵硅薄
+
+letter shell閲囧彇涓涓潤鎬佹暟缁勫瀹氫箟鐨勫涓猻hell杩涜绠$悊锛宻hell鏁伴噺鍙互淇敼瀹廯SHELL_MAX_NUMBER`瀹氫箟(涓轰簡涓嶄娇鐢ㄥ姩鎬佸唴瀛樺垎閰嶏紝姝ゅ閫氳繃鏁版嵁杩涜绠$悊)锛屼粠鑰岋紝鍦╯hell鎵ц鐨勫嚱鏁颁腑锛屽彲浠ヨ皟鐢╜shellGetCurrent()`鑾峰緱褰撳墠娲诲姩鐨剆hell瀵硅薄锛屼粠鑰屽彲浠ュ疄鐜版煇涓涓嚱鏁板湪涓嶅悓鐨剆hell瀵硅薄涓彂鐢熶笉鍚岀殑琛屼负锛屼篃鍙互閫氳繃杩欑鏂瑰紡鑾峰緱shell瀵硅薄鍚庯紝璋冪敤`shellWriteString(shell, string)`杩涜shell鐨勮緭鍑
+
+### 鎵ц鏈鍑哄嚱鏁
+
+letter shell鏀寔閫氳繃鍑芥暟鍦板潃鐩存帴鎵ц鍑芥暟锛屽彲浠ユ柟渚挎墽琛岄偅浜涙病鏈夊鍑猴紝浣嗘槸鍙堜复鏃堕渶瑕佷娇鐢ㄧ殑鍑芥暟锛屼娇鐢ㄥ懡浠exec [addr] [args]`鎵ц锛屼娇鐢ㄦ鍔熻兘闇瑕佸紑鍚痐SHELL_EXEC_UNDEF_FUNC`瀹忥紝娉ㄦ剰锛岀敱浜庣洿鎺ユ搷浣滃嚱鏁板湴鍧鎵ц锛屽鏋滅粰杩涚殑鍦板潃鏈夎锛屽彲鑳藉紩璧风▼搴忓穿婧
+
+鍑芥暟鐨勫湴鍧鍙互閫氳繃缂栬瘧鐢熸垚鐨勬枃浠舵煡鎵撅紝姣斿璇村浜巏eil锛屽彲浠ュ湪`.map`鏂囦欢涓煡鎵惧埌姣忎釜鍑芥暟鐨勫湴鍧锛屼絾鏄娉ㄦ剰鏈変簺骞冲彴鍙兘闇瑕佽瀵瑰湴鍧鍋氳繘涓姝ュ鐞嗭紝姣斿璇村浜 arm 骞冲彴锛屽鏋滀娇鐢ㄧ殑鏄 Thumb 鎸囦护闆嗭紝閭d箞闇瑕佸皢鍦板潃鐨勬渶浣庝綅缃 1锛屾瘮濡傝`shellClear`鍑芥暟鍦板潃涓篳0x08028620`锛屽垯閫氳繃`exec`鎵ц搴斾负`exec 0x08028621`
+
+鍏朵粬缂栬瘧鍣ㄦ煡鎵惧嚱鏁板湴鍧鐨勬柟寮忓拰鍦板潃鍋忕Щ鐨勫鐞嗭紝璇峰弬鑰冨悇缂栬瘧鍣ㄦ墜鍐
+
+## 鍛戒护瀹氫箟
+
+letter shell 3.x灏嗗彲鎵ц鐨勫嚱鏁板懡浠ゅ畾涔夛紝鐢ㄦ埛瀹氫箟锛屾寜閿畾涔変互鍙婂彉閲忓畾涔夌粺涓褰掍负鍛戒护瀹氫箟锛屼娇鐢ㄧ浉鍚岀殑缁撴瀯鍌ㄥ瓨锛屾煡鎵惧拰鎵ц
+
+### 瀹氫箟鏂瑰紡
+
+letter shell 鏀寔浣跨敤鍛戒护瀵煎嚭鏂瑰紡鍜屽懡浠よ〃鏂瑰紡杩涜鍛戒护鐨勬坊鍔狅紝瀹氫箟锛岄氳繃瀹廯``SHELL_USING_CMD_EXPORT```鎺у埗
+
+鍛戒护瀵煎嚭鏂瑰紡鏀寔keil锛孖AR浠ュ強GCC
+
+1. 鍛戒护瀵煎嚭鏂瑰紡
+
+ letter shell 鏀寔鍦ㄥ嚱鏁颁綋澶栭儴锛岄噰鐢ㄥ畾涔夊父閲忕殑鏂瑰紡瀹氫箟鍛戒护锛屼緥濡俙SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE (SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,help, shellHelp, show command info\r\nhelp [cmd]);`
+
+ 瀵逛簬浣跨敤keil杩涜缂栬瘧锛岄渶瑕佸湪keil鐨則arget option涓鍔--keep shellCommand*锛岄槻姝㈠畾涔夌殑鍛戒护琚紭鍖栨帀
+
+ 浣跨敤GCC缂栬瘧鏃讹紝闇瑕佸湪ld鏂囦欢涓殑鍙鏁版嵁鍖(寤鸿)娣诲姞锛
+
+ ```ld
+ _shell_command_start = .;
+ KEEP (*(shellCommand))
+ _shell_command_end = .;
+ ```
+
+2. 鍛戒护琛ㄦ柟寮
+
+ - 褰撲娇鐢ㄥ叾浠栨殏鏃朵笉鏀寔浣跨敤鍛戒护瀵煎嚭鏂瑰紡鐨勭紪璇戝櫒鏃讹紝闇瑕佸湪`shell_cmd_list.c`鏂囦欢鐨勫懡浠よ〃涓坊鍔
+
+ ```C
+ const SHELL_CommandTypeDef shellDefaultCommandList[] =
+ {
+ SHELL_CMD_ITEM(
+ SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,
+ help, shellHelp, show command info\r\nhelp [cmd]),
+ };
+ ```
+
+### 瀹氫箟瀹忚鏄
+
+letter shell 3.x瀵瑰彲鎵ц鍛戒护锛屾寜閿紝鐢ㄦ埛浠ュ強鍙橀噺鍒嗗埆鎻愪緵浜嗕竴涓畯锛岀敤浜庤繘琛屽懡浠ゅ畾涔
+
+1. 鍙墽琛屽懡浠ゅ畾涔
+
+ 浣跨敤瀹廯SHELL_EXPORT_CMD`瀹氫箟鍙墽琛屽懡浠わ紝瀹氫箟濡備笅
+
+ ```C
+ /**
+ * @brief shell 鍛戒护瀹氫箟
+ *
+ * @param _attr 鍛戒护灞炴
+ * @param _name 鍛戒护鍚
+ * @param _func 鍛戒护鍑芥暟
+ * @param _desc 鍛戒护鎻忚堪
+ */
+ #define SHELL_EXPORT_CMD(_attr, _name, _func, _desc) \
+ const char shellCmd##_name[] = #_name; \
+ const char shellDesc##_name[] = #_desc; \
+ SHELL_USED const ShellCommand \
+ shellCommand##_name SHELL_SECTION("shellCommand") = \
+ { \
+ .attr.value = _attr, \
+ .data.cmd.name = shellCmd##_name, \
+ .data.cmd.function = (int (*)())_func, \
+ .data.cmd.desc = shellDesc##_name \
+ }
+ ```
+
+2. 鍙橀噺瀹氫箟
+
+ 浣跨敤瀹廯SHELL_EXPORT_VAR`瀹氫箟鍙橀噺锛屽畾涔夊涓
+
+ ```C
+ /**
+ * @brief shell 鍙橀噺瀹氫箟
+ *
+ * @param _attr 鍙橀噺灞炴
+ * @param _name 鍙橀噺鍚
+ * @param _value 鍙橀噺鍊
+ * @param _desc 鍙橀噺鎻忚堪
+ */
+ #define SHELL_EXPORT_VAR(_attr, _name, _value, _desc) \
+ const char shellCmd##_name[] = #_name; \
+ const char shellDesc##_name[] = #_desc; \
+ SHELL_USED const ShellCommand \
+ shellVar##_name SHELL_SECTION("shellCommand") = \
+ { \
+ .attr.value = _attr, \
+ .data.var.name = shellCmd##_name, \
+ .data.var.value = (void *)_value, \
+ .data.var.desc = shellDesc##_name \
+ }
+ ```
+
+ 鍙橀噺瀹氫箟鏃讹紝`_value`搴旇鏄彉閲忕殑寮曠敤锛屽鏋滃彉閲忎笉鍏佽淇敼锛屽垯闇瑕佸湪澧炲姞`SHELL_CMD_READ_ONLY`灞炴
+
+3. 鐢ㄦ埛瀹氫箟
+
+ 浣跨敤瀹廯SHELL_EXPORT_USER`瀹氫箟鐢ㄦ埛锛屽畾涔夊涓
+
+ ```C
+ /**
+ * @brief shell 鐢ㄦ埛瀹氫箟
+ *
+ * @param _attr 鐢ㄦ埛灞炴
+ * @param _name 鐢ㄦ埛鍚
+ * @param _password 鐢ㄦ埛瀵嗙爜
+ * @param _desc 鐢ㄦ埛鎻忚堪
+ */
+ #define SHELL_EXPORT_USER(_attr, _name, _password, _desc) \
+ const char shellCmd##_name[] = #_name; \
+ const char shellPassword##_name[] = #_password; \
+ const char shellDesc##_name[] = #_desc; \
+ SHELL_USED const ShellCommand \
+ shellUser##_name SHELL_SECTION("shellCommand") = \
+ { \
+ .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_USER), \
+ .data.user.name = shellCmd##_name, \
+ .data.user.password = shellPassword##_name, \
+ .data.user.desc = shellDesc##_name \
+ }
+ ```
+
+4. 鎸夐敭瀹氫箟
+
+ 浣跨敤瀹廯SHELL_EXPORT_KEY`瀹氫箟鎸夐敭锛屽畾涔夊涓
+
+ ```C
+ /**
+ * @brief shell 鎸夐敭瀹氫箟
+ *
+ * @param _attr 鎸夐敭灞炴
+ * @param _value 鎸夐敭閿
+ * @param _func 鎸夐敭鍑芥暟
+ * @param _desc 鎸夐敭鎻忚堪
+ */
+ #define SHELL_EXPORT_KEY(_attr, _value, _func, _desc) \
+ const char shellDesc##_value[] = #_desc; \
+ SHELL_USED const ShellCommand \
+ shellKey##_value SHELL_SECTION("shellCommand") = \
+ { \
+ .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_KEY), \
+ .data.key.value = _value, \
+ .data.key.function = (void (*)(Shell *))_func, \
+ .data.key.desc = shellDesc##_value \
+ }
+ ```
+
+ 鎸夐敭閿间负鍦ㄧ粓绔緭鍏ユ寜閿細鍙戦佺殑瀛楃涓插簭鍒楋紝浠ュぇ绔ā寮忚〃绀猴紝姣斿鍦⊿ecureCRT涓柇锛屾寜涓婽ab閿紝浼氬彂閫0x0B锛屽垯杩欎釜鎸夐敭鐨勯敭鍊间负0x0B000000锛屽鏋滄寜涓嬫柟鍚戜笂锛屼細渚濇鍙戦0x1B, 0x5B, 0x41, 鍒欒繖涓敭鐨勯敭鍊间负0x1B5B4100
+
+### 鍛戒护灞炴у瓧娈佃鏄
+
+鍦ㄥ懡浠ゅ畾涔変腑锛屾湁涓涓猔attr`瀛楁锛岃〃绀鸿鍛戒护鐨勫睘鎬э紝鍏蜂綋瀹氫箟涓
+
+```C
+union
+{
+ struct
+ {
+ unsigned char permission : 8; /**< command鏉冮檺 */
+ ShellCommandType type : 4; /**< command绫诲瀷 */
+ unsigned char enableUnchecked : 1; /**< 鍦ㄦ湭鏍¢獙瀵嗙爜鐨勬儏鍐典笅鍙敤 */
+ unsigned char readOnly : 1; /**< 鍙 */
+ unsigned char reserve : 1; /**< 淇濈暀 */
+ unsigned char paramNum : 4; /**< 鍙傛暟鏁伴噺 */
+ } attrs;
+ int value;
+} attr;
+```
+
+鍦ㄥ畾涔夊懡浠ゆ椂锛岄渶瑕佺粰瀹氳繖浜涘硷紝鍙互閫氳繃瀹廯SHELL_CMD_PERMISSION(permission)`, `SHELL_CMD_TYPE(type)`, `SHELL_CMD_ENABLE_UNCHECKED`, `SHELL_CMD_DISABLE_RETURN`, `SHELL_CMD_READ_ONLY`, `SHELL_CMD_PARAM_NUM(num)`蹇熷0鏄
+
+## 浠g悊鍑芥暟鍜屼唬鐞嗗弬鏁拌В鏋
+
+letter shell 3.x鍘熺敓鏀寔灏嗘暣鏁帮紝瀛楃锛屽瓧绗︿覆鍙傛暟锛屼互鍙婂湪鏌愪簺鎯呭喌涓嬬殑娴偣鍙傛暟鐩存帴浼犻掔粰鎵ц鍛戒护鐨勫嚱鏁帮紝涓鑸儏鍐典笅锛岃繖鍑犵鍙傛暟绫诲瀷瀹屽叏鍙互婊¤冻璋冭瘯闇瑕侊紝鐒惰屽湪鏌愪簺鎯呭喌涓嬶紝鐢ㄦ埛纭疄闇瑕佷紶閫掑叾浠栫被鍨嬬殑鍙傛暟锛屾鏃讹紝鍙互閫夋嫨灏嗗懡浠ゅ畾涔夋垚main鍑芥暟褰㈠紡锛屼娇鐢ㄥ瓧绗︿覆浼犻掑弬鏁帮紝鐒跺悗鑷瀵瑰弬鏁拌繘琛岃В鏋愶紝闄ゆ涔嬪锛宭etter shell杩樻彁渚涗簡浠g悊鍑芥暟鐨勬満鍒讹紝鍙互瀵逛换鎰忕被鍨嬬殑鍙傛暟杩涜鑷畾涔夎В鏋
+
+鍏充簬浠g悊鍑芥暟鐨勫疄鐜板師鐞嗗拰鍏蜂綋浣跨敤绀轰緥锛屽彲浠ュ弬鑰僛letter-shell浠g悊鍑芥暟瑙f瀽](https://nevermindzzt.github.io/2020/04/17/letter-shell%E4%BB%A3%E7%90%86%E5%87%BD%E6%95%B0%E8%A7%A3%E6%9E%90/)
+
+浣跨敤浠g悊鍑芥暟锛岀敤鎴烽渶瑕佽嚜瀹氫箟浠g悊鍙傛暟瑙f瀽鍣紝鍗充竴涓皢鍩烘湰鍙傛暟(鏁存暟锛屽瓧绗︼紝瀛楃涓插弬鏁)杞崲鎴愮洰鏍囩被鍨嬪弬鏁扮殑鍑芥暟鎴栬呭畯锛宭etter shell榛樿瀹炵幇浜嗘诞鐐圭被鍨嬬殑鍙傛暟瑙f瀽鍣╜SHELL_PARAM_FLOAT(x)`
+
+鐒跺悗锛屼娇鐢ㄤ唬鐞嗗嚱鏁板懡浠ゅ鍑哄畯瀹氫箟鍛戒护锛屾瘮濡傞渶瑕侀渶瑕佷紶閫掑涓诞鐐瑰弬鏁扮殑鍑芥暟锛屽涓
+
+```C
+void test(int a, float b, int c, float d)
+{
+ printf("%d, %f, %d, %f \r\n", a, b, c, d);
+}
+SHELL_EXPORT_CMD_AGENCY(SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
+test, test, test,
+p1, SHELL_PARAM_FLOAT(p2), p3, SHELL_PARAM_FLOAT(p4));
+```
+
+鐩告瘮甯歌鐨勫懡浠ゅ鍑猴紝浠g悊鍑芥暟鍛戒护瀵煎嚭鍓4涓弬鏁板拰甯歌褰㈠紡鐨勫懡浠ゅ鍑轰竴鑷达紝涔嬪悗鐨勫弬鏁板嵆浼犻掕嚦鐩爣鍑芥暟鐨勫弬鏁帮紝letter shell榛樿瀹炵幇鐨勪唬鐞嗗嚱鏁板畾涔夋敮鎸佹渶澶7涓弬鏁帮紝p1~p7锛屽浜庝笉闇瑕佷唬鐞嗗弬鏁拌В鏋愮殑鍙傛暟锛屽彧闇瑕佸搴斿啓鍏px(x涓1~7)`鍗冲彲锛屾瘮濡備笂鏂圭ず渚嬬殑`p1`鍜宍p3`锛岃岄渶瑕佷唬鐞嗗弬鏁拌В鏋愮殑鍙傛暟锛屽垯闇瑕佷娇鐢ㄥ搴旂殑鍙傛暟瑙f瀽鍣紝姣斿涓婃柟绀轰緥鐨刞p2`鍜宍p4`
+
+## 鍑芥暟绛惧悕
+
+letter shell 3.2.x 涔嬪悗锛屽紩鍏ヤ簡鍑芥暟绛惧悕鐨勬蹇碉紝浠ヤ究浜庡弬鏁拌嚜鍔ㄨВ鏋
+
+涔嬪墠鐨勭増鏈噷锛屽鏋滃0鏄庣殑鍛戒护鏄 `SHELL_TYPE_CMD_FUNC`锛宻hell 浼氳嚜鍔ㄨ繘琛屽弬鏁扮殑杞崲锛屼絾鏄弬鏁拌浆鎹㈠悗鐨勭被鍨嬫槸鐚滃嚭鏉ョ殑锛屾棤娉曚繚璇佽浆鎹㈠悗鐨勬暟鎹被鍨嬫槸姝g‘鐨勶紝涓鏃︾寽閿欎簡锛屽氨瀹规槗瀵艰嚧绋嬪簭鎸傛帀
+
+鐢辨锛屽熼壌 Java 绛夎瑷鐨勫嚱鏁扮鍚嶏紝鏂扮増涔熷紩鍏ヤ簡鍑芥暟绛惧悕鐨勬蹇碉紝鍦ㄥ0鏄庡懡浠ゆ椂锛屽彲浠ョ粰瀹氭渶缁堟墽琛屽懡浠ょ殑鍑芥暟鐨勭鍚嶏紝shell 鏍规嵁杩欎釜绛惧悕杩涜鍙傛暟杞崲锛屼娇鐢ㄦ鍔熻兘鏃讹紝闇瑕佹墦寮瀹 `SHELL_USING_FUNC_SIGNATURE`
+
+鍑芥暟绛惧悕鏄竴涓瓧绗︿覆锛岄氳繃杩欎釜瀛楃涓插0鏄庤〃杈惧嚱鏁扮殑鍙傛暟绫诲瀷锛岃繑鍥炲间笉澹版槑锛屾瘮濡備竴涓嚱鏁癭int func(int a, char *b, char c)`锛屽畠鐨勫嚱鏁扮鍚嶅氨鏄 `isc`
+
+鍩烘湰绫诲瀷鐨勫弬鏁扮鍚嶅畾涔夊涓:
+
+| 绫诲瀷 | 绛惧悕 |
+| -------------------- | ---- |
+| char(瀛楃) | c |
+| int/short/char(鏁板瓧) | i |
+| char * (瀛楃涓) | s |
+| pointer | p |
+
+澹版槑鍛戒护鏃讹紝鍦ㄦ渶鍚庢坊鍔犱竴涓弬鏁 `.data.cmd.signature = "isc"` 鍗冲彲锛屾瘮濡傦細
+
+```c
+void shellFuncSignatureTest(int a, char *b, char c)
+{
+ printf("a = %d, b = %s, c = %c\r\n", a, b, c);
+}
+SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
+funcSignatureTest, shellFuncSignatureTest, test function signature, .data.cmd.signature = "isc");
+```
+
+### 鑷畾涔夌被鍨嬭В鏋
+
+鐢变簬鍑芥暟绛惧悕鐨勫紩鐢紝鎴戜滑灏卞彲浠ヤ娇鐢ㄥ嚱鏁扮鍚嶆弿杩颁换浣曞弬鏁帮紝瀵瑰簲鐨勶紝鍦ㄥ弬鏁扮被鍨嬪凡鐭ョ殑鎯呭喌涓嬶紝涔熷彲浠ュ畾涔夊搴旂殑鍙傛暟瑙f瀽鍣ㄨ繘琛屽弬鏁拌В鏋愶紝鑷畾涔夌殑鍙傛暟绫诲瀷绛惧悕闇瑕佷互 `L` 寮澶达紝浠 `;` 缁撳熬锛屾瘮濡傝瀹氫箟涓涓 `TestStruct` 缁撴瀯浣撶被鍨嬩负 `LTestStruct;`锛岄偅涔堟帴鏀惰繖涓粨鏋勪綋涓哄弬鏁扮殑鍑芥暟灏卞彲浠ラ氳繃杩欎釜绫诲瀷绛惧悕瀹氫箟鍑芥暟绛惧悕锛屽苟瀵煎嚭鍛戒护
+
+```c
+typedef struct {
+ int a;
+ char *b;
+} TestStruct;
+
+void shellParamParserTest(int a, TestStruct *data, char *c)
+{
+ printf("a = %d, data->a = %d, data->b = %s, c = %s\r\n", a, data->a, data->b, c);
+}
+SHELL_EXPORT_CMD_SIGN(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
+paramParserTest, shellParamParserTest, test function signature and param parser, iLTestStruct;s);
+```
+
+鍚屾椂锛屾垜浠渶瑕佸鑷畾涔夌殑绫诲瀷瀹氫箟瑙f瀽鍣紝浣跨敤 `SHELL_EXPORT_PARAM_PARSER` 瀹
+
+```c
+int testStructParser(char *string, void **param)
+{
+ TestStruct *data = malloc(sizeof(TestStruct));
+ data->b = malloc(16);
+ if (sscanf(string, "%d %s", &(data->a), data->b) == 2)
+ {
+ *param = (void *)data;
+ return 0;
+ }
+ return -1;
+}
+
+int testStructClener(void *param)
+{
+ TestStruct *data = (TestStruct *)param;
+ free(data->b);
+ free(data);
+ return 0;
+}
+SHELL_EXPORT_PARAM_PARSER(0, LTestStruct;, testStructParser, testStructClener);
+```
+
+`SHELL_EXPORT_PARAM_PARSER` 鎺ユ敹鍥涗釜鍙傛暟锛岀涓涓弬鏁拌〃绀哄睘鎬э紝杩欓噷涓鑸~ 0 鐨嗗彲锛岀浜屼釜鍙傛暟灏辨槸瑙f瀽鍣ㄥ搴旂殑绫诲瀷绛惧悕锛岀涓変釜鍙傛暟鏄В鏋愬櫒鍑芥暟锛岀鍥涗釜鍙傛暟鏄竻鐞嗗嚱鏁帮紝娓呯悊鍑芥暟鍦ㄥ弬鏁拌В鏋愬け璐ユ垨鑰呭懡浠ゆ墽琛屽畬姣曞悗浼氳璋冪敤锛屼竴鑸敤浜庢竻鐞嗚В鏋愬櫒鍒嗛厤鐨勫唴瀛橈紝濡傛灉涓嶉渶瑕佹竻鐞嗗嚱鏁帮紝濉 `NULL` 鍗冲彲
+
+瑙f瀽鍣ㄥ嚱鏁版帴鏀朵袱涓弬鏁帮紝绗竴涓弬鏁版槸杈撳叆鐨勫瓧绗︿覆锛屼篃灏辨槸鍛戒护琛岃緭鍏ョ殑鍙傛暟锛岀浜屼釜鍙傛暟鏄В鏋愬悗鐨勫弬鏁帮紝瑙f瀽鎴愬姛鍚庯紝闇瑕佸皢瑙f瀽鍚庣殑鍙傛暟璧嬪肩粰绗簩涓弬鏁帮紝瑙f瀽鎴愬姛杩斿洖 0锛岃В鏋愬け璐ヨ繑鍥 -1
+
+娓呯悊鍑芥暟鎺ユ敹涓涓弬鏁帮紝灏辨槸瑙f瀽鍣ㄥ嚱鏁拌В鏋愬緱鍒扮殑缁撴灉
+
+## 鏉冮檺绯荤粺璇存槑
+
+letter shell 3.x鐨勬潈闄愮鐞嗗悓鐢ㄦ埛瀹氫箟绱у瘑鐩稿叧锛宭etter shell 3.x浣跨敤8涓猙it浣嶈〃绀哄懡浠ゆ潈闄愶紝褰撶敤鎴峰拰鍛戒护鐨勬潈闄愭寜浣嶄笌涓虹湡锛屾垨鑰呭懡浠ゆ潈闄愪负0鏃讹紝琛ㄧず璇ョ敤鎴锋嫢鏈夋鍛戒护鐨勬潈闄愶紝鍙互璋冪敤璇ュ懡浠
+
+## 閿佽鏄
+
+letter shell 3.1澧炲姞浜唖hell閿侊紝涓昏鐩殑鏄负浜嗛槻姝hell杈撳嚭鍜屽叾浠栬緭鍏(姣斿璇存棩蹇)瀵圭粓绔殑绔炰簤锛屽鑷磋緭鍑烘贩涔辩殑鐜拌薄锛屽鏋滀娇鐢ㄥ満鏅腑娌℃湁鍑虹幇缁堢杈撳嚭娣蜂贡鐨勬儏鍐碉紝鍙互涓嶄娇鐢╯hell閿
+
+娉ㄦ剰: 璇蜂娇鐢ㄦ敮鎸佸祵濂楃殑閿
+
+1. 浣胯兘瀹忓苟瀹炵幇閿
+
+ 浣胯兘`SHELL_USING_LOCK`瀹忥紝瀹炵幇shell涓婇攣鍜岃В閿佸嚱鏁帮紝鍑芥暟鍘熷瀷濡備笅锛
+
+ ```c
+ /**
+ * @brief shell涓婇攣
+ *
+ * @param struct shell_def shell瀵硅薄
+ *
+ * @return 0
+ */
+ typedef int (*shellLock)(struct shell_def *);
+
+ /**
+ * @brief shell瑙i攣
+ *
+ * @param struct shell_def shell瀵硅薄
+ *
+ * @return 0
+ */
+ typedef int (*shellLock)(struct shell_def *);
+ ```
+
+2. 浣跨敤閿
+
+ 鍦ㄥ彲鑳戒骇鐢熺粓绔珵浜夌殑鍦版柟锛屽姞涓妔hell閿侊紝姣斿濡傛灉璋冪敤`shellPrint`杩涜鏍煎紡鍖栬緭鍑
+
+ ```C
+ SHELL_LOCK(shell);
+ shellPrint(shell, ...);
+ SHELL_UNLOCK(shell);
+ ```
+
+3. 娉ㄦ剰
+
+ - 涓嶈鍦╯hell鍛戒护涓皟鐢╯hell閿侊紝闄ら潪瀹炵幇鐨剆hell閿佷负鍙祵濂楃殑閿
+
+## 浼寸敓瀵硅薄
+
+letter shell 3.0.3鐗堟湰寮曞叆浜嗕即鐢熷璞$殑姒傚康锛岄氳繃瀹廯SHELL_USING_COMPANION`寮鍚垨鑰呭叧闂紝鑻ヤ娇鐢ㄤ即鐢熷璞$殑鍔熻兘锛岄渶瑕佸悓鏃跺皢shell_companion.c鏂囦欢鍔犲叆鍒板伐绋嬩腑锛屼即鐢熷璞″彲浠ョ敤浜庨渶瑕佸皢鏌愪釜瀵硅薄鍚宻hell鍏宠仈鐨勫満鏅紝姣斿璇达紝閫氳繃蹇嵎閿帶鍒秙hell缁堢瀵瑰簲鐨勬棩蹇楁墦鍗板璞
+
+涓鑸儏鍐典笅锛屼娇鐢╜shellCompanionAdd`灏嗕即鐢熷璞″悓shell瀵硅薄杩涜鍏宠仈锛屼箣鍚庯紝鍙互鍦╯hell鎿嶄綔涓紝閫氳繃`shellCompanionGet`鑾峰彇鐩稿簲鐨勪即鐢熷璞★紝浠ヨ揪鍒板湪涓嶅悓鐨剆hell涓紝鎿嶄綔涓嶅悓瀵硅薄鐨勭洰鐨
+
+## 灏捐妯″紡
+
+letter shell 3.0.4鐗堟湰鏂板浜嗗熬琛屾ā寮忥紝閫傜敤浜庨渶瑕佸湪shell鎵浣跨敤鐨勪氦浜掔粓绔悓鏃惰緭鍏ュ叾浠栦俊鎭(姣斿璇存棩蹇)鏃讹紝闃叉鍏朵粬淇℃伅鐨勮緭鍑猴紝瀵艰嚧shell浜や簰浣撻獙鏋佸樊鐨勬儏鍐碉紝浣跨敤鏃讹紝浣胯兘瀹廯SHELL_SUPPORT_END_LINE`锛岀劧鍚庡浜庡叾浠栭渶瑕佷娇鐢ㄧ粓绔緭鍏ヤ俊鎭殑鍦版柟锛岃皟鐢╜shellWriteEndLine`鎺ュ彛灏嗕俊鎭緭鍏ワ紝姝ゆ椂锛岃皟鐢╜shellWriteEndLine`杩涜杈撳叆鐨勫唴瀹瑰皢浼氭彃鍏ュ埌鍛戒护琛屼笂鏂癸紝缁堢浼氫竴鐩翠繚鎸乻hell鍛戒护琛屼綅浜庢渶鍚庝竴琛
+
+浣跨敤letter shell灏捐妯″紡缁撳悎[log](./extensions/log/readme.md)鏃ュ織杈撳嚭鐨勬晥鏋滃涓嬶細
+
+
+
+## 寤鸿缁堢杞欢
+
+- 瀵逛簬鍩轰簬涓插彛绉绘锛宭etter shell寤鸿浣跨敤secureCRT杞欢锛宭etter shell涓殑鐩稿叧鎸夐敭鏄犲皠閮芥槸鎸夌収secureCRT杩涜璁捐鐨勶紝浣跨敤鍏朵粬涓插彛杞欢鏃讹紝鍙兘闇瑕佷慨鏀归敭鍊
+
+## 鍛戒护閬嶅巻宸ュ叿
+
+letter shell 3.x鎻愪緵浜嗕竴涓敤浜庨亶鍘嗗伐绋嬩腑鍛戒护瀵煎嚭鐨勫伐鍏凤紝浣嶄簬tools/shellTools.py锛岄渶瑕乸ython3鐜杩愯锛屽彲浠ュ垪鍑哄伐绋嬩腑锛屾墍鏈変娇鐢╜SHELL_EXPORT_XXX`瀵煎嚭鐨勫懡浠ゅ悕锛屼互鍙婁綅缃紝缁撳悎VS Code鍙互鐩存帴杩涜璺宠浆
+
+```sh
+python shellTools.py project
+```
+
+娉ㄦ剰锛歴hellTools浼氶亶鍘嗘寚瀹氱洰褰曚腑鎵鏈夋枃浠讹紝鎵浠ュ綋宸ョ▼涓枃浠惰緝澶氭椂锛岄熷害浼氭瘮杈冩參锛屽缓璁彧鐢ㄤ簬閬嶅巻鐢ㄦ埛妯″潡鐨勭洰褰
+
+## x86 demo
+
+letter shell 3.x鎻愪緵浜嗕竴涓獂86鐨刣emo锛屽彲浠ョ洿鎺ョ紪璇戣繍琛岋紝鍏朵腑鍖呭惈浜嗕竴鏉℃寜閿敭鍊兼祴璇曞懡浠わ紝鍙互娴嬭瘯鎸夐敭閿硷紝鐢ㄤ簬蹇嵎閿殑瀹氫箟锛岀紪璇戣繍琛屾柟娉曞涓嬶細
+
+```sh
+cd demo/x86-gcc/
+cmake .
+make
+./LetterShell
+```
diff --git a/common/letter-shell-master/src/log.c b/common/letter-shell-master/src/log.c
new file mode 100644
index 0000000..3cbe489
--- /dev/null
+++ b/common/letter-shell-master/src/log.c
@@ -0,0 +1,315 @@
+/**
+ * @file log.c
+ * @author Letter (nevermindzzt@gmail.com)
+ * @brief log
+ * @version 1.0.0
+ * @date 2020-07-30
+ *
+ * @copyright (c) 2020 Letter
+ *
+ */
+#include "log.h"
+#include "stdio.h"
+#include "stdarg.h"
+#include "shell.h"
+
+#if LOG_USING_COLOR == 1
+#define memPrintHead CSI(31) \
+ " Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" \
+ CSI(39) \
+ "\r\n"
+#define memPrintAddr CSI(31)"0x%08X: "CSI(39)
+#else
+#define memPrintHead " Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n"
+#define memPrintAddr "0x%08X: "
+#endif
+
+Log *logList[LOG_MAX_NUMBER] = {0};
+
+static char logBuffer[LOG_BUFFER_SIZE];
+
+#if LOG_USING_LOCK == 1
+/**
+ * @brief 涓婇攣log瀵硅薄
+ * @param log log瀵硅薄
+ */
+static void logLock(Log *log)
+{
+ if (log == LOG_ALL_OBJ)
+ {
+ for (short i = 0; i < LOG_MAX_NUMBER; i++)
+ {
+ if (logList[i] && logList[i]->active)
+ {
+ if (logList[i]->lock)
+ {
+ LOG_LOCK(logList[i]);
+ }
+ }
+ }
+ }
+ else if (log->lock)
+ {
+ LOG_LOCK(log);
+ }
+}
+
+/**
+ * @brief 瑙i攣log瀵硅薄
+ * @param log log瀵硅薄
+ */
+static void logUnlock(Log *log)
+{
+ if (log == LOG_ALL_OBJ)
+ {
+ for (short i = 0; i < LOG_MAX_NUMBER; i++)
+ {
+ if (logList[i] && logList[i]->active)
+ {
+ if (logList[i]->unlock)
+ {
+ LOG_UNLOCK(logList[i]);
+ }
+ }
+ }
+ }
+ else if (log->unlock)
+ {
+ LOG_UNLOCK(log);
+ }
+}
+#endif /* LOG_USING_LOCK == 1 */
+
+/**
+ * @brief 娉ㄥ唽log瀵硅薄
+ *
+ * @param log log瀵硅薄
+ */
+void logRegister(Log *log, Shell *shell)
+{
+ if (shell)
+ {
+ log->shell = shell;
+ #if SHELL_USING_COMPANION == 1
+ shellCompanionAdd(shell, SHELL_COMPANION_ID_LOG, log);
+ #endif
+ }
+ for (short i = 0; i < LOG_MAX_NUMBER; i++)
+ {
+ if (logList[i] == 0)
+ {
+ logList[i] = log;
+ return;
+ }
+ }
+}
+
+
+/**
+ * @brief 娉ㄩ攢log瀵硅薄
+ *
+ * @param log log瀵硅薄
+ */
+void logUnRegister(Log *log)
+{
+ for (short i = 0; i < LOG_MAX_NUMBER; i++)
+ {
+ if (logList[i] == log)
+ {
+ logList[i] = 0;
+ return;
+ }
+ }
+}
+
+
+/**
+ * @brief 璁剧疆log鏃ュ織绾у埆
+ *
+ * @param log log瀵硅薄
+ * @param level 鏃ュ織绾у埆
+ */
+void logSetLevel(Log *log, LogLevel level)
+{
+ logAssert(log, return);
+ log->level = level;
+}
+#if SHELL_USING_COMPANION == 1
+SHELL_EXPORT_CMD_AGENCY(
+SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+logSetLevel, logSetLevel, set log level\r\n logSetLevel [level],
+(void *)shellCompanionGet(shellGetCurrent(), SHELL_COMPANION_ID_LOG), p1);
+#else
+SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
+logSetLevel, logSetLevel, set log level\r\n logSetLevel [log] [level]);
+#endif /** SHELL_USING_COMPANION == 1 */
+
+
+/**
+ * @brief log鍐檅uffer
+ *
+ * @param log log瀵硅薄
+ * @param level 鏃ュ織绾у埆
+ * @param buffer buffer
+ * @param len buffer闀垮害
+ */
+static void logWriteBuffer(Log *log, LogLevel level, char *buffer, short len)
+{
+#if LOG_USING_LOCK == 1
+ logLock(log);
+#endif /* LOG_USING_LOCK == 1 */
+ if (log == LOG_ALL_OBJ)
+ {
+ for (short i = 0; i < LOG_MAX_NUMBER; i++)
+ {
+ if (logList[i]
+ && logList[i]->active
+ && logList[i]->level >= level)
+ {
+ logList[i]->write(logBuffer, len);
+ }
+ }
+ }
+ else if (log && log->active && log->level >= level)
+ {
+ log->write(logBuffer, len);
+ }
+#if LOG_USING_LOCK == 1
+ logUnlock(log);
+#endif /* LOG_USING_LOCK == 1 */
+}
+
+/**
+ * @brief log鏍煎紡鍖栧啓鍏ユ暟鎹
+ *
+ * @param log log瀵硅薄
+ * @param level log绾у埆
+ * @param fmt 鏍煎紡
+ * @param ... 鍙傛暟
+ */
+void logWrite(Log *log, LogLevel level, const char *fmt, ...)
+{
+ va_list vargs;
+ int len;
+
+#if LOG_USING_LOCK == 1
+ logLock(log);
+#endif /* LOG_USING_LOCK == 1 */
+ va_start(vargs, fmt);
+ len = vsnprintf(logBuffer, LOG_BUFFER_SIZE - 1, fmt, vargs);
+ va_end(vargs);
+
+ if (len > LOG_BUFFER_SIZE)
+ {
+ len = LOG_BUFFER_SIZE;
+ }
+
+ logWriteBuffer(log, level, logBuffer, len);
+#if LOG_USING_LOCK == 1
+ logUnlock(log);
+#endif /* LOG_USING_LOCK == 1 */
+}
+
+/**
+ * @brief 16杩涘埗杈撳嚭
+ *
+ * @param log log瀵硅薄
+ * @param level 鏃ュ織绾у埆
+ * @param base 鍐呭瓨鍩哄潃
+ * @param length 闀垮害
+ */
+void logHexDump(Log *log, LogLevel level, void *base, unsigned int length)
+{
+ unsigned char *address;
+ unsigned int len;
+ unsigned int printLen = 0;
+
+ if (length == 0 || (log != LOG_ALL_OBJ && log->level < level))
+ {
+ return;
+ }
+#if LOG_USING_LOCK == 1
+ logLock(log);
+#endif /* LOG_USING_LOCK == 1 */
+ len = snprintf(logBuffer, LOG_BUFFER_SIZE - 1, "memory of 0x%08X, size: %d:\r\n%s",
+ (unsigned int)base, length, memPrintHead);
+ logWriteBuffer(log, level, logBuffer, len);
+
+ len = length;
+
+ address = (unsigned char *)((unsigned int)base & (~0x0000000F));
+ length += (unsigned int)base - (unsigned int)address;
+ length = (length + 15) & (~0x0000000F);
+
+ while (length)
+ {
+ printLen += sprintf(logBuffer + printLen, memPrintAddr, (unsigned int)address);
+ for (int i = 0; i < 16; i++)
+ {
+ if ((unsigned int)(address + i) < (unsigned int)base
+ || (unsigned int)(address + i) >= (unsigned int)base + len)
+ {
+ logBuffer[printLen ++] = ' ';
+ logBuffer[printLen ++] = ' ';
+ logBuffer[printLen ++] = ' ';
+ }
+ else
+ {
+ printLen += sprintf(logBuffer + printLen, "%02X ", *(address + i));
+ }
+ }
+ logBuffer[printLen ++] = '|';
+ logBuffer[printLen ++] = ' ';
+ for (int i = 0; i < 16; i++)
+ {
+ if ((unsigned int)(address + i) < (unsigned int)base
+ || (unsigned int)(address + i) >= (unsigned int)base + len)
+ {
+ logBuffer[printLen ++] = ' ';
+ }
+ else
+ {
+ if (*(address + i) >= 32 && *(address + i) <= 126)
+ {
+ printLen += sprintf(logBuffer + printLen, "%c", *(address + i));
+ }
+ else
+ {
+ logBuffer[printLen ++] = '.';
+ }
+ }
+ }
+ logBuffer[printLen ++] = ' ';
+ logBuffer[printLen ++] = '|';
+ logBuffer[printLen ++] = '\r';
+ logBuffer[printLen ++] = '\n';
+ logWriteBuffer(log, level, logBuffer, printLen);
+ address += 16;
+ length -= 16;
+ printLen = 0;
+ }
+#if LOG_USING_LOCK == 1
+ logUnlock(log);
+#endif /* LOG_USING_LOCK == 1 */
+}
+#if SHELL_USING_COMPANION == 1
+SHELL_EXPORT_CMD_AGENCY(
+SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+hexdump, logHexDump, hex dump\r\n hexdump [base] [len],
+(void *)shellCompanionGet(shellGetCurrent(), SHELL_COMPANION_ID_LOG), LOG_NONE, (void *)p1, (unsigned int)p2);
+#else
+SHELL_EXPORT_CMD(
+SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+hexdump, logHexDump, hex dump\r\n hexdump [log] [level] [base] [len]);
+#endif /** SHELL_USING_COMPANION == 1 */
+
+#if SHELL_USING_COMPANION == 1
+void logSwitchLevel(Shell *shell)
+{
+ Log *log = shellCompanionGet(shell, SHELL_COMPANION_ID_LOG);
+ SHELL_ASSERT(log, return);
+ log->level = (LogLevel)(log->level >= LOG_ALL ? LOG_NONE : (log->level + 1));
+ logPrintln("set log level: %d", log->level);
+}
+SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x04000000, logSwitchLevel, switch log level);
+#endif /** SHELL_USING_COMPANION == 1 */
diff --git a/common/letter-shell-master/src/log.h b/common/letter-shell-master/src/log.h
new file mode 100644
index 0000000..14f6100
--- /dev/null
+++ b/common/letter-shell-master/src/log.h
@@ -0,0 +1,225 @@
+/**
+ * @file log.h
+ * @author Letter (nevermindzzt@gmail.com)
+ * @brief log
+ * @version 1.0.0
+ * @date 2020-07-30
+ *
+ * @copyright (c) 2020 Letter
+ *
+ */
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /**< defined __cplusplus */
+
+#include "shell.h"
+#include "bsp_tim.h"
+
+#define LOG_VERSION "1.0.1"
+
+#define SHELL_COMPANION_ID_LOG -2
+
+#define LOG_USING_LOCK 0
+#define LOG_BUFFER_SIZE 512 /**< log杈撳嚭缂撳啿澶у皬 */
+#define LOG_USING_COLOR 1 /**< 鏄惁浣跨敤棰滆壊 */
+#define LOG_MAX_NUMBER 10 /**< 鍏佽娉ㄥ唽鐨勬渶澶og瀵硅薄鏁伴噺 */
+#define LOG_AUTO_TAG 1 /**< 鏄惁鑷姩娣诲姞TAG */
+#define LOG_END "\r\n" /**< log淇℃伅缁撳熬 */
+#define LOG_TIME_STAMP BSP_Get_Tick() /**< 璁剧疆鑾峰彇绯荤粺鏃堕棿鎴 */
+
+#ifndef LOG_TAG
+ #define LOG_TAG __FUNCTION__ /**< 鑷畾娣诲姞鐨凾AG */
+#endif
+
+#ifndef LOG_ENABLE
+ #define LOG_ENABLE 1 /**< 浣胯兘log */
+#endif
+
+#if LOG_USING_LOCK == 1
+#define LOG_LOCK(log) log->lock(log)
+#define LOG_UNLOCK(log) log->unlock(log)
+#else
+#define LOG_LOCK(log)
+#define LOG_UNLOCK(log)
+#endif /* LOG_USING_LOCK == 1 */
+
+#define LOG_ALL_OBJ ((Log *)-1) /**< 鎵鏈夊凡娉ㄥ唽鐨刲og瀵硅薄 */
+
+/**
+ * 缁堢瀛椾綋棰滆壊浠g爜
+ */
+#define CSI_BLACK 30 /**< 榛戣壊 */
+#define CSI_RED 31 /**< 绾㈣壊 */
+#define CSI_GREEN 32 /**< 缁胯壊 */
+#define CSI_YELLOW 33 /**< 榛勮壊 */
+#define CSI_BLUE 34 /**< 钃濊壊 */
+#define CSI_FUCHSIN 35 /**< 鍝佺孩 */
+#define CSI_CYAN 36 /**< 闈掕壊 */
+#define CSI_WHITE 37 /**< 鐧借壊 */
+#define CSI_BLACK_L 90 /**< 浜粦 */
+#define CSI_RED_L 91 /**< 浜孩 */
+#define CSI_GREEN_L 92 /**< 浜豢 */
+#define CSI_YELLOW_L 93 /**< 浜粍 */
+#define CSI_BLUE_L 94 /**< 浜摑 */
+#define CSI_FUCHSIN_L 95 /**< 浜搧绾 */
+#define CSI_CYAN_L 96 /**< 浜潚 */
+#define CSI_WHITE_L 97 /**< 浜櫧 */
+#define CSI_DEFAULT 39 /**< 榛樿 */
+
+#define CSI(code) "\033[" #code "m" /**< ANSI CSI鎸囦护 */
+
+/**
+ * log绾у埆瀛楃(鍖呭惈棰滆壊)
+ */
+#if LOG_USING_COLOR == 1
+#define ERROR_TEXT CSI(31) "E(%d) %s:" CSI(39) /**< 閿欒鏍囩 */
+#define WARNING_TEXT CSI(33) "W(%d) %s:" CSI(39) /**< 璀﹀憡鏍囩 */
+#define INFO_TEXT CSI(32) "I(%d) %s:" CSI(39) /**< 淇℃伅鏍囩 */
+#define DEBUG_TEXT CSI(34) "D(%d) %s:" CSI(39) /**< 璋冭瘯鏍囩 */
+#define VERBOSE_TEXT CSI(36) "V(%d) %s:" CSI(39) /**< 鍐椾綑淇℃伅鏍囩 */
+#else
+#define ERROR_TEXT "E(%d) %s:"
+#define WARNING_TEXT "W(%d) %s:"
+#define INFO_TEXT "I(%d) %s:"
+#define DEBUG_TEXT "D(%d) %s:"
+#define VERBOSE_TEXT "V(%d) %s:"
+#endif
+
+
+/**
+ * @brief 鏃ュ織绾у埆瀹氫箟
+ *
+ */
+typedef enum
+{
+ LOG_NONE = 0, /**< 鏃犵骇鍒 */
+ LOG_ERROR = 1, /**< 閿欒 */
+ LOG_WRANING = 2, /**< 璀﹀憡 */
+ LOG_INFO = 3, /**< 娑堟伅 */
+ LOG_DEBUG = 4, /**< 璋冭瘯 */
+ LOG_VERBOSE = 5, /**< 鍐椾綑 */
+ LOG_ALL = 6, /**< 鎵鏈夋棩蹇 */
+} LogLevel;
+
+
+/**
+ * @brief log瀵硅薄瀹氫箟
+ *
+ */
+typedef struct log_def
+{
+ void (*write)(char *, short shot); /**< 鍐檅uffer */
+ char active; /**< 鏄惁婵娲 */
+ LogLevel level; /**< 鏃ュ織绾у埆 */
+#if LOG_USING_LOCK == 1
+ int (*lock)(struct log_def *); /**< log 鍔犻攣 */
+ int (*unlock)(struct log_def *); /**< log 瑙i攣 */
+#endif /** LOG_USING_LOCK == 1 */
+ Shell *shell; /**< 鍏宠仈shell瀵硅薄 */
+} Log;
+
+
+
+/**
+ * @brief log鎵撳嵃(鑷姩鎹㈣)
+ *
+ * @param fmt 鏍煎紡
+ * @param ... 鍙傛暟
+ */
+#define logPrintln(format, ...) \
+ logWrite(LOG_ALL_OBJ, LOG_NONE, format "\r\n", ##__VA_ARGS__)
+
+
+/**
+ * @brief 鏃ュ織鏍煎紡鍖栬緭鍑
+ *
+ * @param text 娑堟伅鏂囨湰
+ * @param level 鏃ュ織绾у埆
+ * @param fmt 鏍煎紡
+ * @param ... 鍙傛暟
+ */
+#define logFormat(text, level, fmt, ...) \
+ if (LOG_ENABLE) {\
+ logWrite(LOG_ALL_OBJ, level, text " " fmt "" LOG_END, \
+ LOG_TIME_STAMP, LOG_TAG, ##__VA_ARGS__); }
+
+/**
+ * @brief 閿欒log杈撳嚭
+ *
+ * @param fmt 鏍煎紡
+ * @param ... 鍙傛暟
+ */
+#define logError(fmt, ...) \
+ logFormat(ERROR_TEXT, LOG_ERROR, fmt, ##__VA_ARGS__)
+
+/**
+ * @brief 璀﹀憡log杈撳嚭
+ *
+ * @param fmt 鏍煎紡
+ * @param ... 鍙傛暟
+ */
+#define logWarning(fmt, ...) \
+ logFormat(WARNING_TEXT, LOG_WRANING, fmt, ##__VA_ARGS__)
+
+/**
+ * @brief 淇℃伅log杈撳嚭
+ *
+ * @param fmt 鏍煎紡
+ * @param ... 鍙傛暟
+ */
+#define logInfo(fmt, ...) \
+ logFormat(INFO_TEXT, LOG_INFO, fmt, ##__VA_ARGS__)
+
+/**
+ * @brief 璋冭瘯log杈撳嚭
+ *
+ * @param fmt 鏍煎紡
+ * @param ... 鍙傛暟
+ */
+#define logDebug(fmt, ...) \
+ logFormat(DEBUG_TEXT, LOG_DEBUG, fmt, ##__VA_ARGS__)
+
+/**
+ * @brief 鍐椾綑log杈撳嚭
+ *
+ * @param fmt 鏍煎紡
+ * @param ... 鍙傛暟
+ */
+#define logVerbose(fmt, ...) \
+ logFormat(VERBOSE_TEXT, LOG_VERBOSE, fmt, ##__VA_ARGS__)
+
+/**
+ * @brief 鏂█
+ *
+ * @param expr 琛ㄨ揪寮
+ * @param action 鏂█澶辫触鎿嶄綔
+ */
+#define logAssert(expr, action) \
+ if (!(expr)) { \
+ logError("\"" #expr "\" assert failed at file: %s, line: %d", __FILE__, __LINE__); \
+ action; \
+ }
+
+/**
+ * @brief 16杩涘埗杈撳嚭鍒版墍鏈夌粓绔
+ *
+ * @param base 鍐呭瓨鍩哄潃
+ * @param length 闀垮害
+ */
+#define logHexDumpAll(base, length) \
+ logHexDump(LOG_ALL_OBJ, LOG_ALL, base, length)
+
+void logRegister(Log *log, Shell *shell);
+void logUnRegister(Log *log);
+void logSetLevel(Log *log, LogLevel level);
+void logWrite(Log *log, LogLevel level, const char *fmt, ...);
+void logHexDump(Log *log, LogLevel level, void *base, unsigned int length);
+
+#ifdef __cplusplus
+}
+#endif /**< defined __cplusplus */
+
+#endif
diff --git a/common/letter-shell-master/src/readme.md b/common/letter-shell-master/src/readme.md
new file mode 100644
index 0000000..587a025
--- /dev/null
+++ b/common/letter-shell-master/src/readme.md
@@ -0,0 +1,215 @@
+# log
+
+
+
+
+
+
+宓屽叆寮忔棩蹇楁墦鍗板伐鍏
+
+
+
+- [log](#log)
+ - [绠浠媇(#绠浠)
+ - [浣跨敤](#浣跨敤)
+ - [閰嶇疆](#閰嶇疆)
+ - [API](#api)
+ - [logPrintln](#logprintln)
+ - [logError](#logerror)
+ - [logWarning](#logwarning)
+ - [logInfo](#loginfo)
+ - [logDebug](#logdebug)
+ - [logVerbose](#logverbose)
+ - [logAssert](#logassert)
+ - [logRegister](#logregister)
+ - [logSetLevel](#logsetlevel)
+ - [logHexDump](#loghexdump)
+ - [缁撳悎letter shell灏捐妯″紡](#缁撳悎letter-shell灏捐妯″紡)
+ - [鍏朵粬鐢ㄦ硶](#鍏朵粬鐢ㄦ硶)
+ - [鍗曠嫭鎺у埗鏌愪釜鏂囦欢鏃ュ織](#鍗曠嫭鎺у埗鏌愪釜鏂囦欢鏃ュ織)
+
+## 绠浠
+
+log鏄竴涓敤浜庡祵鍏ュ紡绯荤粺鐨勬棩蹇楁墦鍗板伐鍏凤紝鍙互涓烘棩蹇楀畾涔変笉鍚岀殑绾у埆锛岀劧鍚庤缃棩蹇楀伐鍏风殑鎵撳嵃绾у埆锛屽彲浠ヨ繘琛屾棩蹇楁墦鍗扮殑鎺у埗
+
+姝ゅ锛宭og閫氳繃letter shell鐨勪即鐢熷璞″姛鑳斤紝鍙互鍜宭etter shell缁撳悎锛屽疄鐜發og鍜宻hell鐨勭粦瀹氱瓑鍔熻兘
+
+## 浣跨敤
+
+1. 瀹炵幇log鍐檅uffer鍑芥暟
+
+ ```C
+ void uartLogWrite(char *buffer, short len)
+ {
+ serialTransmit(&debugSerial, (uint8_t *)buffer, len, 0x100);
+ }
+ ```
+
+2. 瀹氫箟log瀵硅薄
+
+ ```C
+ Log uartLog = {
+ .write = uartLogWrite,
+ .active = true,
+ .level = LOG_DEBUG
+ };
+ ```
+
+3. 娉ㄥ唽log瀵硅薄
+
+ ```C
+ logRegister(&uartLog, NULL);
+ ```
+
+## 閰嶇疆
+
+閫氳繃淇敼log.h鏂囦欢涓殑瀹忥紝鍙互瀵筶og宸ュ叿杩涜閰嶇疆
+
+| 瀹 | 鎰忎箟 |
+| --------------- | ------------------------- |
+| LOG_BUFFER_SIZE | log杈撳嚭缂撳啿澶у皬 |
+| LOG_USING_COLOR | 鏄惁浣跨敤棰滆壊 |
+| LOG_MAX_NUMBER | 鍏佽娉ㄥ唽鐨勬渶澶og瀵硅薄鏁伴噺 |
+| LOG_AUTO_TAG | 鏄惁鑷姩娣诲姞TAG |
+| LOG_END | log淇℃伅缁撳熬 |
+| LOG_TAG | 鑷畾娣诲姞鐨凾AG |
+| LOG_TIME_STAMP | 璁剧疆鑾峰彇绯荤粺鏃堕棿鎴 |
+
+## API
+
+浠ヤ笅鏄痩og宸ュ叿閮ㄥ垎API鐨勮鏄
+
+### logPrintln
+
+瀹忓0鏄庯紝鐢ㄤ簬涓鑸殑鎵撳嵃杈撳嚭
+
+```C
+#define logPrintln(format, ...)
+```
+
+- 鍙傛暟
+ - `format` 杈撳嚭鏍煎紡
+ - `...` 鍙彉鍙傛暟
+
+### logError
+
+瀹忓0鏄庯紝閿欒鏃ュ織绾у埆杈撳嚭
+
+```C
+#define logError(fmt, ...)
+```
+
+- 鍙傛暟
+ - `fmt` 杈撳嚭鏍煎紡
+ - `...` 鍙彉鍙傛暟
+
+### logWarning
+
+瀹忓0鏄庯紝璀﹀憡鏃ュ織绾у埆杈撳嚭锛屽嚱鏁板師鍨嬪強鍙傛暟璇存槑鍙傝僠logError`
+
+### logInfo
+
+瀹忓0鏄庯紝淇℃伅鏃ュ織绾у埆杈撳嚭锛屽嚱鏁板師鍨嬪強鍙傛暟璇存槑鍙傝僠logError`
+
+### logDebug
+
+瀹忓0鏄庯紝璋冭瘯鏃ュ織绾у埆杈撳嚭锛屽嚱鏁板師鍨嬪強鍙傛暟璇存槑鍙傝僠logError`
+
+### logVerbose
+
+瀹忓0鏄庯紝鍐椾綑鏃ュ織绾у埆杈撳嚭锛屽嚱鏁板師鍨嬪強鍙傛暟璇存槑鍙傝僠logError`
+
+### logAssert
+
+瀹忓0鏄庯紝鏂█
+
+```C
+#define logAssert(expr, action)
+```
+
+- 鍙傛暟
+ - `expr` 琛ㄨ揪寮
+ - `action` 鏂█澶辫触鎵ц鎿嶄綔
+
+### logRegister
+
+娉ㄥ唽log瀵硅薄
+
+```C
+void logRegister(Log *log, Shell *shell)
+```
+
+- 鍙傛暟
+ - `log` log瀵硅薄
+ - `shell` 鍏宠仈鐨剆hell瀵硅薄
+
+### logSetLevel
+
+璁剧疆鏃ュ織绾у埆
+
+```C
+void logSetLevel(Log *log, LogLevel level)
+```
+
+- 鍙傛暟
+ - `log` log瀵硅薄
+ - `level` 鏃ュ織绾у埆
+
+### logHexDump
+
+鏁版嵁16杩涘埗鎵撳嵃
+
+```C
+void logHexDump(Log *log, LogLevel level, void *base, unsigned int length)
+```
+
+- 鍙傛暟
+ - `log` log瀵硅薄
+ - `level` 鏃ュ織绾у埆
+ - `base` 鏁版嵁鍩哄潃
+ - `length` 鏁版嵁闀垮害
+
+## 缁撳悎letter shell灏捐妯″紡
+
+log宸ュ叿鍙互缁撳悎letter shell鐨勫熬琛屾ā寮忥紝瀹炵幇log鍜宻hell鍏辩敤涓涓粓绔紝浣嗕笉褰卞搷shell浜や簰浣撻獙
+
+1. 浣跨敤`shellWriteEndLine`浜嬮」log鍐檅uffer鍑芥暟
+
+ ```C
+ void uartLogWrite(char *buffer, short len)
+ {
+ if (uartLog.shell)
+ {
+ shellWriteEndLine(uartLog.shell, buffer, len);
+ }
+ }
+ ```
+
+2. 瀹氫箟log瀵硅薄
+
+ ```C
+ Log uartLog = {
+ .write = uartLogWrite,
+ .active = true,
+ .level = LOG_DEBUG
+ };
+ ```
+
+3. 娉ㄥ唽log瀵硅薄
+
+ ```C
+ logRegister(&uartLog, &shell);
+ ```
+
+## 鍏朵粬鐢ㄦ硶
+
+### 鍗曠嫭鎺у埗鏌愪釜鏂囦欢鏃ュ織
+
+log宸ュ叿鍙互鍗曠嫭瀵规煇涓枃浠剁殑鏃ュ織杩涜鎵撳嵃鎺у埗锛屼娇鐢ㄦ椂锛屽湪瀵瑰簲鐨.c鏂囦欢涓姞鍏
+
+```C
+#undef LOG_ENABLE
+#define LOG_ENABLE 1
+```
+
+鍗冲彲鍗曠嫭鎺у埗鏌愪釜鏂囦欢鐨勬棩蹇楀紑鍏
diff --git a/common/letter-shell-master/src/shell.c b/common/letter-shell-master/src/shell.c
new file mode 100644
index 0000000..a32b286
--- /dev/null
+++ b/common/letter-shell-master/src/shell.c
@@ -0,0 +1,2033 @@
+#include "shell.h"
+#include "string.h"
+#include "stdio.h"
+#include "stdarg.h"
+#include "shell_ext.h"
+
+
+#if SHELL_USING_CMD_EXPORT == 1
+/**
+ * @brief 榛樿鐢ㄦ埛
+ */
+const char shellCmdDefaultUser[] = SHELL_DEFAULT_USER;
+const char shellPasswordDefaultUser[] = SHELL_DEFAULT_USER_PASSWORD;
+const char shellDesDefaultUser[] = "default user";
+SHELL_USED const ShellCommand shellUserDefault SHELL_SECTION("shellCommand") =
+{
+ .attr.value = SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_USER),
+ .data.user.name = shellCmdDefaultUser,
+ .data.user.password = shellPasswordDefaultUser,
+ .data.user.desc = shellDesDefaultUser
+};
+#endif
+
+#if SHELL_USING_CMD_EXPORT == 1
+ #if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000)
+ extern const unsigned int shellCommand$$Base;
+ extern const unsigned int shellCommand$$Limit;
+ #elif defined(__ICCARM__) || defined(__ICCRX__)
+ #pragma section="shellCommand"
+ #elif defined(__GNUC__)
+ extern const unsigned int _shell_command_start;
+ extern const unsigned int _shell_command_end;
+ #endif
+#else
+ extern const ShellCommand shellCommandList[];
+ extern const unsigned short shellCommandCount;
+#endif
+
+
+/**
+ * @brief shell 甯搁噺鏂囨湰绱㈠紩
+ */
+enum
+{
+#if SHELL_SHOW_INFO == 1
+ SHELL_TEXT_INFO, /**< shell淇℃伅 */
+#endif
+ SHELL_TEXT_CMD_TOO_LONG, /**< 鍛戒护杩囬暱 */
+ SHELL_TEXT_CMD_LIST, /**< 鍙墽琛屽懡浠ゅ垪琛ㄦ爣棰 */
+ SHELL_TEXT_VAR_LIST, /**< 鍙橀噺鍒楄〃鏍囬 */
+ SHELL_TEXT_USER_LIST, /**< 鐢ㄦ埛鍒楄〃鏍囬 */
+ SHELL_TEXT_KEY_LIST, /**< 鎸夐敭鍒楄〃鏍囬 */
+ SHELL_TEXT_CMD_NOT_FOUND, /**< 鍛戒护鏈壘鍒 */
+ SHELL_TEXT_POINT_CANNOT_MODIFY, /**< 鎸囬拡鍙橀噺涓嶅厑璁镐慨鏀 */
+ SHELL_TEXT_VAR_READ_ONLY_CANNOT_MODIFY, /**< 鍙鍙橀噺涓嶅厑璁镐慨鏀 */
+ SHELL_TEXT_NOT_VAR, /**< 鍛戒护涓嶆槸鍙橀噺 */
+ SHELL_TEXT_VAR_NOT_FOUND, /**< 鍙橀噺鏈壘鍒 */
+ SHELL_TEXT_HELP_HEADER, /**< help澶 */
+ SHELL_TEXT_PASSWORD_HINT, /**< 瀵嗙爜杈撳叆鎻愮ず */
+ SHELL_TEXT_PASSWORD_ERROR, /**< 瀵嗙爜閿欒 */
+ SHELL_TEXT_CLEAR_CONSOLE, /**< 娓呯┖鎺у埗鍙 */
+ SHELL_TEXT_CLEAR_LINE, /**< 娓呯┖褰撳墠琛 */
+ SHELL_TEXT_TYPE_CMD, /**< 鍛戒护绫诲瀷 */
+ SHELL_TEXT_TYPE_VAR, /**< 鍙橀噺绫诲瀷 */
+ SHELL_TEXT_TYPE_USER, /**< 鐢ㄦ埛绫诲瀷 */
+ SHELL_TEXT_TYPE_KEY, /**< 鎸夐敭绫诲瀷 */
+ SHELL_TEXT_TYPE_NONE, /**< 闈炴硶绫诲瀷 */
+#if SHELL_EXEC_UNDEF_FUNC == 1
+ SHELL_TEXT_PARAM_ERROR, /**< 鍙傛暟閿欒 */
+#endif
+};
+
+
+static const char *shellText[] =
+{
+#if SHELL_SHOW_INFO == 1
+ [SHELL_TEXT_INFO] =
+ "\r\n"
+ " _ _ _ _ _ _ \r\n"
+ "| | ___| |_| |_ ___ _ __ ___| |__ ___| | |\r\n"
+ "| | / _ \\ __| __/ _ \\ '__| / __| '_ \\ / _ \\ | |\r\n"
+ "| |__| __/ |_| || __/ | \\__ \\ | | | __/ | |\r\n"
+ "|_____\\___|\\__|\\__\\___|_| |___/_| |_|\\___|_|_|\r\n"
+ "\r\n"
+ "Build: "__DATE__" "__TIME__"\r\n"
+ "Version: "SHELL_VERSION"\r\n"
+ "Copyright: (c) 2020 Letter\r\n",
+#endif
+ [SHELL_TEXT_CMD_TOO_LONG] =
+ "\r\nWarning: Command is too long\r\n",
+ [SHELL_TEXT_CMD_LIST] =
+ "\r\nCommand List:\r\n",
+ [SHELL_TEXT_VAR_LIST] =
+ "\r\nVar List:\r\n",
+ [SHELL_TEXT_USER_LIST] =
+ "\r\nUser List:\r\n",
+ [SHELL_TEXT_KEY_LIST] =
+ "\r\nKey List:\r\n",
+ [SHELL_TEXT_CMD_NOT_FOUND] =
+ "Command not Found\r\n",
+ [SHELL_TEXT_POINT_CANNOT_MODIFY] =
+ "can't set pointer\r\n",
+ [SHELL_TEXT_VAR_READ_ONLY_CANNOT_MODIFY] =
+ "can't set read only var\r\n",
+ [SHELL_TEXT_NOT_VAR] =
+ " is not a var\r\n",
+ [SHELL_TEXT_VAR_NOT_FOUND] =
+ "Var not Fount\r\n",
+ [SHELL_TEXT_HELP_HEADER] =
+ "command help of ",
+ [SHELL_TEXT_PASSWORD_HINT] =
+ "Please input password:",
+ [SHELL_TEXT_PASSWORD_ERROR] =
+ "\r\npassword error\r\n",
+ [SHELL_TEXT_CLEAR_CONSOLE] =
+ "\033[2J\033[1H",
+ [SHELL_TEXT_CLEAR_LINE] =
+ "\033[2K\r",
+ [SHELL_TEXT_TYPE_CMD] =
+ "CMD ",
+ [SHELL_TEXT_TYPE_VAR] =
+ "VAR ",
+ [SHELL_TEXT_TYPE_USER] =
+ "USER",
+ [SHELL_TEXT_TYPE_KEY] =
+ "KEY ",
+ [SHELL_TEXT_TYPE_NONE] =
+ "NONE",
+#if SHELL_EXEC_UNDEF_FUNC == 1
+ [SHELL_TEXT_PARAM_ERROR] =
+ "Parameter error\r\n",
+#endif
+};
+
+
+unsigned char pairedChars[][2] = {
+ {'\"', '\"'},
+ // {'[', ']'},
+ // {'(', ')'},
+ // {'{', '}'},
+ // {'<', '>'},
+ // {'\'', '\''},
+ // {'`', '`'},
+};
+
+
+/**
+ * @brief shell瀵硅薄琛
+ */
+static Shell *shellList[SHELL_MAX_NUMBER] = {NULL};
+
+
+static void shellAdd(Shell *shell);
+static void shellWritePrompt(Shell *shell, unsigned char newline);
+static void shellWriteReturnValue(Shell *shell, int value);
+static int shellShowVar(Shell *shell, ShellCommand *command);
+void shellSetUser(Shell *shell, const ShellCommand *user);
+ShellCommand* shellSeekCommand(Shell *shell,
+ const char *cmd,
+ ShellCommand *base,
+ unsigned short compareLength);
+static void shellWriteCommandHelp(Shell *shell, char *cmd);
+
+/**
+ * @brief shell 鍒濆鍖
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellInit(Shell *shell, char *buffer, unsigned short size)
+{
+ shell->parser.length = 0;
+ shell->parser.cursor = 0;
+ shell->info.user = NULL;
+ shell->status.isChecked = 1;
+
+ shell->parser.buffer = buffer;
+ shell->parser.bufferSize = size / (SHELL_HISTORY_MAX_NUMBER + 1);
+
+#if SHELL_HISTORY_MAX_NUMBER > 0
+ shell->history.offset = 0;
+ shell->history.number = 0;
+ shell->history.record = 0;
+ for (short i = 0; i < SHELL_HISTORY_MAX_NUMBER; i++)
+ {
+ shell->history.item[i] = buffer + shell->parser.bufferSize * (i + 1);
+ }
+#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */
+
+#if SHELL_USING_CMD_EXPORT == 1
+ #if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000)
+ shell->commandList.base = (ShellCommand *)(&shellCommand$$Base);
+ shell->commandList.count = ((unsigned int)(&shellCommand$$Limit)
+ - (unsigned int)(&shellCommand$$Base))
+ / sizeof(ShellCommand);
+
+ #elif defined(__ICCARM__) || defined(__ICCRX__)
+ shell->commandList.base = (ShellCommand *)(__section_begin("shellCommand"));
+ shell->commandList.count = ((unsigned int)(__section_end("shellCommand"))
+ - (unsigned int)(__section_begin("shellCommand")))
+ / sizeof(ShellCommand);
+ #elif defined(__GNUC__)
+ shell->commandList.base = (ShellCommand *)(&_shell_command_start);
+ shell->commandList.count = ((unsigned int)(&_shell_command_end)
+ - (unsigned int)(&_shell_command_start))
+ / sizeof(ShellCommand);
+ #else
+ #error not supported compiler, please use command table mode
+ #endif
+#else
+ shell->commandList.base = (ShellCommand *)shellCommandList;
+ shell->commandList.count = shellCommandCount;
+#endif
+
+ shellAdd(shell);
+
+ shellSetUser(shell, shellSeekCommand(shell,
+ SHELL_DEFAULT_USER,
+ shell->commandList.base,
+ 0));
+ shellWritePrompt(shell, 1);
+}
+
+
+/**
+ * @brief 娣诲姞shell
+ *
+ * @param shell shell瀵硅薄
+ */
+static void shellAdd(Shell *shell)
+{
+ for (short i = 0; i < SHELL_MAX_NUMBER; i++)
+ {
+ if (shellList[i] == NULL)
+ {
+ shellList[i] = shell;
+ return;
+ }
+ }
+}
+
+/**
+ * @brief 绉婚櫎shell
+ *
+ * @param shell shell瀵硅薄
+ *
+ */
+void shellRemove(Shell *shell)
+{
+ for (short i = 0; i < SHELL_MAX_NUMBER; i++)
+ {
+ if (shellList[i] == shell)
+ {
+ shellList[i] = NULL;
+ return;
+ }
+ }
+}
+
+/**
+ * @brief 鑾峰彇褰撳墠娲诲姩shell
+ *
+ * @return Shell* 褰撳墠娲诲姩shell瀵硅薄
+ */
+Shell* shellGetCurrent(void)
+{
+ for (short i = 0; i < SHELL_MAX_NUMBER; i++)
+ {
+ if (shellList[i] && shellList[i]->status.isActive)
+ {
+ return shellList[i];
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ * @brief shell鍐欏瓧绗
+ *
+ * @param shell shell瀵硅薄
+ * @param data 瀛楃鏁版嵁
+ */
+static void shellWriteByte(Shell *shell, char data)
+{
+// shell->write(&data, 1);
+ if (shell->status.isChecked)
+ {
+ shell->write(&data, 1);
+ }
+ else {
+ char tmp = '*';
+ shell->write(&tmp, 1);
+ }
+}
+
+
+/**
+ * @brief shell 鍐欏瓧绗︿覆
+ *
+ * @param shell shell瀵硅薄
+ * @param string 瀛楃涓叉暟鎹
+ *
+ * @return unsigned short 鍐欏叆瀛楃鐨勬暟閲
+ */
+unsigned short shellWriteString(Shell *shell, const char *string)
+{
+ unsigned short count = 0;
+ const char *p = string;
+ SHELL_ASSERT(shell->write, return 0);
+ while(*p++)
+ {
+ count ++;
+ }
+ return shell->write((char *)string, count);
+}
+
+
+/**
+ * @brief shell 鍐欏懡浠ゆ弿杩板瓧绗︿覆
+ *
+ * @param shell shell瀵硅薄
+ * @param string 瀛楃涓叉暟鎹
+ *
+ * @return unsigned short 鍐欏叆瀛楃鐨勬暟閲
+ */
+static unsigned short shellWriteCommandDesc(Shell *shell, const char *string)
+{
+ unsigned short count = 0;
+ const char *p = string;
+ SHELL_ASSERT(shell->write, return 0);
+ while (*p && *p != '\r' && *p != '\n')
+ {
+ p++;
+ count++;
+ }
+
+ if (count > 36)
+ {
+ shell->write((char *)string, 36);
+ shell->write("...", 3);
+ }
+ else
+ {
+ shell->write((char *)string, count);
+ }
+ return count > 36 ? 36 : 39;
+}
+
+
+/**
+ * @brief shell鍐欏懡浠ゆ彁绀虹
+ *
+ * @param shell shell瀵硅薄
+ * @param newline 鏂拌
+ *
+ */
+static void shellWritePrompt(Shell *shell, unsigned char newline)
+{
+ if (shell->status.isChecked)
+ {
+ if (newline)
+ {
+ shellWriteString(shell, "\r\n");
+ }
+ shellWriteString(shell, shell->info.user->data.user.name);
+ shellWriteString(shell, ":");
+ shellWriteString(shell, shell->info.path ? shell->info.path : "/");
+ shellWriteString(shell, "$ ");
+ }
+ else
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_PASSWORD_HINT]);
+ }
+}
+
+
+#if SHELL_PRINT_BUFFER > 0
+/**
+ * @brief shell鏍煎紡鍖栬緭鍑
+ *
+ * @param shell shell瀵硅薄
+ * @param fmt 鏍煎紡鍖栧瓧绗︿覆
+ * @param ... 鍙傛暟
+ */
+void shellPrint(Shell *shell, const char *fmt, ...)
+{
+ char buffer[SHELL_PRINT_BUFFER];
+ va_list vargs;
+ int len;
+
+ SHELL_ASSERT(shell, return);
+
+ va_start(vargs, fmt);
+ len = vsnprintf(buffer, SHELL_PRINT_BUFFER, fmt, vargs);
+ va_end(vargs);
+ if (len > SHELL_PRINT_BUFFER)
+ {
+ len = SHELL_PRINT_BUFFER;
+ }
+ shell->write(buffer, len);
+}
+#endif
+
+
+#if SHELL_SCAN_BUFFER > 0
+/**
+ * @brief shell鏍煎紡鍖栬緭鍏
+ *
+ * @param shell shell瀵硅薄
+ * @param fmt 鏍煎紡鍖栧瓧绗︿覆
+ * @param ... 鍙傛暟
+ */
+void shellScan(Shell *shell, char *fmt, ...)
+{
+ char buffer[SHELL_SCAN_BUFFER];
+ va_list vargs;
+ short index = 0;
+
+ SHELL_ASSERT(shell, return);
+
+ if (shell->read)
+ {
+ do {
+ if (shell->read(&buffer[index], 1) == 1)
+ {
+ shell->write(&buffer[index], 1);
+ index++;
+ }
+ } while (buffer[index -1] != '\r' && buffer[index -1] != '\n' && index < SHELL_SCAN_BUFFER);
+ shellWriteString(shell, "\r\n");
+ buffer[index] = '\0';
+ }
+
+ va_start(vargs, fmt);
+ vsscanf(buffer, fmt, vargs);
+ va_end(vargs);
+}
+#endif
+
+
+/**
+ * @brief shell 妫鏌ュ懡浠ゆ潈闄
+ *
+ * @param shell shell瀵硅薄
+ * @param command ShellCommand
+ *
+ * @return signed char 0 褰撳墠鐢ㄦ埛鍏锋湁璇ュ懡浠ゆ潈闄
+ * @return signec char -1 褰撳墠鐢ㄦ埛涓嶅叿鏈夎鍛戒护鏉冮檺
+ */
+signed char shellCheckPermission(Shell *shell, ShellCommand *command)
+{
+ return ((!command->attr.attrs.permission
+ || command->attr.attrs.type == SHELL_TYPE_USER
+ || (shell->info.user
+ && (command->attr.attrs.permission
+ & shell->info.user->attr.attrs.permission)))
+ && (shell->status.isChecked
+ || command->attr.attrs.enableUnchecked))
+ ? 0 : -1;
+}
+
+
+/**
+ * @brief int杞16杩涘埗瀛楃涓
+ *
+ * @param value 鏁板
+ * @param buffer 缂撳啿
+ *
+ * @return signed char 杞崲鍚庢湁鏁堟暟鎹暱搴
+ */
+signed char shellToHex(unsigned int value, char *buffer)
+{
+ char byte;
+ unsigned char i = 8;
+ buffer[8] = 0;
+ while (value)
+ {
+ byte = value & 0x0000000F;
+ buffer[--i] = (byte > 9) ? (byte + 87) : (byte + 48);
+ value >>= 4;
+ }
+ return 8 - i;
+}
+
+
+/**
+* @brief int杞10杩涘埗瀛楃涓
+ *
+ * @param value 鏁板
+ * @param buffer 缂撳啿
+ *
+ * @return signed char 杞崲鍚庢湁鏁堟暟鎹暱搴
+ */
+signed char shellToDec(int value, char *buffer)
+{
+ unsigned char i = 11;
+ int v = value;
+ if (value < 0)
+ {
+ v = -value;
+ }
+ buffer[11] = 0;
+ while (v)
+ {
+ buffer[--i] = v % 10 + 48;
+ v /= 10;
+ }
+ if (value < 0)
+ {
+ buffer[--i] = '-';
+ }
+ if (value == 0) {
+ buffer[--i] = '0';
+ }
+ return 11 - i;
+}
+
+
+/**
+ * @brief shell瀛楃涓插鍒
+ *
+ * @param dest 鐩爣瀛楃涓
+ * @param src 婧愬瓧绗︿覆
+ * @return unsigned short 瀛楃涓查暱搴
+ */
+static unsigned short shellStringCopy(char *dest, char* src)
+{
+ unsigned short count = 0;
+ while (*(src + count))
+ {
+ *(dest + count) = *(src + count);
+ count++;
+ }
+ *(dest + count) = 0;
+ return count;
+}
+
+
+/**
+ * @brief shell瀛楃涓叉瘮杈
+ *
+ * @param dest 鐩爣瀛楃涓
+ * @param src 婧愬瓧绗︿覆
+ * @return unsigned short 鍖归厤闀垮害
+ */
+static unsigned short shellStringCompare(char* dest, char *src)
+{
+ unsigned short match = 0;
+ unsigned short i = 0;
+
+ while (*(dest +i) && *(src + i))
+ {
+ if (*(dest + i) != *(src +i))
+ {
+ break;
+ }
+ match ++;
+ i++;
+ }
+ return match;
+}
+
+
+/**
+ * @brief shell鑾峰彇鍛戒护鍚
+ *
+ * @param command 鍛戒护
+ * @return const char* 鍛戒护鍚
+ */
+static const char* shellGetCommandName(ShellCommand *command)
+{
+ static char buffer[9];
+ for (unsigned char i = 0; i < 9; i++)
+ {
+ buffer[i] = '0';
+ }
+ if (command->attr.attrs.type <= SHELL_TYPE_CMD_FUNC)
+ {
+ return command->data.cmd.name;
+ }
+ else if (command->attr.attrs.type <= SHELL_TYPE_VAR_NODE)
+ {
+ return command->data.var.name;
+ }
+ else if (command->attr.attrs.type <= SHELL_TYPE_USER)
+ {
+ return command->data.user.name;
+ }
+#if SHELL_USING_FUNC_SIGNATURE == 1
+ else if (command->attr.attrs.type == SHELL_TYPE_PARAM_PARSER)
+ {
+ return command->data.paramParser.type;
+ }
+#endif
+ else
+ {
+ shellToHex(command->data.key.value, buffer);
+ return buffer;
+ }
+}
+
+
+/**
+ * @brief shell鑾峰彇鍛戒护鎻忚堪
+ *
+ * @param command 鍛戒护
+ * @return const char* 鍛戒护鎻忚堪
+ */
+static const char* shellGetCommandDesc(ShellCommand *command)
+{
+ if (command->attr.attrs.type <= SHELL_TYPE_CMD_FUNC)
+ {
+ return command->data.cmd.desc;
+ }
+ else if (command->attr.attrs.type <= SHELL_TYPE_VAR_NODE)
+ {
+ return command->data.var.desc;
+ }
+ else if (command->attr.attrs.type <= SHELL_TYPE_USER)
+ {
+ return command->data.user.desc;
+ }
+ else
+ {
+ return command->data.key.desc;
+ }
+}
+
+/**
+ * @brief shell 鍒楀嚭鍛戒护鏉$洰
+ *
+ * @param shell shell瀵硅薄
+ * @param item 鍛戒护鏉$洰
+ */
+void shellListItem(Shell *shell, ShellCommand *item)
+{
+ short spaceLength;
+
+ spaceLength = 22 - shellWriteString(shell, shellGetCommandName(item));
+ spaceLength = (spaceLength > 0) ? spaceLength : 4;
+ do {
+ shellWriteByte(shell, ' ');
+ } while (--spaceLength);
+ if (item->attr.attrs.type <= SHELL_TYPE_CMD_FUNC)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_TYPE_CMD]);
+ }
+ else if (item->attr.attrs.type <= SHELL_TYPE_VAR_NODE)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_TYPE_VAR]);
+ }
+ else if (item->attr.attrs.type <= SHELL_TYPE_USER)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_TYPE_USER]);
+ }
+ else if (item->attr.attrs.type <= SHELL_TYPE_KEY)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_TYPE_KEY]);
+ }
+ else
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_TYPE_NONE]);
+ }
+#if SHELL_HELP_SHOW_PERMISSION == 1
+ shellWriteString(shell, " ");
+ for (signed char i = 7; i >= 0; i--)
+ {
+ shellWriteByte(shell, item->attr.attrs.permission & (1 << i) ? 'x' : '-');
+ }
+#endif
+ shellWriteString(shell, " ");
+ shellWriteCommandDesc(shell, shellGetCommandDesc(item));
+ shellWriteString(shell, "\r\n");
+}
+
+
+/**
+ * @brief shell鍒楀嚭鍙墽琛屽懡浠
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellListCommand(Shell *shell)
+{
+ ShellCommand *base = (ShellCommand *)shell->commandList.base;
+ shellWriteString(shell, shellText[SHELL_TEXT_CMD_LIST]);
+ for (short i = 0; i < shell->commandList.count; i++)
+ {
+ if (base[i].attr.attrs.type <= SHELL_TYPE_CMD_FUNC
+ && shellCheckPermission(shell, &base[i]) == 0)
+ {
+ shellListItem(shell, &base[i]);
+ }
+ }
+}
+
+
+/**
+ * @brief shell鍒楀嚭鍙橀噺
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellListVar(Shell *shell)
+{
+ ShellCommand *base = (ShellCommand *)shell->commandList.base;
+ shellWriteString(shell, shellText[SHELL_TEXT_VAR_LIST]);
+ for (short i = 0; i < shell->commandList.count; i++)
+ {
+ if (base[i].attr.attrs.type > SHELL_TYPE_CMD_FUNC
+ && base[i].attr.attrs.type <= SHELL_TYPE_VAR_NODE
+ && shellCheckPermission(shell, &base[i]) == 0)
+ {
+ shellListItem(shell, &base[i]);
+ }
+ }
+}
+
+
+/**
+ * @brief shell鍒楀嚭鐢ㄦ埛
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellListUser(Shell *shell)
+{
+ ShellCommand *base = (ShellCommand *)shell->commandList.base;
+ shellWriteString(shell, shellText[SHELL_TEXT_USER_LIST]);
+ for (short i = 0; i < shell->commandList.count; i++)
+ {
+ if (base[i].attr.attrs.type > SHELL_TYPE_VAR_NODE
+ && base[i].attr.attrs.type <= SHELL_TYPE_USER
+ && shellCheckPermission(shell, &base[i]) == 0)
+ {
+ shellListItem(shell, &base[i]);
+ }
+ }
+}
+
+
+/**
+ * @brief shell鍒楀嚭鎸夐敭
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellListKey(Shell *shell)
+{
+ ShellCommand *base = (ShellCommand *)shell->commandList.base;
+ shellWriteString(shell, shellText[SHELL_TEXT_KEY_LIST]);
+ for (short i = 0; i < shell->commandList.count; i++)
+ {
+ if (base[i].attr.attrs.type > SHELL_TYPE_USER
+ && base[i].attr.attrs.type <= SHELL_TYPE_KEY
+ && shellCheckPermission(shell, &base[i]) == 0)
+ {
+ shellListItem(shell, &base[i]);
+ }
+ }
+}
+
+
+/**
+ * @brief shell鍒楀嚭鎵鏈夊懡浠
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellListAll(Shell *shell)
+{
+#if SHELL_HELP_LIST_USER == 1
+ shellListUser(shell);
+#endif
+ shellListCommand(shell);
+#if SHELL_HELP_LIST_VAR == 1
+ shellListVar(shell);
+#endif
+#if SHELL_HELP_LIST_KEY == 1
+ shellListKey(shell);
+#endif
+}
+
+
+/**
+ * @brief shell鍒犻櫎鍛戒护琛屾暟鎹
+ *
+ * @param shell shell瀵硅薄
+ * @param length 鍒犻櫎闀垮害
+ */
+void shellDeleteCommandLine(Shell *shell, unsigned char length)
+{
+ while (length--)
+ {
+ shellWriteString(shell, "\b \b");
+ }
+}
+
+
+/**
+ * @brief shell 娓呯┖鍛戒护琛岃緭鍏
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellClearCommandLine(Shell *shell)
+{
+ for (short i = shell->parser.length - shell->parser.cursor; i > 0; i--)
+ {
+ shellWriteByte(shell, ' ');
+ }
+ shellDeleteCommandLine(shell, shell->parser.length);
+}
+
+
+/**
+ * @brief shell鎻掑叆涓涓瓧绗﹀埌鍏夋爣浣嶇疆
+ *
+ * @param shell shell瀵硅薄
+ * @param data 瀛楃鏁版嵁
+ */
+void shellInsertByte(Shell *shell, char data)
+{
+ /* 鍒ゆ柇杈撳叆鏁版嵁鏄惁杩囬暱 */
+ if (shell->parser.length >= shell->parser.bufferSize - 1)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_CMD_TOO_LONG]);
+ shellWritePrompt(shell, 1);
+ shellWriteString(shell, shell->parser.buffer);
+ return;
+ }
+
+ /* 鎻掑叆鏁版嵁 */
+ if (shell->parser.cursor == shell->parser.length)
+ {
+ shell->parser.buffer[shell->parser.length++] = data;
+ shell->parser.buffer[shell->parser.length] = 0;
+ shell->parser.cursor++;
+ shellWriteByte(shell, data);
+ }
+ else if (shell->parser.cursor < shell->parser.length)
+ {
+ for (short i = shell->parser.length - shell->parser.cursor; i > 0; i--)
+ {
+ shell->parser.buffer[shell->parser.cursor + i] =
+ shell->parser.buffer[shell->parser.cursor + i - 1];
+ }
+ shell->parser.buffer[shell->parser.cursor++] = data;
+ shell->parser.buffer[++shell->parser.length] = 0;
+ for (short i = shell->parser.cursor - 1; i < shell->parser.length; i++)
+ {
+ shellWriteByte(shell, shell->parser.buffer[i]);
+ }
+ for (short i = shell->parser.length - shell->parser.cursor; i > 0; i--)
+ {
+ shellWriteByte(shell, '\b');
+ }
+ }
+}
+
+
+/**
+ * @brief shell 鍒犻櫎瀛楄妭
+ *
+ * @param shell shell瀵硅薄
+ * @param direction 鍒犻櫎鏂瑰悜 {@code 1}鍒犻櫎鍏夋爣鍓嶅瓧绗 {@code -1}鍒犻櫎鍏夋爣澶勫瓧绗
+ */
+void shellDeleteByte(Shell *shell, signed char direction)
+{
+ char offset = (direction == -1) ? 1 : 0;
+
+ if ((shell->parser.cursor == 0 && direction == 1)
+ || (shell->parser.cursor == shell->parser.length && direction == -1))
+ {
+ return;
+ }
+ if (shell->parser.cursor == shell->parser.length && direction == 1)
+ {
+ shell->parser.cursor--;
+ shell->parser.length--;
+ shell->parser.buffer[shell->parser.length] = 0;
+ shellDeleteCommandLine(shell, 1);
+ }
+ else
+ {
+ for (short i = offset; i < shell->parser.length - shell->parser.cursor; i++)
+ {
+ shell->parser.buffer[shell->parser.cursor + i - 1] =
+ shell->parser.buffer[shell->parser.cursor + i];
+ }
+ shell->parser.length--;
+ if (!offset)
+ {
+ shell->parser.cursor--;
+ shellWriteByte(shell, '\b');
+ }
+ shell->parser.buffer[shell->parser.length] = 0;
+ for (short i = shell->parser.cursor; i < shell->parser.length; i++)
+ {
+ shellWriteByte(shell, shell->parser.buffer[i]);
+ }
+ shellWriteByte(shell, ' ');
+ for (short i = shell->parser.length - shell->parser.cursor + 1; i > 0; i--)
+ {
+ shellWriteByte(shell, '\b');
+ }
+ }
+}
+
+
+/**
+ * @brief shell 瑙f瀽鍙傛暟
+ *
+ * @param shell shell瀵硅薄
+ */
+static void shellParserParam(Shell *shell)
+{
+ unsigned char record = 1;
+ unsigned char pairedLeft[16] = {0};
+ unsigned char pariedCount = 0;
+
+ for (short i = 0; i < SHELL_PARAMETER_MAX_NUMBER; i++)
+ {
+ shell->parser.param[i] = NULL;
+ }
+
+ shell->parser.paramCount = 0;
+ for (unsigned short i = 0; i < shell->parser.length; i++)
+ {
+ if (pariedCount == 0) {
+ if (shell->parser.buffer[i] != ' '
+ && record == 1
+ && shell->parser.paramCount < SHELL_PARAMETER_MAX_NUMBER)
+ {
+ shell->parser.param[shell->parser.paramCount++] =
+ &(shell->parser.buffer[i]);
+ record = 0;
+ }
+ else if (shell->parser.buffer[i] == ' ' && record == 0)
+ {
+ shell->parser.buffer[i] = 0;
+ record = 1;
+ continue;
+ }
+ }
+
+ for (unsigned char j = 0; j < sizeof(pairedChars) / 2; j++)
+ {
+ if (pariedCount > 0
+ && shell->parser.buffer[i] == pairedChars[j][1]
+ && pairedLeft[pariedCount - 1] == pairedChars[j][0])
+ {
+ --pariedCount;
+ break;
+ }
+ else if (shell->parser.buffer[i] == pairedChars[j][0])
+ {
+ pairedLeft[pariedCount++] = pairedChars[j][0];
+ pariedCount &= 0x0F;
+ break;
+ }
+ }
+
+ if (shell->parser.buffer[i] == '\\'
+ && shell->parser.buffer[i + 1] != 0)
+ {
+ i++;
+ }
+ }
+}
+
+
+/**
+ * @brief shell鍘婚櫎瀛楃涓插弬鏁板ご灏剧殑鍙屽紩鍙
+ *
+ * @param shell shell瀵硅薄
+ */
+static void shellRemoveParamQuotes(Shell *shell)
+{
+ unsigned short paramLength;
+ for (unsigned short i = 0; i < shell->parser.paramCount; i++)
+ {
+ if (shell->parser.param[i][0] == '\"')
+ {
+ shell->parser.param[i][0] = 0;
+ shell->parser.param[i] = &shell->parser.param[i][1];
+ }
+ paramLength = strlen(shell->parser.param[i]);
+ if (shell->parser.param[i][paramLength - 1] == '\"')
+ {
+ shell->parser.param[i][paramLength - 1] = 0;
+ }
+ }
+}
+
+
+/**
+ * @brief shell鍖归厤鍛戒护
+ *
+ * @param shell shell瀵硅薄
+ * @param cmd 鍛戒护
+ * @param base 鍖归厤鍛戒护琛ㄥ熀鍧
+ * @param compareLength 鍖归厤瀛楃涓查暱搴
+ * @return ShellCommand* 鍖归厤鍒扮殑鍛戒护
+ */
+ShellCommand* shellSeekCommand(Shell *shell,
+ const char *cmd,
+ ShellCommand *base,
+ unsigned short compareLength)
+{
+ const char *name;
+ unsigned short count = shell->commandList.count -
+ ((int)base - (int)shell->commandList.base) / sizeof(ShellCommand);
+ for (unsigned short i = 0; i < count; i++)
+ {
+ if (base[i].attr.attrs.type == SHELL_TYPE_KEY
+ || shellCheckPermission(shell, &base[i]) != 0)
+ {
+ continue;
+ }
+ name = shellGetCommandName(&base[i]);
+ if (!compareLength)
+ {
+ if (strcmp(cmd, name) == 0)
+ {
+ return &base[i];
+ }
+ }
+ else
+ {
+ if (strncmp(cmd, name, compareLength) == 0)
+ {
+ return &base[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ * @brief shell 鑾峰彇鍙橀噺鍊
+ *
+ * @param shell shell瀵硅薄
+ * @param command 鍛戒护
+ * @return int 鍙橀噺鍊
+ */
+int shellGetVarValue(Shell *shell, ShellCommand *command)
+{
+ int value = 0;
+ switch (command->attr.attrs.type)
+ {
+ case SHELL_TYPE_VAR_INT:
+ value = *((int *)(command->data.var.value));
+ break;
+ case SHELL_TYPE_VAR_SHORT:
+ value = *((short *)(command->data.var.value));
+ break;
+ case SHELL_TYPE_VAR_CHAR:
+ value = *((char *)(command->data.var.value));
+ break;
+ case SHELL_TYPE_VAR_STRING:
+ case SHELL_TYPE_VAR_POINT:
+ value = (int)(command->data.var.value);
+ break;
+ case SHELL_TYPE_VAR_NODE:
+ value = ((ShellNodeVarAttr *)command->data.var.value)->get ?
+ ((ShellNodeVarAttr *)command->data.var.value)
+ ->get(((ShellNodeVarAttr *)command->data.var.value)->var) : 0;
+ break;
+ default:
+ break;
+ }
+ return value;
+}
+
+
+/**
+ * @brief shell璁剧疆鍙橀噺鍊
+ *
+ * @param shell shell瀵硅薄
+ * @param command 鍛戒护
+ * @param value 鍊
+ * @return int 杩斿洖鍙橀噺鍊
+ */
+int shellSetVarValue(Shell *shell, ShellCommand *command, int value)
+{
+ if (command->attr.attrs.readOnly)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_VAR_READ_ONLY_CANNOT_MODIFY]);
+ }
+ else
+ {
+ switch (command->attr.attrs.type)
+ {
+ case SHELL_TYPE_VAR_INT:
+ *((int *)(command->data.var.value)) = value;
+ break;
+ case SHELL_TYPE_VAR_SHORT:
+ *((short *)(command->data.var.value)) = value;
+ break;
+ case SHELL_TYPE_VAR_CHAR:
+ *((char *)(command->data.var.value)) = value;
+ break;
+ case SHELL_TYPE_VAR_STRING:
+ shellStringCopy(((char *)(command->data.var.value)), (char *)value);
+ break;
+ case SHELL_TYPE_VAR_POINT:
+ shellWriteString(shell, shellText[SHELL_TEXT_POINT_CANNOT_MODIFY]);
+ break;
+ case SHELL_TYPE_VAR_NODE:
+ if (((ShellNodeVarAttr *)command->data.var.value)->set)
+ {
+ if (((ShellNodeVarAttr *)command->data.var.value)->var)
+ {
+ ((ShellNodeVarAttr *)command->data.var.value)
+ ->set(((ShellNodeVarAttr *)command->data.var.value)->var, value);
+ }
+ else
+ {
+ ((ShellNodeVarAttr *)command->data.var.value)->set(value);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return shellShowVar(shell, command);
+}
+
+
+/**
+ * @brief shell鍙橀噺杈撳嚭
+ *
+ * @param shell shell瀵硅薄
+ * @param command 鍛戒护
+ * @return int 杩斿洖鍙橀噺鍊
+ */
+static int shellShowVar(Shell *shell, ShellCommand *command)
+{
+ char buffer[12] = "00000000000";
+ int value = shellGetVarValue(shell, command);
+
+ shellWriteString(shell, command->data.var.name);
+ shellWriteString(shell, " = ");
+
+ switch (command->attr.attrs.type)
+ {
+ case SHELL_TYPE_VAR_STRING:
+ shellWriteString(shell, "\"");
+ shellWriteString(shell, (char *)value);
+ shellWriteString(shell, "\"");
+ break;
+ // case SHELL_TYPE_VAR_INT:
+ // case SHELL_TYPE_VAR_SHORT:
+ // case SHELL_TYPE_VAR_CHAR:
+ // case SHELL_TYPE_VAR_POINT:
+ default:
+ shellWriteString(shell, &buffer[11 - shellToDec(value, buffer)]);
+ shellWriteString(shell, ", 0x");
+ for (short i = 0; i < 11; i++)
+ {
+ buffer[i] = '0';
+ }
+ shellToHex(value, buffer);
+ shellWriteString(shell, buffer);
+ break;
+ }
+
+ shellWriteString(shell, "\r\n");
+ return value;
+}
+
+
+/**
+ * @brief shell璁剧疆鍙橀噺
+ *
+ * @param name 鍙橀噺鍚
+ * @param value 鍙橀噺鍊
+ * @return int 杩斿洖鍙橀噺鍊
+ */
+int shellSetVar(char *name, int value)
+{
+ Shell *shell = shellGetCurrent();
+ if (shell == NULL)
+ {
+ return 0;
+ }
+ ShellCommand *command = shellSeekCommand(shell,
+ name,
+ shell->commandList.base,
+ 0);
+ if (!command)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_VAR_NOT_FOUND]);
+ return 0;
+ }
+ if (command->attr.attrs.type < SHELL_TYPE_VAR_INT
+ || command->attr.attrs.type > SHELL_TYPE_VAR_NODE)
+ {
+ shellWriteString(shell, name);
+ shellWriteString(shell, shellText[SHELL_TEXT_NOT_VAR]);
+ return 0;
+ }
+ return shellSetVarValue(shell, command, value);
+}
+SHELL_EXPORT_CMD(
+SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+setVar, shellSetVar, set var);
+
+
+/**
+ * @brief shell杩愯鍛戒护
+ *
+ * @param shell shell瀵硅薄
+ * @param command 鍛戒护
+ *
+ * @return unsigned int 鍛戒护杩斿洖鍊
+ */
+unsigned int shellRunCommand(Shell *shell, ShellCommand *command)
+{
+ int returnValue = 0;
+ shell->status.isActive = 1;
+ if (command->attr.attrs.type == SHELL_TYPE_CMD_MAIN)
+ {
+ shellRemoveParamQuotes(shell);
+ returnValue = command->data.cmd.function(shell->parser.paramCount,
+ shell->parser.param);
+ if (!command->attr.attrs.disableReturn)
+ {
+ shellWriteReturnValue(shell, returnValue);
+ }
+ }
+ else if (command->attr.attrs.type == SHELL_TYPE_CMD_FUNC)
+ {
+ returnValue = shellExtRun(shell,
+ command,
+ shell->parser.paramCount,
+ shell->parser.param);
+ if (!command->attr.attrs.disableReturn)
+ {
+ shellWriteReturnValue(shell, returnValue);
+ }
+ }
+ else if (command->attr.attrs.type >= SHELL_TYPE_VAR_INT
+ && command->attr.attrs.type <= SHELL_TYPE_VAR_NODE)
+ {
+ shellShowVar(shell, command);
+ }
+ else if (command->attr.attrs.type == SHELL_TYPE_USER)
+ {
+ shellSetUser(shell, command);
+ }
+ shell->status.isActive = 0;
+
+ return returnValue;
+}
+
+
+/**
+ * @brief shell鏍¢獙瀵嗙爜
+ *
+ * @param shell shell瀵硅薄
+ */
+static void shellCheckPassword(Shell *shell)
+{
+ if (strcmp(shell->parser.buffer, shell->info.user->data.user.password) == 0)
+ {
+ shell->status.isChecked = 1;
+ #if SHELL_SHOW_INFO == 1
+ shellWriteString(shell, shellText[SHELL_TEXT_INFO]);
+ #endif
+ }
+ else
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_PASSWORD_ERROR]);
+ }
+ shell->parser.length = 0;
+ shell->parser.cursor = 0;
+}
+
+
+/**
+ * @brief shell璁剧疆鐢ㄦ埛
+ *
+ * @param shell shell瀵硅薄
+ * @param user 鐢ㄦ埛
+ */
+void shellSetUser(Shell *shell, const ShellCommand *user)
+{
+ shell->info.user = user;
+ shell->status.isChecked =
+ ((user->data.user.password && strlen(user->data.user.password) != 0)
+ && (shell->parser.paramCount < 2
+ || strcmp(user->data.user.password, shell->parser.param[1]) != 0))
+ ? 0 : 1;
+
+#if SHELL_CLS_WHEN_LOGIN == 1
+ shellWriteString(shell, shellText[SHELL_TEXT_CLEAR_CONSOLE]);
+#endif
+#if SHELL_SHOW_INFO == 1
+ if (shell->status.isChecked)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_INFO]);
+ }
+#endif
+}
+
+
+/**
+ * @brief shell鍐欒繑鍥炲
+ *
+ * @param shell shell瀵硅薄
+ * @param value 杩斿洖鍊
+ */
+void shellWriteReturnValue(Shell *shell, int value)
+{
+ char buffer[12] = "00000000000";
+ shellWriteString(shell, "Return: ");
+ shellWriteString(shell, &buffer[11 - shellToDec(value, buffer)]);
+ shellWriteString(shell, ", 0x");
+ for (short i = 0; i < 11; i++)
+ {
+ buffer[i] = '0';
+ }
+ shellToHex(value, buffer);
+ shellWriteString(shell, buffer);
+ shellWriteString(shell, "\r\n");
+#if SHELL_KEEP_RETURN_VALUE == 1
+ shell->info.retVal = value;
+#endif
+}
+
+
+#if SHELL_HISTORY_MAX_NUMBER > 0
+/**
+ * @brief shell鍘嗗彶璁板綍娣诲姞
+ *
+ * @param shell shell瀵硅薄
+ */
+static void shellHistoryAdd(Shell *shell)
+{
+ shell->history.offset = 0;
+ if (shell->history.number > 0
+ && strcmp(shell->history.item[(shell->history.record == 0 ?
+ SHELL_HISTORY_MAX_NUMBER : shell->history.record) - 1],
+ shell->parser.buffer) == 0)
+ {
+ return;
+ }
+ if (shellStringCopy(shell->history.item[shell->history.record],
+ shell->parser.buffer) != 0)
+ {
+ shell->history.record++;
+ }
+ if (++shell->history.number > SHELL_HISTORY_MAX_NUMBER)
+ {
+ shell->history.number = SHELL_HISTORY_MAX_NUMBER;
+ }
+ if (shell->history.record >= SHELL_HISTORY_MAX_NUMBER)
+ {
+ shell->history.record = 0;
+ }
+}
+
+
+/**
+ * @brief shell鍘嗗彶璁板綍鏌ユ壘
+ *
+ * @param shell shell瀵硅薄
+ * @param dir 鏂瑰悜 {@code <0}寰涓婃煡鎵 {@code >0}寰涓嬫煡鎵
+ */
+static void shellHistory(Shell *shell, signed char dir)
+{
+ if (dir > 0)
+ {
+ if (shell->history.offset-- <=
+ -((shell->history.number > shell->history.record) ?
+ shell->history.number : shell->history.record))
+ {
+ shell->history.offset = -((shell->history.number > shell->history.record)
+ ? shell->history.number : shell->history.record);
+ }
+ }
+ else if (dir < 0)
+ {
+ if (++shell->history.offset > 0)
+ {
+ shell->history.offset = 0;
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+ shellClearCommandLine(shell);
+ if (shell->history.offset == 0)
+ {
+ shell->parser.cursor = shell->parser.length = 0;
+ }
+ else
+ {
+ if ((shell->parser.length = shellStringCopy(shell->parser.buffer,
+ shell->history.item[(shell->history.record + SHELL_HISTORY_MAX_NUMBER
+ + shell->history.offset) % SHELL_HISTORY_MAX_NUMBER])) == 0)
+ {
+ return;
+ }
+ shell->parser.cursor = shell->parser.length;
+ shellWriteString(shell, shell->parser.buffer);
+ }
+
+}
+#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */
+
+
+/**
+ * @brief shell 甯歌杈撳叆
+ *
+ * @param shell shell 瀵硅薄
+ * @param data 杈撳叆瀛楃
+ */
+void shellNormalInput(Shell *shell, char data)
+{
+ shell->status.tabFlag_t = 0;
+ shellInsertByte(shell, data);
+}
+
+
+/**
+ * @brief shell杩愯鍛戒护
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellExec(Shell *shell)
+{
+
+ if (shell->parser.length == 0)
+ {
+ return;
+ }
+
+ shell->parser.buffer[shell->parser.length] = 0;
+
+ if (shell->status.isChecked)
+ {
+ #if SHELL_HISTORY_MAX_NUMBER > 0
+ shellHistoryAdd(shell);
+ #endif /** SHELL_HISTORY_MAX_NUMBER > 0 */
+ shellParserParam(shell);
+ shell->parser.length = shell->parser.cursor = 0;
+ if (shell->parser.paramCount == 0)
+ {
+ return;
+ }
+ shellWriteString(shell, "\r\n");
+
+ ShellCommand *command = shellSeekCommand(shell,
+ shell->parser.param[0],
+ shell->commandList.base,
+ 0);
+ if (command != NULL)
+ {
+ shellRunCommand(shell, command);
+ }
+ else
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_CMD_NOT_FOUND]);
+ }
+ }
+ else
+ {
+ shellCheckPassword(shell);
+ }
+}
+
+
+#if SHELL_HISTORY_MAX_NUMBER > 0
+/**
+ * @brief shell涓婃柟鍚戦敭杈撳叆
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellUp(Shell *shell)
+{
+ shellHistory(shell, 1);
+}
+SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x1B5B4100, shellUp, up);
+
+
+/**
+ * @brief shell涓嬫柟鍚戦敭杈撳叆
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellDown(Shell *shell)
+{
+ shellHistory(shell, -1);
+}
+SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x1B5B4200, shellDown, down);
+#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */
+
+
+/**
+ * @brief shell鍙虫柟鍚戦敭杈撳叆
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellRight(Shell *shell)
+{
+ if (shell->parser.cursor < shell->parser.length)
+ {
+ shellWriteByte(shell, shell->parser.buffer[shell->parser.cursor++]);
+ }
+}
+SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+0x1B5B4300, shellRight, right);
+
+
+/**
+ * @brief shell宸︽柟鍚戦敭杈撳叆
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellLeft(Shell *shell)
+{
+ if (shell->parser.cursor > 0)
+ {
+ shellWriteByte(shell, '\b');
+ shell->parser.cursor--;
+ }
+}
+SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+0x1B5B4400, shellLeft, left);
+
+
+/**
+ * @brief shell Tab鎸夐敭澶勭悊
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellTab(Shell *shell)
+{
+ unsigned short maxMatch = shell->parser.bufferSize;
+ unsigned short lastMatchIndex = 0;
+ unsigned short matchNum = 0;
+ unsigned short length;
+
+ if (shell->parser.length == 0)
+ {
+ shellListAll(shell);
+ shellWritePrompt(shell, 1);
+ }
+ else if (shell->parser.length > 0)
+ {
+ shell->parser.buffer[shell->parser.length] = 0;
+ ShellCommand *base = (ShellCommand *)shell->commandList.base;
+ for (short i = 0; i < shell->commandList.count; i++)
+ {
+ if (shellCheckPermission(shell, &base[i]) == 0
+ && shellStringCompare(shell->parser.buffer,
+ (char *)shellGetCommandName(&base[i]))
+ == shell->parser.length)
+ {
+ if (matchNum != 0)
+ {
+ if (matchNum == 1)
+ {
+ shellWriteString(shell, "\r\n");
+ }
+ shellListItem(shell, &base[lastMatchIndex]);
+ length =
+ shellStringCompare((char *)shellGetCommandName(&base[lastMatchIndex]),
+ (char *)shellGetCommandName(&base[i]));
+ maxMatch = (maxMatch > length) ? length : maxMatch;
+ }
+ lastMatchIndex = i;
+ matchNum++;
+ }
+ }
+ if (matchNum == 0)
+ {
+ return;
+ }
+ if (matchNum == 1)
+ {
+ shellClearCommandLine(shell);
+ }
+ if (matchNum != 0)
+ {
+ shell->parser.length =
+ shellStringCopy(shell->parser.buffer,
+ (char *)shellGetCommandName(&base[lastMatchIndex]));
+ }
+ if (matchNum > 1)
+ {
+ shellListItem(shell, &base[lastMatchIndex]);
+ shellWritePrompt(shell, 1);
+ shell->parser.length = maxMatch;
+ }
+ shell->parser.buffer[shell->parser.length] = 0;
+ shell->parser.cursor = shell->parser.length;
+ shellWriteString(shell, shell->parser.buffer);
+ }
+
+ if (SHELL_GET_TICK())
+ {
+ if (matchNum == 1
+ && shell->status.tabFlag_t
+ && SHELL_GET_TICK() - shell->info.activeTime < SHELL_DOUBLE_CLICK_TIME)
+ {
+ #if SHELL_QUICK_HELP == 1
+ shellWriteString(shell, "\r\n");
+ shellWriteCommandHelp(shell, shell->parser.buffer);
+ shellWritePrompt(shell, 1);
+ shellWriteString(shell, shell->parser.buffer);
+ #else
+ shellClearCommandLine(shell);
+ for (short i = shell->parser.length; i >= 0; i--)
+ {
+ shell->parser.buffer[i + 5] = shell->parser.buffer[i];
+ }
+ shellStringCopy(shell->parser.buffer, "help");
+ shell->parser.buffer[4] = ' ';
+ shell->parser.length += 5;
+ shell->parser.cursor = shell->parser.length;
+ shellWriteString(shell, shell->parser.buffer);
+ #endif
+ }
+ else
+ {
+ shell->status.tabFlag_t = 1;
+ }
+ }
+}
+SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x09000000, shellTab, tab);
+
+
+/**
+ * @brief shell 閫鏍
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellBackspace(Shell *shell)
+{
+ shellDeleteByte(shell, 1);
+}
+SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+0x08000000, shellBackspace, backspace);
+SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+0x7F000000, shellBackspace, backspace);
+
+
+/**
+ * @brief shell 鍒犻櫎
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellDelete(Shell *shell)
+{
+ shellDeleteByte(shell, -1);
+}
+SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+0x1B5B337E, shellDelete, delete);
+
+
+/**
+ * @brief shell 鍥炶溅澶勭悊
+ *
+ * @param shell shell瀵硅薄
+ */
+void shellEnter(Shell *shell)
+{
+ shellExec(shell);
+ shellWritePrompt(shell, 1);
+}
+#if SHELL_ENTER_LF == 1
+SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+0x0A000000, shellEnter, enter);
+#endif
+#if SHELL_ENTER_CR == 1
+SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+0x0D000000, shellEnter, enter);
+#endif
+#if SHELL_ENTER_CRLF == 1
+SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+0x0D0A0000, shellEnter, enter);
+#endif
+
+/**
+ * @brief shell 鍐欏懡浠ゅ府鍔╀俊鎭
+ *
+ * @param shell shell瀵硅薄
+ * @param cmd 鍛戒护瀛楃涓
+ */
+static void shellWriteCommandHelp(Shell *shell, char *cmd)
+{
+ ShellCommand *command = shellSeekCommand(shell,
+ cmd,
+ shell->commandList.base,
+ 0);
+ if (command)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_HELP_HEADER]);
+ shellWriteString(shell, shellGetCommandName(command));
+ shellWriteString(shell, "\r\n");
+ shellWriteString(shell, shellGetCommandDesc(command));
+ shellWriteString(shell, "\r\n");
+ }
+ else
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_CMD_NOT_FOUND]);
+ }
+}
+
+/**
+ * @brief shell help
+ *
+ * @param argc 鍙傛暟涓暟
+ * @param argv 鍙傛暟
+ */
+void shellHelp(int argc, char *argv[])
+{
+ Shell *shell = shellGetCurrent();
+ SHELL_ASSERT(shell, return);
+ if (argc == 1)
+ {
+ shellListAll(shell);
+ }
+ else if (argc > 1)
+ {
+ shellWriteCommandHelp(shell, argv[1]);
+ }
+}
+SHELL_EXPORT_CMD(
+SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,
+help, shellHelp, show command info\r\nhelp [cmd]);
+
+/**
+ * @brief shell 杈撳叆澶勭悊
+ *
+ * @param shell shell瀵硅薄
+ * @param data 杈撳叆鏁版嵁
+ */
+void shellHandler(Shell *shell, char data)
+{
+ SHELL_ASSERT(data, return);
+ SHELL_LOCK(shell);
+
+#if SHELL_LOCK_TIMEOUT > 0
+ if (shell->info.user->data.user.password
+ && strlen(shell->info.user->data.user.password) != 0
+ && SHELL_GET_TICK())
+ {
+ if (SHELL_GET_TICK() - shell->info.activeTime > SHELL_LOCK_TIMEOUT)
+ {
+ shell->status.isChecked = 0;
+ }
+ }
+#endif
+
+ /* 鏍规嵁璁板綍鐨勬寜閿敭鍊艰绠楀綋鍓嶅瓧鑺傚湪鎸夐敭閿间腑鐨勫亸绉 */
+ char keyByteOffset = 24;
+ int keyFilter = 0x00000000;
+ if ((shell->parser.keyValue & 0x0000FF00) != 0x00000000)
+ {
+ keyByteOffset = 0;
+ keyFilter = 0xFFFFFF00;
+ }
+ else if ((shell->parser.keyValue & 0x00FF0000) != 0x00000000)
+ {
+ keyByteOffset = 8;
+ keyFilter = 0xFFFF0000;
+ }
+ else if ((shell->parser.keyValue & 0xFF000000) != 0x00000000)
+ {
+ keyByteOffset = 16;
+ keyFilter = 0xFF000000;
+ }
+
+ /* 閬嶅巻ShellCommand鍒楄〃锛屽皾璇曡繘琛屾寜閿敭鍊煎尮閰 */
+ ShellCommand *base = (ShellCommand *)shell->commandList.base;
+ for (short i = 0; i < shell->commandList.count; i++)
+ {
+ /* 鍒ゆ柇鏄惁鏄寜閿畾涔夊苟楠岃瘉鏉冮檺 */
+ if (base[i].attr.attrs.type == SHELL_TYPE_KEY
+ && shellCheckPermission(shell, &(base[i])) == 0)
+ {
+ /* 瀵硅緭鍏ョ殑瀛楄妭鍚屾寜閿敭鍊艰繘琛屽尮閰 */
+ if ((base[i].data.key.value & keyFilter) == shell->parser.keyValue
+ && (base[i].data.key.value & (0xFF << keyByteOffset))
+ == (data << keyByteOffset))
+ {
+ shell->parser.keyValue |= data << keyByteOffset;
+ data = 0x00;
+ if (keyByteOffset == 0
+ || (base[i].data.key.value & (0xFF << (keyByteOffset - 8)))
+ == 0x00000000)
+ {
+ if (base[i].data.key.function)
+ {
+ base[i].data.key.function(shell);
+ }
+ shell->parser.keyValue = 0x00000000;
+ break;
+ }
+ }
+ }
+ }
+
+ if (data != 0x00)
+ {
+ shell->parser.keyValue = 0x00000000;
+ shellNormalInput(shell, data);
+ }
+
+ if (SHELL_GET_TICK())
+ {
+ shell->info.activeTime = SHELL_GET_TICK();
+ }
+ SHELL_UNLOCK(shell);
+}
+
+
+#if SHELL_SUPPORT_END_LINE == 1
+void shellWriteEndLine(Shell *shell, char *buffer, int len)
+{
+ SHELL_LOCK(shell);
+ if (!shell->status.isActive)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_CLEAR_LINE]);
+ }
+ shell->write(buffer, len);
+
+ if (!shell->status.isActive)
+ {
+ shellWritePrompt(shell, 0);
+ if (shell->parser.length > 0)
+ {
+ shellWriteString(shell, shell->parser.buffer);
+ for (short i = 0; i < shell->parser.length - shell->parser.cursor; i++)
+ {
+ shellWriteByte(shell, '\b');
+ }
+ }
+ }
+ SHELL_UNLOCK(shell);
+}
+#endif /** SHELL_SUPPORT_END_LINE == 1 */
+
+
+/**
+ * @brief shell 浠诲姟
+ *
+ * @param param 鍙傛暟(shell瀵硅薄)
+ *
+ */
+void shellTask(void *param)
+{
+ Shell *shell = (Shell *)param;
+ char data;
+#if SHELL_TASK_WHILE == 1
+ while(1)
+ {
+#endif
+ if (shell->read && shell->read(&data, 1) == 1)
+ {
+ shellHandler(shell, data);
+ }
+#if SHELL_TASK_WHILE == 1
+ }
+#endif
+}
+
+
+/**
+ * @brief shell 杈撳嚭鐢ㄦ埛鍒楄〃(shell璋冪敤)
+ */
+void shellUsers(void)
+{
+ Shell *shell = shellGetCurrent();
+ if (shell)
+ {
+ shellListUser(shell);
+ }
+}
+SHELL_EXPORT_CMD(
+SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+users, shellUsers, list all user);
+
+
+/**
+ * @brief shell 杈撳嚭鍛戒护鍒楄〃(shell璋冪敤)
+ */
+void shellCmds(void)
+{
+ Shell *shell = shellGetCurrent();
+ if (shell)
+ {
+ shellListCommand(shell);
+ }
+}
+SHELL_EXPORT_CMD(
+SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+cmds, shellCmds, list all cmd);
+
+
+/**
+ * @brief shell 杈撳嚭鍙橀噺鍒楄〃(shell璋冪敤)
+ */
+void shellVars(void)
+{
+ Shell *shell = shellGetCurrent();
+ if (shell)
+ {
+ shellListVar(shell);
+ }
+}
+SHELL_EXPORT_CMD(
+SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+vars, shellVars, list all var);
+
+
+/**
+ * @brief shell 杈撳嚭鎸夐敭鍒楄〃(shell璋冪敤)
+ */
+void shellKeys(void)
+{
+ Shell *shell = shellGetCurrent();
+ if (shell)
+ {
+ shellListKey(shell);
+ }
+}
+SHELL_EXPORT_CMD(
+SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+keys, shellKeys, list all key);
+
+
+/**
+ * @brief shell 娓呯┖鎺у埗鍙(shell璋冪敤)
+ */
+void shellClear(void)
+{
+ Shell *shell = shellGetCurrent();
+ if (shell)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_CLEAR_CONSOLE]);
+ }
+}
+SHELL_EXPORT_CMD(
+SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+clear, shellClear, clear console);
+
+
+/**
+ * @brief shell鎵ц鍛戒护
+ *
+ * @param shell shell瀵硅薄
+ * @param cmd 鍛戒护瀛楃涓
+ * @return int 杩斿洖鍊
+ */
+int shellRun(Shell *shell, const char *cmd)
+{
+ SHELL_ASSERT(shell && cmd, return -1);
+ char active = shell->status.isActive;
+ if (strlen(cmd) > shell->parser.bufferSize - 1)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_CMD_TOO_LONG]);
+ return -1;
+ }
+ else
+ {
+ shell->parser.length = shellStringCopy(shell->parser.buffer, (char *)cmd);
+ shellExec(shell);
+ shell->status.isActive = active;
+ return 0;
+ }
+}
+
+
+#if SHELL_EXEC_UNDEF_FUNC == 1
+/**
+ * @brief shell鎵ц鏈畾涔夊嚱鏁
+ *
+ * @param argc 鍙傛暟涓暟
+ * @param argv 鍙傛暟
+ * @return int 杩斿洖鍊
+ */
+int shellExecute(int argc, char *argv[])
+{
+ Shell *shell = shellGetCurrent();
+ if (shell && argc >= 2)
+ {
+ unsigned result;
+ if (shellExtParsePara(shell, argv[1], NULL, &result) != 0)
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_PARAM_ERROR]);
+ return -1;
+ }
+ int (*func)() = (int (*)())result;
+ ShellCommand command = {
+ .attr.value = SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)
+ |SHELL_CMD_DISABLE_RETURN,
+ .data.cmd.function = func,
+ };
+ return shellExtRun(shell, &command, argc - 1, &argv[1]);
+ }
+ else
+ {
+ shellWriteString(shell, shellText[SHELL_TEXT_PARAM_ERROR]);
+ return -1;
+ }
+}
+SHELL_EXPORT_CMD(
+SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,
+exec, shellExecute, execute function undefined);
+#endif
+
+#if SHELL_KEEP_RETURN_VALUE == 1
+/**
+ * @brief shell杩斿洖鍊艰幏鍙
+ * 鑾峰彇涓婁竴娆℃墽琛岀殑鍛戒护鐨勮繑鍥炲
+ *
+ * @return int 杩斿洖鍊
+ */
+static int shellRetValGet()
+{
+ Shell *shell = shellGetCurrent();
+ return shell ? shell->info.retVal : 0;
+}
+static ShellNodeVarAttr shellRetVal = {
+ .get = shellRetValGet
+};
+SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_NODE)|SHELL_CMD_READ_ONLY,
+RETVAL, &shellRetVal, return value of last command);
+#endif /** SHELL_KEEP_RETURN_VALUE == 1 */
diff --git a/common/letter-shell-master/src/shell.h b/common/letter-shell-master/src/shell.h
new file mode 100644
index 0000000..06e8533
--- /dev/null
+++ b/common/letter-shell-master/src/shell.h
@@ -0,0 +1,557 @@
+/**
+ * @file shell.h
+ * @author Letter (NevermindZZT@gmail.com)
+ * @brief letter shell
+ * @version 3.0.0
+ * @date 2019-12-30
+ *
+ * @copyright (c) 2020 Letter
+ *
+ */
+
+#ifndef __SHELL_H__
+#define __SHELL_H__
+
+#include "shell_cfg.h"
+
+#define SHELL_VERSION "3.2.1" /**< 鐗堟湰鍙 */
+
+
+/**
+ * @brief shell 鏂█
+ *
+ * @param expr 琛ㄨ揪寮
+ * @param action 鏂█澶辫触鎿嶄綔
+ */
+#define SHELL_ASSERT(expr, action) \
+ if (!(expr)) { \
+ action; \
+ }
+
+#if SHELL_USING_LOCK == 1
+#define SHELL_LOCK(shell) shell->lock(shell)
+#define SHELL_UNLOCK(shell) shell->unlock(shell)
+#else
+#define SHELL_LOCK(shell)
+#define SHELL_UNLOCK(shell)
+#endif /** SHELL_USING_LOCK == 1 */
+/**
+ * @brief shell 鍛戒护鏉冮檺
+ *
+ * @param permission 鏉冮檺绾у埆
+ */
+#define SHELL_CMD_PERMISSION(permission) \
+ (permission & 0x000000FF)
+
+/**
+ * @brief shell 鍛戒护绫诲瀷
+ *
+ * @param type 绫诲瀷
+ */
+#define SHELL_CMD_TYPE(type) \
+ ((type & 0x0000000F) << 8)
+
+/**
+ * @brief 浣胯兘鍛戒护鍦ㄦ湭鏍¢獙瀵嗙爜鐨勬儏鍐典笅浣跨敤
+ */
+#define SHELL_CMD_ENABLE_UNCHECKED \
+ (1 << 12)
+
+/**
+ * @brief 绂佺敤杩斿洖鍊兼墦鍗
+ */
+#define SHELL_CMD_DISABLE_RETURN \
+ (1 << 13)
+
+/**
+ * @brief 鍙灞炴(浠呭鍙橀噺鐢熸晥)
+ */
+#define SHELL_CMD_READ_ONLY \
+ (1 << 14)
+
+/**
+ * @brief 鍛戒护鍙傛暟鏁伴噺
+ */
+#define SHELL_CMD_PARAM_NUM(num) \
+ ((num & 0x0000000F)) << 16
+
+#ifndef SHELL_SECTION
+ #if defined(__CC_ARM) || defined(__CLANG_ARM)
+ #define SHELL_SECTION(x) __attribute__((section(x), aligned(1)))
+ #elif defined (__IAR_SYSTEMS_ICC__)
+ #define SHELL_SECTION(x) @ x
+ #elif defined(__GNUC__)
+ #define SHELL_SECTION(x) __attribute__((section(x), aligned(1)))
+ #else
+ #define SHELL_SECTION(x)
+ #endif
+#endif
+
+#ifndef SHELL_USED
+ #if defined(__CC_ARM) || defined(__CLANG_ARM)
+ #define SHELL_USED __attribute__((used))
+ #elif defined (__IAR_SYSTEMS_ICC__)
+ #define SHELL_USED __root
+ #elif defined(__GNUC__)
+ #define SHELL_USED __attribute__((used))
+ #else
+ #define SHELL_USED
+ #endif
+#endif
+
+/**
+ * @brief shell float鍨嬪弬鏁拌浆鎹
+ */
+#define SHELL_PARAM_FLOAT(x) (*(float *)(&x))
+
+/**
+ * @brief shell 浠g悊鍑芥暟鍚
+ */
+#define SHELL_AGENCY_FUNC_NAME(_func) agency##_func
+
+/**
+ * @brief shell浠g悊鍑芥暟瀹氫箟
+ *
+ * @param _func 琚唬鐞嗙殑鍑芥暟
+ * @param ... 浠g悊鍙傛暟
+ */
+#define SHELL_AGENCY_FUNC(_func, ...) \
+ void SHELL_AGENCY_FUNC_NAME(_func)(int p1, int p2, int p3, int p4, int p5, int p6, int p7) \
+ { _func(__VA_ARGS__); }
+
+#if SHELL_USING_CMD_EXPORT == 1
+
+ /**
+ * @brief shell 鍛戒护瀹氫箟
+ *
+ * @param _attr 鍛戒护灞炴
+ * @param _name 鍛戒护鍚
+ * @param _func 鍛戒护鍑芥暟
+ * @param _desc 鍛戒护鎻忚堪
+ * @param ... 鍏朵粬鍙傛暟
+ */
+ #define SHELL_EXPORT_CMD(_attr, _name, _func, _desc, ...) \
+ const char shellCmd##_name[] = #_name; \
+ const char shellDesc##_name[] = #_desc; \
+ SHELL_USED const ShellCommand \
+ shellCommand##_name SHELL_SECTION("shellCommand") = \
+ { \
+ .attr.value = _attr, \
+ .data.cmd.name = shellCmd##_name, \
+ .data.cmd.function = (int (*)())_func, \
+ .data.cmd.desc = shellDesc##_name, \
+ ##__VA_ARGS__ \
+ }
+
+#if SHELL_USING_FUNC_SIGNATURE == 1
+ /**
+ * @brief shell 鍛戒护瀹氫箟
+ *
+ * @param _attr 鍛戒护灞炴
+ * @param _name 鍛戒护鍚
+ * @param _func 鍛戒护鍑芥暟
+ * @param _desc 鍛戒护鎻忚堪
+ * @param _sign 鍛戒护绛惧悕
+ */
+ #define SHELL_EXPORT_CMD_SIGN(_attr, _name, _func, _desc, _sign) \
+ const char shellCmd##_name[] = #_name; \
+ const char shellDesc##_name[] = #_desc; \
+ const char shellSign##_name[] = #_sign; \
+ SHELL_USED const ShellCommand \
+ shellCommand##_name SHELL_SECTION("shellCommand") = \
+ { \
+ .attr.value = _attr, \
+ .data.cmd.name = shellCmd##_name, \
+ .data.cmd.function = (int (*)())_func, \
+ .data.cmd.desc = shellDesc##_name, \
+ .data.cmd.signature = shellSign##_name \
+ }
+#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
+
+ /**
+ * @brief shell 浠g悊鍛戒护瀹氫箟
+ *
+ * @param _attr 鍛戒护灞炴
+ * @param _name 鍛戒护鍚
+ * @param _func 鍛戒护鍑芥暟
+ * @param _desc 鍛戒护鎻忚堪
+ * @param ... 浠g悊鍙傛暟
+ */
+ #define SHELL_EXPORT_CMD_AGENCY(_attr, _name, _func, _desc, ...) \
+ SHELL_AGENCY_FUNC(_func, ##__VA_ARGS__) \
+ SHELL_EXPORT_CMD(_attr, _name, SHELL_AGENCY_FUNC_NAME(_func), _desc)
+
+ /**
+ * @brief shell 鍙橀噺瀹氫箟
+ *
+ * @param _attr 鍙橀噺灞炴
+ * @param _name 鍙橀噺鍚
+ * @param _value 鍙橀噺鍊
+ * @param _desc 鍙橀噺鎻忚堪
+ */
+ #define SHELL_EXPORT_VAR(_attr, _name, _value, _desc) \
+ const char shellCmd##_name[] = #_name; \
+ const char shellDesc##_name[] = #_desc; \
+ SHELL_USED const ShellCommand \
+ shellVar##_name SHELL_SECTION("shellCommand") = \
+ { \
+ .attr.value = _attr, \
+ .data.var.name = shellCmd##_name, \
+ .data.var.value = (void *)_value, \
+ .data.var.desc = shellDesc##_name \
+ }
+
+ /**
+ * @brief shell 鐢ㄦ埛瀹氫箟
+ *
+ * @param _attr 鐢ㄦ埛灞炴
+ * @param _name 鐢ㄦ埛鍚
+ * @param _password 鐢ㄦ埛瀵嗙爜
+ * @param _desc 鐢ㄦ埛鎻忚堪
+ */
+ #define SHELL_EXPORT_USER(_attr, _name, _password, _desc) \
+ const char shellCmd##_name[] = #_name; \
+ const char shellPassword##_name[] = #_password; \
+ const char shellDesc##_name[] = #_desc; \
+ SHELL_USED const ShellCommand \
+ shellUser##_name SHELL_SECTION("shellCommand") = \
+ { \
+ .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_USER), \
+ .data.user.name = shellCmd##_name, \
+ .data.user.password = shellPassword##_name, \
+ .data.user.desc = shellDesc##_name \
+ }
+
+ /**
+ * @brief shell 鎸夐敭瀹氫箟
+ *
+ * @param _attr 鎸夐敭灞炴
+ * @param _value 鎸夐敭閿
+ * @param _func 鎸夐敭鍑芥暟
+ * @param _desc 鎸夐敭鎻忚堪
+ */
+ #define SHELL_EXPORT_KEY(_attr, _value, _func, _desc) \
+ const char shellDesc##_value[] = #_desc; \
+ SHELL_USED const ShellCommand \
+ shellKey##_value SHELL_SECTION("shellCommand") = \
+ { \
+ .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_KEY), \
+ .data.key.value = _value, \
+ .data.key.function = (void (*)(Shell *))_func, \
+ .data.key.desc = shellDesc##_value \
+ }
+
+ /**
+ * @brief shell 浠g悊鎸夐敭瀹氫箟
+ *
+ * @param _attr 鎸夐敭灞炴
+ * @param _value 鎸夐敭閿
+ * @param _func 鎸夐敭鍑芥暟
+ * @param _desc 鎸夐敭鎻忚堪
+ * @param ... 浠g悊鍙傛暟
+ */
+ #define SHELL_EXPORT_KEY_AGENCY(_attr, _value, _func, _desc, ...) \
+ SHELL_AGENCY_FUNC(_func, ##__VA_ARGS__) \
+ SHELL_EXPORT_KEY(_attr, _value, SHELL_AGENCY_FUNC_NAME(_func), _desc)
+
+#if SHELL_USING_FUNC_SIGNATURE == 1
+ /**
+ * @brief shell 鍙傛暟瑙f瀽鍣ㄥ畾涔
+ *
+ * @param _attr 鍙傛暟瑙f瀽鍣ㄥ睘鎬
+ * @param _type 鍙傛暟瑙f瀽鍣ㄧ被鍨
+ * @param _parser 鍙傛暟瑙f瀽鍣ㄥ嚱鏁
+ * @param _cleaner 鍙傛暟娓呯悊鍣
+ */
+ #define SHELL_EXPORT_PARAM_PARSER(_attr, _type, _parser, _cleaner) \
+ const char shellDesc##_parser[] = #_type; \
+ SHELL_USED const ShellCommand \
+ shellCommand##_parser SHELL_SECTION("shellCommand") = \
+ { \
+ .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_PARAM_PARSER), \
+ .data.paramParser.type = shellDesc##_parser, \
+ .data.paramParser.parser = (int (*)(char *, void **))_parser, \
+ .data.paramParser.cleaner = (int (*)(void *))_cleaner \
+ }
+#endif
+
+#else
+ /**
+ * @brief shell 鍛戒护item瀹氫箟
+ *
+ * @param _attr 鍛戒护灞炴
+ * @param _name 鍛戒护鍚
+ * @param _func 鍛戒护鍑芥暟
+ * @param _desc 鍛戒护鎻忚堪
+ */
+ #define SHELL_CMD_ITEM(_attr, _name, _func, _desc) \
+ { \
+ .attr.value = _attr, \
+ .data.cmd.name = #_name, \
+ .data.cmd.function = (int (*)())_func, \
+ .data.cmd.desc = #_desc \
+ }
+
+ /**
+ * @brief shell 鍙橀噺item瀹氫箟
+ *
+ * @param _attr 鍙橀噺灞炴
+ * @param _name 鍙橀噺鍚
+ * @param _value 鍙橀噺鍊
+ * @param _desc 鍙橀噺鎻忚堪
+ */
+ #define SHELL_VAR_ITEM(_attr, _name, _value, _desc) \
+ { \
+ .attr.value = _attr, \
+ .data.var.name = #_name, \
+ .data.var.value = (void *)_value, \
+ .data.var.desc = #_desc \
+ }
+
+ /**
+ * @brief shell 鐢ㄦ埛item瀹氫箟
+ *
+ * @param _attr 鐢ㄦ埛灞炴
+ * @param _name 鐢ㄦ埛鍚
+ * @param _password 鐢ㄦ埛瀵嗙爜
+ * @param _desc 鐢ㄦ埛鎻忚堪
+ */
+ #define SHELL_USER_ITEM(_attr, _name, _password, _desc) \
+ { \
+ .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_USER), \
+ .data.user.name = #_name, \
+ .data.user.password = #_password, \
+ .data.user.desc = #_desc \
+ }
+
+ /**
+ * @brief shell 鎸夐敭item瀹氫箟
+ *
+ * @param _attr 鎸夐敭灞炴
+ * @param _value 鎸夐敭閿
+ * @param _func 鎸夐敭鍑芥暟
+ * @param _desc 鎸夐敭鎻忚堪
+ */
+ #define SHELL_KEY_ITEM(_attr, _value, _func, _desc) \
+ { \
+ .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_KEY), \
+ .data.key.value = _value, \
+ .data.key.function = (void (*)(Shell *))_func, \
+ .data.key.desc = #_desc \
+ }
+
+#if SHELL_USING_FUNC_SIGNATURE == 1
+ /**
+ * @brief shell 鍙傛暟瑙f瀽鍣╥tem瀹氫箟
+ *
+ * @param _attr 鍙傛暟瑙f瀽鍣ㄥ睘鎬
+ * @param _type 鍙傛暟瑙f瀽鍣ㄧ被鍨
+ * @param _parser 鍙傛暟瑙f瀽鍣ㄥ嚱鏁
+ * @param _cleaner 鍙傛暟娓呯悊鍣
+ */
+ #define SHELL_PARAM_PARSER_ITEM(_attr, _type, _parser, _cleaner) \
+ { \
+ .attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_PARAM_PARSER), \
+ .data.paramParser.type = #_type, \
+ .data.paramParser.parser = (int (*)(char *, void **))_parser, \
+ .data.paramParser.cleaner = (int (*)(void *))_cleaner \
+ }
+#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
+
+ #define SHELL_EXPORT_CMD(_attr, _name, _func, _desc)
+#if SHELL_USING_FUNC_SIGNATURE == 1
+ #define SHELL_EXPORT_CMD_SIGN(_attr, _name, _func, _desc, _sign)
+#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
+ #define SHELL_EXPORT_CMD_AGENCY(_attr, _name, _func, _desc, ...)
+ #define SHELL_EXPORT_VAR(_attr, _name, _value, _desc)
+ #define SHELL_EXPORT_USER(_attr, _name, _password, _desc)
+ #define SHELL_EXPORT_KEY(_attr, _value, _func, _desc)
+ #define SHELL_EXPORT_KEY_AGENCY(_attr, _name, _func, _desc, ...)
+#if SHELL_USING_FUNC_SIGNATURE == 1
+ #define SHELL_EXPORT_PARAM_PARSER(_attr, _type, _parser, _cleaner)
+#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
+#endif /** SHELL_USING_CMD_EXPORT == 1 */
+
+/**
+ * @brief shell command绫诲瀷
+ */
+typedef enum
+{
+ SHELL_TYPE_CMD_MAIN = 0, /**< main褰㈠紡鍛戒护 */
+ SHELL_TYPE_CMD_FUNC, /**< C鍑芥暟褰㈠紡鍛戒护 */
+ SHELL_TYPE_VAR_INT, /**< int鍨嬪彉閲 */
+ SHELL_TYPE_VAR_SHORT, /**< short鍨嬪彉閲 */
+ SHELL_TYPE_VAR_CHAR, /**< char鍨嬪彉閲 */
+ SHELL_TYPE_VAR_STRING, /**< string鍨嬪彉閲 */
+ SHELL_TYPE_VAR_POINT, /**< 鎸囬拡鍨嬪彉閲 */
+ SHELL_TYPE_VAR_NODE, /**< 鑺傜偣鍙橀噺 */
+ SHELL_TYPE_USER, /**< 鐢ㄦ埛 */
+ SHELL_TYPE_KEY, /**< 鎸夐敭 */
+#if SHELL_USING_FUNC_SIGNATURE == 1
+ SHELL_TYPE_PARAM_PARSER, /**< 鍙傛暟瑙f瀽鍣 */
+#endif
+} ShellCommandType;
+
+
+/**
+ * @brief Shell瀹氫箟
+ */
+typedef struct shell_def
+{
+ struct
+ {
+ const struct shell_command *user; /**< 褰撳墠鐢ㄦ埛 */
+ int activeTime; /**< shell婵娲绘椂闂 */
+ char *path; /**< 褰撳墠shell璺緞 */
+ #if SHELL_USING_COMPANION == 1
+ struct shell_companion_object *companions; /**< 浼寸敓瀵硅薄 */
+ #endif
+ #if SHELL_KEEP_RETURN_VALUE == 1
+ int retVal; /**< 杩斿洖鍊 */
+ #endif
+ } info;
+ struct
+ {
+ unsigned short length; /**< 杈撳叆鏁版嵁闀垮害 */
+ unsigned short cursor; /**< 褰撳墠鍏夋爣浣嶇疆 */
+ char *buffer; /**< 杈撳叆缂撳啿 */
+ char *param[SHELL_PARAMETER_MAX_NUMBER]; /**< 鍙傛暟 */
+ unsigned short bufferSize; /**< 杈撳叆缂撳啿澶у皬 */
+ unsigned short paramCount; /**< 鍙傛暟鏁伴噺 */
+ int keyValue; /**< 杈撳叆鎸夐敭閿 */
+ } parser;
+#if SHELL_HISTORY_MAX_NUMBER > 0
+ struct
+ {
+ char *item[SHELL_HISTORY_MAX_NUMBER]; /**< 鍘嗗彶璁板綍 */
+ unsigned short number; /**< 鍘嗗彶璁板綍鏁 */
+ unsigned short record; /**< 褰撳墠璁板綍浣嶇疆 */
+ signed short offset; /**< 褰撳墠鍘嗗彶璁板綍鍋忕Щ */
+ } history;
+#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */
+ struct
+ {
+ void *base; /**< 鍛戒护琛ㄥ熀鍧 */
+ unsigned short count; /**< 鍛戒护鏁伴噺 */
+ } commandList;
+ struct
+ {
+ unsigned char isChecked : 1; /**< 瀵嗙爜鏍¢獙閫氳繃 */
+ unsigned char isActive : 1; /**< 褰撳墠娲诲姩Shell */
+ unsigned char tabFlag_t : 1; /**< tab鏍囧織 */
+ } status;
+ signed short (*read)(char *, unsigned short); /**< shell璇诲嚱鏁 */
+ signed short (*write)(char *, unsigned short); /**< shell鍐欏嚱鏁 */
+#if SHELL_USING_LOCK == 1
+ int (*lock)(struct shell_def *); /**< shell 鍔犻攣 */
+ int (*unlock)(struct shell_def *); /**< shell 瑙i攣 */
+#endif
+} Shell;
+
+
+/**
+ * @brief shell command瀹氫箟
+ */
+typedef struct shell_command
+{
+ union
+ {
+ struct
+ {
+ unsigned char permission : 8; /**< command鏉冮檺 */
+ ShellCommandType type : 4; /**< command绫诲瀷 */
+ unsigned char enableUnchecked : 1; /**< 鍦ㄦ湭鏍¢獙瀵嗙爜鐨勬儏鍐典笅鍙敤 */
+ unsigned char disableReturn : 1; /**< 绂佺敤杩斿洖鍊艰緭鍑 */
+ unsigned char readOnly : 1; /**< 鍙 */
+ unsigned char reserve : 1; /**< 淇濈暀 */
+ unsigned char paramNum : 4; /**< 鍙傛暟鏁伴噺 */
+ } attrs;
+ int value;
+ } attr; /**< 灞炴 */
+ union
+ {
+ struct
+ {
+ const char *name; /**< 鍛戒护鍚 */
+ int (*function)(); /**< 鍛戒护鎵ц鍑芥暟 */
+ const char *desc; /**< 鍛戒护鎻忚堪 */
+ #if SHELL_USING_FUNC_SIGNATURE == 1
+ const char *signature; /**< 鍑芥暟绛惧悕 */
+ #endif
+ } cmd; /**< 鍛戒护瀹氫箟 */
+ struct
+ {
+ const char *name; /**< 鍙橀噺鍚 */
+ void *value; /**< 鍙橀噺鍊 */
+ const char *desc; /**< 鍙橀噺鎻忚堪 */
+ } var; /**< 鍙橀噺瀹氫箟 */
+ struct
+ {
+ const char *name; /**< 鐢ㄦ埛鍚 */
+ const char *password; /**< 鐢ㄦ埛瀵嗙爜 */
+ const char *desc; /**< 鐢ㄦ埛鎻忚堪 */
+ } user; /**< 鐢ㄦ埛瀹氫箟 */
+ struct
+ {
+ int value; /**< 鎸夐敭閿 */
+ void (*function)(Shell *); /**< 鎸夐敭鎵ц鍑芥暟 */
+ const char *desc; /**< 鎸夐敭鎻忚堪 */
+ } key; /**< 鎸夐敭瀹氫箟 */
+#if SHELL_USING_FUNC_SIGNATURE == 1
+ struct
+ {
+ const char *type; /**< 鍙傛暟绫诲瀷 */
+ int (*parser)(char *, void **); /**< 瑙f瀽鍑芥暟 */
+ int (*cleaner)(void *); /**< 娓呯悊鍣 */
+ } paramParser; /**< 鍙傛暟瑙f瀽鍣 */
+#endif
+ } data;
+} ShellCommand;
+
+/**
+ * @brief shell鑺傜偣鍙橀噺灞炴
+ */
+typedef struct
+{
+ void *var; /**< 鍙橀噺寮曠敤 */
+ int (*get)(); /**< 鍙橀噺get鏂规硶 */
+ int (*set)(); /**< 鍙橀噺set鏂规硶 */
+} ShellNodeVarAttr;
+
+
+#define shellSetPath(_shell, _path) (_shell)->info.path = _path
+#define shellGetPath(_shell) ((_shell)->info.path)
+
+#define shellDeInit(shell) shellRemove(shell)
+
+void shellInit(Shell *shell, char *buffer, unsigned short size);
+void shellRemove(Shell *shell);
+unsigned short shellWriteString(Shell *shell, const char *string);
+void shellPrint(Shell *shell, const char *fmt, ...);
+void shellScan(Shell *shell, char *fmt, ...);
+Shell* shellGetCurrent(void);
+void shellHandler(Shell *shell, char data);
+void shellWriteEndLine(Shell *shell, char *buffer, int len);
+void shellTask(void *param);
+int shellRun(Shell *shell, const char *cmd);
+
+
+
+#if SHELL_USING_COMPANION == 1
+/**
+ * @brief shell浼寸敓瀵硅薄瀹氫箟
+ */
+typedef struct shell_companion_object
+{
+ int id; /**< 浼寸敓瀵硅薄ID */
+ void *obj; /**< 浼寸敓瀵硅薄 */
+ struct shell_companion_object *next; /**< 涓嬩竴涓即鐢熷璞 */
+} ShellCompanionObj;
+
+
+signed char shellCompanionAdd(Shell *shell, int id, void *object);
+signed char shellCompanionDel(Shell *shell, int id);
+void *shellCompanionGet(Shell *shell, int id);
+#endif
+
+#endif
diff --git a/common/letter-shell-master/src/shell_cfg.h b/common/letter-shell-master/src/shell_cfg.h
new file mode 100644
index 0000000..1e3be8f
--- /dev/null
+++ b/common/letter-shell-master/src/shell_cfg.h
@@ -0,0 +1,262 @@
+/**
+ * @file shell_cfg.h
+ * @author Letter (nevermindzzt@gmail.com)
+ * @brief shell config
+ * @version 3.0.0
+ * @date 2019-12-31
+ *
+ * @copyright (c) 2019 Letter
+ *
+ */
+
+#ifndef __SHELL_CFG_H__
+#define __SHELL_CFG_H__
+
+#include "bsp_tim.h"
+
+
+#ifdef SHELL_CFG_USER
+#include SHELL_CFG_USER
+#endif
+
+
+#ifndef SHELL_TASK_WHILE
+/**
+ * @brief 鏄惁浣跨敤榛樿shell浠诲姟while寰幆
+ * 浣胯兘姝ゅ畯锛屽垯`shellTask()`鍑芥暟浼氫竴鐩村惊鐜鍙栬緭鍏ワ紝涓鑸娇鐢ㄦ搷浣滅郴缁熷缓绔媠hell
+ * 浠诲姟鏃朵娇鑳芥瀹忥紝鍏抽棴姝ゅ畯鐨勬儏鍐典笅锛屼竴鑸傜敤浜庢棤鎿嶄綔绯荤粺锛屽湪涓诲惊鐜腑璋冪敤`shellTask()`
+ */
+#define SHELL_TASK_WHILE 1
+#endif /** SHELL_TASK_WHILE */
+
+#ifndef SHELL_USING_CMD_EXPORT
+/**
+ * @brief 鏄惁浣跨敤鍛戒护瀵煎嚭鏂瑰紡
+ * 浣胯兘姝ゅ畯鍚庯紝鍙互浣跨敤`SHELL_EXPORT_CMD()`绛夊鍑哄懡浠
+ * 瀹氫箟shell鍛戒护锛屽叧闂瀹忕殑鎯呭喌涓嬶紝闇瑕佷娇鐢ㄥ懡浠よ〃鐨勬柟寮
+ */
+#define SHELL_USING_CMD_EXPORT 1
+#endif /** SHELL_USING_CMD_EXPORT */
+
+#ifndef SHELL_USING_COMPANION
+/**
+ * @brief 鏄惁浣跨敤shell浼寸敓瀵硅薄
+ * 涓浜涙墿灞曠殑缁勪欢(鏂囦欢绯荤粺鏀寔锛屾棩蹇楀伐鍏风瓑)闇瑕佷娇鐢ㄤ即鐢熷璞
+ */
+#define SHELL_USING_COMPANION 1
+#endif /** SHELL_USING_COMPANION */
+
+#ifndef SHELL_SUPPORT_END_LINE
+/**
+ * @brief 鏀寔shell灏捐妯″紡
+ */
+#define SHELL_SUPPORT_END_LINE 1
+#endif /** SHELL_SUPPORT_END_LINE */
+
+#ifndef SHELL_HELP_LIST_USER
+/**
+ * @brief 鏄惁鍦ㄨ緭鍑哄懡浠ゅ垪琛ㄤ腑鍒楀嚭鐢ㄦ埛
+ */
+#define SHELL_HELP_LIST_USER 1
+#endif /** SHELL_HELP_LIST_USER */
+
+#ifndef SHELL_HELP_LIST_VAR
+/**
+ * @brief 鏄惁鍦ㄨ緭鍑哄懡浠ゅ垪琛ㄤ腑鍒楀嚭鍙橀噺
+ */
+#define SHELL_HELP_LIST_VAR 0
+#endif /** SHELL_HELP_LIST_VAR */
+
+#ifndef SHELL_HELP_LIST_KEY
+/**
+ * @brief 鏄惁鍦ㄨ緭鍑哄懡浠ゅ垪琛ㄤ腑鍒楀嚭鎸夐敭
+ */
+#define SHELL_HELP_LIST_KEY 0
+#endif /** SHELL_HELP_LIST_KEY */
+
+#ifndef SHELL_HELP_SHOW_PERMISSION
+/**
+ * @brief 鏄惁鍦ㄨ緭鍑哄懡浠ゅ垪琛ㄤ腑灞曠ず鍛戒护鏉冮檺
+ */
+#define SHELL_HELP_SHOW_PERMISSION 1
+#endif /** SHELL_HELP_SHOW_PERMISSION */
+
+#ifndef SHELL_ENTER_LF
+/**
+ * @brief 浣跨敤LF浣滀负鍛戒护琛屽洖杞﹁Е鍙
+ * 鍙互鍜孲HELL_ENTER_CR鍚屾椂寮鍚
+ */
+#define SHELL_ENTER_LF 1
+#endif /** SHELL_ENTER_LF */
+
+#ifndef SHELL_ENTER_CR
+/**
+ * @brief 浣跨敤CR浣滀负鍛戒护琛屽洖杞﹁Е鍙
+ * 鍙互鍜孲HELL_ENTER_LF鍚屾椂寮鍚
+ */
+#define SHELL_ENTER_CR 1
+#endif /** SHELL_ENTER_CR */
+
+#ifndef SHELL_ENTER_CRLF
+/**
+ * @brief 浣跨敤CRLF浣滀负鍛戒护琛屽洖杞﹁Е鍙
+ * 涓嶅彲浠ュ拰SHELL_ENTER_LF鎴朣HELL_ENTER_CR鍚屾椂寮鍚
+ */
+#define SHELL_ENTER_CRLF 0
+#endif /** SHELL_ENTER_CRLF */
+
+#ifndef SHELL_EXEC_UNDEF_FUNC
+/**
+ * @brief 浣跨敤鎵ц鏈鍑哄嚱鏁扮殑鍔熻兘
+ * 鍚敤鍚庯紝鍙互閫氳繃`exec [addr] [args]`鐩存帴鎵ц瀵瑰簲鍦板潃鐨勫嚱鏁
+ * @attention 濡傛灉鍦板潃閿欒锛屽彲鑳戒細鐩存帴寮曡捣绋嬪簭宕╂簝
+ */
+#define SHELL_EXEC_UNDEF_FUNC 0
+#endif /** SHELL_EXEC_UNDEF_FUNC */
+
+#ifndef SHELL_PARAMETER_MAX_NUMBER
+/**
+ * @brief shell鍛戒护鍙傛暟鏈澶ф暟閲
+ * 鍖呭惈鍛戒护鍚嶅湪鍐咃紝瓒呰繃16涓弬鏁板苟涓斾娇鐢ㄤ簡鍙傛暟鑷姩杞崲鐨勬儏鍐典笅锛岄渶瑕佷慨鏀规簮鐮
+ */
+#define SHELL_PARAMETER_MAX_NUMBER 15
+#endif /** SHELL_PARAMETER_MAX_NUMBER */
+
+#ifndef SHELL_HISTORY_MAX_NUMBER
+/**
+ * @brief 鍘嗗彶鍛戒护璁板綍鏁伴噺
+ */
+#define SHELL_HISTORY_MAX_NUMBER 5
+#endif /** SHELL_HISTORY_MAX_NUMBER */
+
+#ifndef SHELL_DOUBLE_CLICK_TIME
+/**
+ * @brief 鍙屽嚮闂撮殧(ms)
+ * 浣胯兘瀹廯SHELL_LONG_HELP`鍚庢瀹忕敓鏁堬紝瀹氫箟鍙屽嚮tab琛ュ叏help鐨勬椂闂撮棿闅
+ */
+#define SHELL_DOUBLE_CLICK_TIME 200
+#endif /** SHELL_DOUBLE_CLICK_TIME */
+
+#ifndef SHELL_QUICK_HELP
+/**
+ * @brief 蹇熷府鍔
+ * 浣滅敤浜庡弻鍑籺ab鐨勫満鏅紝褰撲娇鑳芥瀹忔椂锛屽弻鍑籺ab涓嶄細瀵瑰懡浠よ繘琛宧elp琛ュ叏锛岃屾槸鐩存帴鏄剧ず瀵瑰簲鍛戒护鐨勫府鍔╀俊鎭
+ */
+#define SHELL_QUICK_HELP 1
+#endif /** SHELL_QUICK_HELP */
+
+#ifndef SHELL_KEEP_RETURN_VALUE
+/**
+ * @brief 淇濆瓨鍛戒护杩斿洖鍊
+ * 寮鍚悗浼氶粯璁ゅ畾涔変竴涓猔RETVAL`鍙橀噺锛屼細淇濆瓨涓婁竴娆″懡浠ゆ墽琛岀殑杩斿洖鍊硷紝鍙互鍦ㄩ殢鍚庣殑鍛戒护涓繘琛岃皟鐢
+ * 濡傛灉鍛戒护鐨刞SHELL_CMD_DISABLE_RETURN`鏍囧織琚缃紝鍒欒鍛戒护涓嶄細鏇存柊`RETVAL`
+ */
+#define SHELL_KEEP_RETURN_VALUE 0
+#endif /** SHELL_KEEP_RETURN_VALUE */
+
+#ifndef SHELL_MAX_NUMBER
+/**
+ * @brief 绠$悊鐨勬渶澶hell鏁伴噺
+ */
+#define SHELL_MAX_NUMBER 30
+#endif /** SHELL_MAX_NUMBER */
+
+#ifndef SHELL_PRINT_BUFFER
+/**
+ * @brief shell鏍煎紡鍖栬緭鍑虹殑缂撳啿澶у皬
+ * 涓0鏃朵笉浣跨敤shell鏍煎紡鍖栬緭鍑
+ */
+#define SHELL_PRINT_BUFFER 128
+#endif /** SHELL_PRINT_BUFFER */
+
+#ifndef SHELL_SCAN_BUFFER
+/**
+ * @brief shell鏍煎紡鍖栬緭鍏ョ殑缂撳啿澶у皬
+ * 涓0鏃朵笉浣跨敤shell鏍煎紡鍖栬緭鍏
+ * @note shell鏍煎紡鍖栬緭鍏ヤ細闃诲shellTask, 浠呴傜敤浜庡湪鏈夋搷浣滅郴缁熺殑鎯呭喌涓嬩娇鐢
+ */
+#define SHELL_SCAN_BUFFER 0
+#endif /** SHELL_SCAN_BUFFER */
+
+#ifndef SHELL_GET_TICK
+/**
+ * @brief 鑾峰彇绯荤粺鏃堕棿(ms)
+ * 瀹氫箟姝ゅ畯涓鸿幏鍙栫郴缁烼ick锛屽`HAL_GetTick()`
+ * @note 姝ゅ畯涓嶅畾涔夋椂鏃犳硶浣跨敤鍙屽嚮tab琛ュ叏鍛戒护help锛屾棤娉曚娇鐢╯hell瓒呮椂閿佸畾
+ */
+#define SHELL_GET_TICK() BSP_Get_Tick()
+#endif /** SHELL_GET_TICK */
+
+#ifndef SHELL_USING_LOCK
+/**
+ * @brief 浣跨敤閿
+ * @note 浣跨敤shell閿佹椂锛岄渶瑕佸鍔犻攣鍜岃В閿佽繘琛屽疄鐜
+ */
+#define SHELL_USING_LOCK 0
+#endif /** SHELL_USING_LOCK */
+
+#ifndef SHELL_MALLOC
+/**
+ * @brief shell鍐呭瓨鍒嗛厤
+ * shell鏈韩涓嶉渶瑕佹鎺ュ彛锛岃嫢浣跨敤shell浼寸敓瀵硅薄锛岄渶瑕佽繘琛屽畾涔
+ */
+#define SHELL_MALLOC(size) 0
+#endif /** SHELL_MALLOC */
+
+#ifndef SHELL_FREE
+/**
+ * @brief shell鍐呭瓨閲婃斁
+ * shell鏈韩涓嶉渶瑕佹鎺ュ彛锛岃嫢浣跨敤shell浼寸敓瀵硅薄锛岄渶瑕佽繘琛屽畾涔
+ */
+#define SHELL_FREE(obj)
+#endif /** SHELL_FREE */
+
+#ifndef SHELL_SHOW_INFO
+/**
+ * @brief 鏄惁鏄剧ずshell淇℃伅
+ */
+#define SHELL_SHOW_INFO 0
+#endif /** SHELL_SHOW_INFO */
+
+#ifndef SHELL_CLS_WHEN_LOGIN
+/**
+ * @brief 鏄惁鍦ㄧ櫥褰曞悗娓呴櫎鍛戒护琛
+ */
+#define SHELL_CLS_WHEN_LOGIN 1
+#endif /** SHELL_CLS_WHEN_LOGIN */
+
+#ifndef SHELL_DEFAULT_USER
+/**
+ * @brief shell榛樿鐢ㄦ埛
+ */
+#define SHELL_DEFAULT_USER "ch585"
+#endif /** SHELL_DEFAULT_USER */
+
+#ifndef SHELL_DEFAULT_USER_PASSWORD
+/**
+ * @brief shell榛樿鐢ㄦ埛瀵嗙爜
+ * 鑻ラ粯璁ょ敤鎴蜂笉闇瑕佸瘑鐮侊紝璁句负""
+ */
+#define SHELL_DEFAULT_USER_PASSWORD ""
+#endif /** SHELL_DEFAULT_USER_PASSWORD */
+
+#ifndef SHELL_LOCK_TIMEOUT
+/**
+ * @brief shell鑷姩閿佸畾瓒呮椂
+ * shell褰撳墠鐢ㄦ埛瀵嗙爜鏈夋晥鐨勬椂鍊欑敓鏁堬紝瓒呮椂鍚庝細鑷姩閲嶆柊閿佸畾shell
+ * 璁剧疆涓0鏃跺叧闂嚜鍔ㄩ攣瀹氬姛鑳斤紝鏃堕棿鍗曚綅涓篳SHELL_GET_TICK()`鍗曚綅
+ * @note 浣跨敤瓒呮椂閿佸畾蹇呴』淇濊瘉`SHELL_GET_TICK()`鏈夋晥
+ */
+#define SHELL_LOCK_TIMEOUT 0 * 60 * 1000
+#endif /** SHELL_LOCK_TIMEOUT */
+
+#ifndef SHELL_USING_FUNC_SIGNATURE
+/**
+ * @brief 浣跨敤鍑芥暟绛惧悕
+ * 浣胯兘鍚庯紝鍙互鍦ㄥ0鏄庡懡浠ゆ椂锛屾寚瀹氬嚱鏁扮殑绛惧悕锛宻hell 浼氭牴鎹嚱鏁扮鍚嶈繘琛屽弬鏁拌浆鎹紝
+ * 鑰屼笉鏄嚜鍔ㄥ垽鏂弬鏁扮殑绫诲瀷锛屽鏋滃弬鏁板拰鍑芥暟绛惧悕涓嶅尮閰嶏紝浼氬仠姝㈡墽琛屽懡浠
+ */
+#define SHELL_USING_FUNC_SIGNATURE 1
+#endif /** SHELL_USING_FUNC_SIGNATURE */
+
+#endif
diff --git a/common/letter-shell-master/src/shell_cmd_list.c b/common/letter-shell-master/src/shell_cmd_list.c
new file mode 100644
index 0000000..6a433a8
--- /dev/null
+++ b/common/letter-shell-master/src/shell_cmd_list.c
@@ -0,0 +1,107 @@
+/**
+ * @file shell_cmd_list.c
+ * @author Letter (NevermindZZT@gmail.com)
+ * @brief shell cmd list
+ * @version 3.0.0
+ * @date 2020-01-17
+ *
+ * @copyright (c) 2020 Letter
+ *
+ */
+
+#include "shell.h"
+
+
+#if SHELL_USING_CMD_EXPORT != 1
+
+extern int shellSetVar(char *name, int value);
+extern void shellUp(Shell *shell);
+extern void shellDown(Shell *shell);
+extern void shellRight(Shell *shell);
+extern void shellLeft(Shell *shell);
+extern void shellTab(Shell *shell);
+extern void shellBackspace(Shell *shell);
+extern void shellDelete(Shell *shell);
+extern void shellEnter(Shell *shell);
+extern void shellHelp(int argc, char *argv[]);
+extern void shellUsers(void);
+extern void shellCmds(void);
+extern void shellVars(void);
+extern void shellKeys(void);
+extern void shellClear(void);
+#if SHELL_EXEC_UNDEF_FUNC == 1
+extern int shellExecute(int argc, char *argv[]);
+#endif
+
+SHELL_AGENCY_FUNC(shellRun, shellGetCurrent(), (const char *)p1);
+
+
+
+/**
+ * @brief shell鍛戒护琛
+ *
+ */
+const ShellCommand shellCommandList[] =
+{
+ {.attr.value=SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_USER),
+ .data.user.name = SHELL_DEFAULT_USER,
+ .data.user.password = SHELL_DEFAULT_USER_PASSWORD,
+ .data.user.desc = "default user"},
+ SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
+ setVar, shellSetVar, set var),
+ SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0), 0x1B5B4100, shellUp, up),
+ SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0), 0x1B5B4200, shellDown, down),
+ SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+ 0x1B5B4300, shellRight, right),
+ SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+ 0x1B5B4400, shellLeft, left),
+ SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0), 0x09000000, shellTab, tab),
+ SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+ 0x08000000, shellBackspace, backspace),
+ SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+ 0x7F000000, shellDelete, delete),
+ SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+ 0x1B5B337E, shellDelete, delete),
+#if SHELL_ENTER_LF == 1
+ SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+ 0x0A000000, shellEnter, enter),
+#endif
+#if SHELL_ENTER_CR == 1
+ SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+ 0x0D000000, shellEnter, enter),
+#endif
+#if SHELL_ENTER_CRLF == 1
+ SHELL_KEY_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED,
+ 0x0D0A0000, shellEnter, enter),
+#endif
+ SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,
+ help, shellHelp, show command info\r\nhelp [cmd]),
+ SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+ users, shellUsers, list all user),
+ SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+ cmds, shellCmds, list all cmd),
+ SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+ vars, shellVars, list all var),
+ SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+ keys, shellKeys, list all key),
+ SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+ clear, shellClear, clear console),
+ SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
+ sh, SHELL_AGENCY_FUNC_NAME(shellRun), run command directly),
+#if SHELL_EXEC_UNDEF_FUNC == 1
+ SHELL_CMD_ITEM(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,
+ exec, shellExecute, execute function undefined),
+#endif
+
+};
+
+
+
+/**
+ * @brief shell鍛戒护琛ㄥぇ灏
+ *
+ */
+const unsigned short shellCommandCount
+ = sizeof(shellCommandList) / sizeof(ShellCommand);
+
+#endif
diff --git a/common/letter-shell-master/src/shell_companion.c b/common/letter-shell-master/src/shell_companion.c
new file mode 100644
index 0000000..eb494c7
--- /dev/null
+++ b/common/letter-shell-master/src/shell_companion.c
@@ -0,0 +1,87 @@
+/**
+ * @file shell_companion.c
+ * @author Letter (nevermindzzt@gmail.com)
+ * @brief shell companion object support
+ * @version 3.0.3
+ * @date 2020-07-22
+ *
+ * @copyright (c) 2020 Letter
+ *
+ */
+ #include "shell.h"
+
+#if SHELL_USING_COMPANION == 1
+/**
+ * @brief shell娣诲姞浼寸敓瀵硅薄
+ *
+ * @param shell shell瀵硅薄
+ * @param id 浼寸敓瀵硅薄ID
+ * @param object 浼寸敓瀵硅薄
+ * @return signed char 0 娣诲姞鎴愬姛 -1 娣诲姞澶辫触
+ */
+signed char shellCompanionAdd(Shell *shell, int id, void *object)
+{
+ ShellCompanionObj *companions = shell->info.companions;
+ ShellCompanionObj *node = SHELL_MALLOC(sizeof(ShellCompanionObj));
+ SHELL_ASSERT(node, return -1);
+ node->id = id;
+ node->obj = object;
+ node->next = companions;
+ shell->info.companions = node;
+ return 0;
+}
+
+/**
+ * @brief shell鍒犻櫎浼寸敓瀵硅薄
+ *
+ * @param shell shell瀵硅薄
+ * @param id 浼寸敓瀵硅薄ID
+ * @return signed char 0 鍒犻櫎鎴愬姛 -1 鏃犲尮閰嶅璞
+ */
+signed char shellCompanionDel(Shell *shell, int id)
+{
+ ShellCompanionObj *companions = shell->info.companions;
+ ShellCompanionObj *front = companions;
+ while (companions)
+ {
+ if (companions->id == id)
+ {
+ if (companions == shell->info.companions && !(companions->next))
+ {
+ shell->info.companions = (void *)0;
+ }
+ else
+ {
+ front->next = companions->next;
+ }
+ SHELL_FREE(companions);
+ return 0;
+ }
+ front = companions;
+ companions = companions->next;
+ }
+ return -1;
+}
+
+/**
+ * @brief shell鑾峰彇浼寸敓瀵硅薄
+ *
+ * @param shell shell瀵硅薄
+ * @param id 浼寸敓瀵硅薄ID
+ * @return void* 浼寸敓瀵硅薄锛屾棤鍖归厤瀵硅薄鏃惰繑鍥濶ULL
+ */
+void *shellCompanionGet(Shell *shell, int id)
+{
+ SHELL_ASSERT(shell, return (void *)0);
+ ShellCompanionObj *companions = shell->info.companions;
+ while (companions)
+ {
+ if (companions->id == id)
+ {
+ return companions->obj;
+ }
+ companions = companions->next;
+ }
+ return (void *)0;
+}
+#endif /** SHELL_USING_COMPANION == 1 */
diff --git a/common/letter-shell-master/src/shell_ext.c b/common/letter-shell-master/src/shell_ext.c
new file mode 100644
index 0000000..a761d49
--- /dev/null
+++ b/common/letter-shell-master/src/shell_ext.c
@@ -0,0 +1,658 @@
+/**
+ * @file shell_ext.c
+ * @author Letter (NevermindZZT@gmail.com)
+ * @brief shell extensions
+ * @version 3.0.0
+ * @date 2019-12-31
+ *
+ * @copyright (c) 2019 Letter
+ *
+ */
+
+#include "shell_cfg.h"
+#include "shell.h"
+#include "shell_ext.h"
+#include "string.h"
+
+extern ShellCommand* shellSeekCommand(Shell *shell,
+ const char *cmd,
+ ShellCommand *base,
+ unsigned short compareLength);
+extern int shellGetVarValue(Shell *shell, ShellCommand *command);
+
+#if SHELL_USING_FUNC_SIGNATURE == 1
+/**
+ * @brief 鑾峰彇涓嬩竴涓弬鏁扮被鍨
+ *
+ * @param signature 鍑芥暟绛惧悕
+ * @param index 鍙傛暟閬嶅巻鍦ㄧ鍚嶄腑鐨勮捣濮嬬储寮
+ * @param type 鑾峰彇鍒扮殑鍙傛暟绫诲瀷
+ *
+ * @return int 涓嬩竴涓弬鏁板湪绛惧悕涓殑绱㈠紩
+ */
+static int shellGetNextParamType(const char *signature, int index, char *type)
+{
+ const char *p = signature + index;
+ if (*p == 'L')
+ {
+ while (*p != ';' && *p != 0)
+ {
+ *type++ = *p++;
+ index++;
+ }
+ *type++ = *p++;
+ index++;
+ }
+ else if (*p != 0)
+ {
+ *type++ = *p;
+ index++;
+ }
+ *type = '\0';
+ return index;
+}
+
+
+static int shellGetParamNumExcept(const char *signature)
+{
+ int num = 0;
+ const char *p = signature;
+
+ while (*p)
+ {
+ if (*p == 'L')
+ {
+ while (*p != ';' && *p != 0)
+ {
+ p++;
+ }
+ p++;
+ }
+ else
+ {
+ p++;
+ }
+ num++;
+ }
+ return num;
+}
+#endif
+
+/**
+ * @brief 鍒ゆ柇鏁板瓧杩涘埗
+ *
+ * @param string 鍙傛暟瀛楃涓
+ * @return ShellNumType 杩涘埗
+ */
+static ShellNumType shellExtNumType(char *string)
+{
+ char *p = string;
+ ShellNumType type = NUM_TYPE_DEC;
+
+ if ((*p == '0') && ((*(p + 1) == 'x') || (*(p + 1) == 'X')))
+ {
+ type = NUM_TYPE_HEX;
+ }
+ else if ((*p == '0') && ((*(p + 1) == 'b') || (*(p + 1) == 'B')))
+ {
+ type = NUM_TYPE_BIN;
+ }
+ else if (*p == '0')
+ {
+ type = NUM_TYPE_OCT;
+ }
+
+ while (*p++)
+ {
+ if (*p == '.' && *(p + 1) != 0)
+ {
+ type = NUM_TYPE_FLOAT;
+ break;
+ }
+ }
+
+ return type;
+}
+
+
+/**
+ * @brief 瀛楃杞暟瀛
+ *
+ * @param code 瀛楃
+ * @return char 鏁板瓧
+ */
+static char shellExtToNum(char code)
+{
+ if ((code >= '0') && (code <= '9'))
+ {
+ return code -'0';
+ }
+ else if ((code >= 'a') && (code <= 'f'))
+ {
+ return code - 'a' + 10;
+ }
+ else if ((code >= 'A') && (code <= 'F'))
+ {
+ return code - 'A' + 10;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+/**
+ * @brief 瑙f瀽瀛楃鍙傛暟
+ *
+ * @param string 瀛楃涓插弬鏁
+ * @return char 瑙f瀽鍑虹殑瀛楃
+ */
+static char shellExtParseChar(char *string)
+{
+ char *p = (*string == '\'') ? (string + 1) : string;
+ char value = 0;
+
+ if (*p == '\\')
+ {
+ switch (*(p + 1))
+ {
+ case 'b':
+ value = '\b';
+ break;
+ case 'r':
+ value = '\r';
+ break;
+ case 'n':
+ value = '\n';
+ break;
+ case 't':
+ value = '\t';
+ break;
+ case '0':
+ value = 0;
+ break;
+ default:
+ value = *(p + 1);
+ break;
+ }
+ }
+ else
+ {
+ value = *p;
+ }
+ return value;
+}
+
+
+/**
+ * @brief 瑙f瀽瀛楃涓插弬鏁
+ *
+ * @param string 瀛楃涓插弬鏁
+ * @return char* 瑙f瀽鍑虹殑瀛楃涓
+ */
+static char* shellExtParseString(char *string)
+{
+ char *p = string;
+ unsigned short index = 0;
+
+ if (*string == '\"')
+ {
+ p = ++string;
+ }
+
+ while (*p)
+ {
+ if (*p == '\\')
+ {
+ *(string + index) = shellExtParseChar(p - 1);
+ p++;
+ }
+ else if (*p == '\"')
+ {
+ *(string + index) = 0;
+ }
+ else
+ {
+ *(string + index) = *p;
+ }
+ p++;
+ index ++;
+ }
+ *(string + index) = 0;
+ return string;
+}
+
+
+/**
+ * @brief 瑙f瀽鏁板瓧鍙傛暟
+ *
+ * @param string 瀛楃涓插弬鏁
+ * @return unsigned int 瑙f瀽鍑虹殑鏁板瓧
+ */
+static unsigned int shellExtParseNumber(char *string)
+{
+ ShellNumType type = NUM_TYPE_DEC;
+ char radix = 10;
+ char *p = string;
+ char offset = 0;
+ signed char sign = 1;
+ unsigned int valueInt = 0;
+ float valueFloat = 0.0;
+ unsigned int devide = 0;
+
+ if (*string == '-')
+ {
+ sign = -1;
+ }
+
+ type = shellExtNumType(string + ((sign == -1) ? 1 : 0));
+
+ switch ((char)type)
+ {
+ case NUM_TYPE_HEX:
+ radix = 16;
+ offset = 2;
+ break;
+
+ case NUM_TYPE_OCT:
+ radix = 8;
+ offset = 1;
+ break;
+
+ case NUM_TYPE_BIN:
+ radix = 2;
+ offset = 2;
+ break;
+
+ default:
+ break;
+ }
+
+ p = string + offset + ((sign == -1) ? 1 : 0);
+
+ while (*p)
+ {
+ if (*p == '.')
+ {
+ devide = 1;
+ p++;
+ continue;
+ }
+ valueInt = valueInt * radix + shellExtToNum(*p);
+ devide *= 10;
+ p++;
+ }
+ if (type == NUM_TYPE_FLOAT && devide != 0)
+ {
+ valueFloat = (float)valueInt / devide * sign;
+ return *(unsigned int *)(&valueFloat);
+ }
+ else
+ {
+ return valueInt * sign;
+ }
+}
+
+
+/**
+ * @brief 瑙f瀽鍙橀噺鍙傛暟
+ *
+ * @param shell shell瀵硅薄
+ * @param var 鍙橀噺
+ * @return unsigned int 鍙橀噺鍊
+ */
+static unsigned int shellExtParseVar(Shell *shell, char *var)
+{
+ ShellCommand *command = shellSeekCommand(shell,
+ var + 1,
+ shell->commandList.base,
+ 0);
+ if (command)
+ {
+ return shellGetVarValue(shell, command);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+/**
+ * @brief 瑙f瀽鍙傛暟
+ *
+ * @param shell shell瀵硅薄
+ * @param string 鍙傛暟
+ * @param type 鍙傛暟绫诲瀷
+ * @param result 瑙f瀽缁撴灉
+ *
+ * @return int 0 瑙f瀽鎴愬姛 --1 瑙f瀽澶辫触
+ */
+int shellExtParsePara(Shell *shell, char *string, char *type, unsigned int *result)
+{
+ if (type == NULL || (*string == '$' && *(string + 1)))
+ {
+ if (*string == '\'' && *(string + 1))
+ {
+ *result = (unsigned int)shellExtParseChar(string);
+ return 0;
+ }
+ else if (*string == '-' || (*string >= '0' && *string <= '9'))
+ {
+ *result = (unsigned int)shellExtParseNumber(string);
+ return 0;
+ }
+ else if (*string == '$' && *(string + 1))
+ {
+ *result = shellExtParseVar(shell, string);
+ return 0;
+ }
+ else if (*string)
+ {
+ *result = (unsigned int)shellExtParseString(string);
+ return 0;
+ }
+ }
+#if SHELL_USING_FUNC_SIGNATURE == 1
+ else
+ {
+ if (strcmp("c", type) == 0)
+ {
+ *result = (unsigned int)shellExtParseChar(string);
+ return 0;
+ }
+ else if (strcmp("i", type) == 0
+ || strcmp("f", type) == 0
+ || strcmp("p", type) == 0)
+ {
+ *result = (unsigned int)shellExtParseNumber(string);
+ return 0;
+ }
+ else if (strcmp("s", type) == 0)
+ {
+ *result = (unsigned int)shellExtParseString(string);
+ return 0;
+ }
+ else
+ {
+ ShellCommand *command = shellSeekCommand(shell,
+ type,
+ shell->commandList.base,
+ 0);
+ if (command != NULL)
+ {
+ void *param;
+ if (command->data.paramParser.parser(shellExtParseString(string), ¶m) == 0)
+ {
+ *result = (unsigned int)param;
+ return 0;
+ }
+ else
+ {
+ shellWriteString(shell, "Parse param for type: ");
+ shellWriteString(shell, type);
+ shellWriteString(shell, " failed\r\n");
+ return -1;
+ }
+ }
+ else
+ {
+ shellWriteString(shell, "Can't find the param parser for type: ");
+ shellWriteString(shell, type);
+ shellWriteString(shell, "\r\n");
+ return -1;
+ }
+ }
+ }
+#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
+ return -1;
+}
+
+
+#if SHELL_USING_FUNC_SIGNATURE == 1
+/**
+ * @brief 娓呯悊鍙傛暟
+ *
+ * @param shell shell
+ * @param type 鍙傛暟绫诲瀷
+ * @param param 鍙傛暟
+ *
+ * @return int 0 娓呯悊鎴愬姛 -1 娓呯悊澶辫触
+ */
+int shellExtCleanerPara(Shell *shell, char *type, unsigned int param)
+{
+ if (type == NULL)
+ {
+ return 0;
+ }
+ else
+ {
+ if (strcmp("c", type) == 0
+ || strcmp("i", type) == 0
+ || strcmp("f", type) == 0
+ || strcmp("p", type) == 0
+ || strcmp("s", type) == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ ShellCommand *command = shellSeekCommand(shell,
+ type,
+ shell->commandList.base,
+ 0);
+ if (command != NULL && command->data.paramParser.cleaner != NULL)
+ {
+ return command->data.paramParser.cleaner((void *)param);
+ }
+ }
+ }
+ return -1;
+}
+#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
+
+
+/**
+ * @brief 鎵ц鍛戒护
+ *
+ * @param shell shell瀵硅薄
+ * @param command 鍛戒护
+ * @param argc 鍙傛暟涓暟
+ * @param argv 鍙傛暟
+ * @return int 杩斿洖鍊
+ */
+int shellExtRun(Shell *shell, ShellCommand *command, int argc, char *argv[])
+{
+ int ret = 0;
+ unsigned int params[SHELL_PARAMETER_MAX_NUMBER] = {0};
+ int paramNum = command->attr.attrs.paramNum > (argc - 1) ?
+ command->attr.attrs.paramNum : (argc - 1);
+#if SHELL_USING_FUNC_SIGNATURE == 1
+ char type[16];
+ int index = 0;
+
+ if (command->data.cmd.signature != NULL)
+ {
+ int except = shellGetParamNumExcept(command->data.cmd.signature);
+ if (except != argc - 1)
+ {
+ shellWriteString(shell, "Parameters number incorrect\r\n");
+ return -1;
+ }
+ }
+#endif
+ for (int i = 0; i < argc - 1; i++)
+ {
+ #if SHELL_USING_FUNC_SIGNATURE == 1
+ if (command->data.cmd.signature != NULL) {
+ index = shellGetNextParamType(command->data.cmd.signature, index, type);
+ if (shellExtParsePara(shell, argv[i + 1], type, ¶ms[i]) != 0)
+ {
+ return -1;
+ }
+ }
+ else
+ #endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
+ {
+ if (shellExtParsePara(shell, argv[i + 1], NULL, ¶ms[i]) != 0)
+ {
+ return -1;
+ }
+ }
+ }
+ switch (paramNum)
+ {
+#if SHELL_PARAMETER_MAX_NUMBER >= 1
+ case 0:
+ ret = command->data.cmd.function();
+ break;
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 1 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 2
+ case 1:
+ {
+ int (*func)(int) = command->data.cmd.function;
+ ret = func(params[0]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 2 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 3
+ case 2:
+ {
+ int (*func)(int, int) = command->data.cmd.function;
+ ret = func(params[0], params[1]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 3 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 4
+ case 3:
+ {
+ int (*func)(int, int, int) = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 4 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 5
+ case 4:
+ {
+ int (*func)(int, int, int, int) = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2], params[3]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 5 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 6
+ case 5:
+ {
+ int (*func)(int, int, int, int, int) = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2], params[3], params[4]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 6 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 7
+ case 6:
+ {
+ int (*func)(int, int, int, int, int, int) = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2], params[3], params[4], params[5]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 7 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 8
+ case 7:
+ {
+ int (*func)(int, int, int, int, int, int, int) = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 8 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 9
+ case 8:
+ {
+ int (*func)(int, int, int, int, int, int, int, int) = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 9 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 10
+ case 9:
+ {
+ int (*func)(int, int, int, int, int, int, int, int, int) = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
+ params[8]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 10 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 11
+ case 10:
+ {
+ int (*func)(int, int, int, int, int, int, int, int, int, int) = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
+ params[8], params[9]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 11 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 12
+ case 11:
+ {
+ int (*func)(int, int, int, int, int, int, int, int, int, int, int) = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
+ params[8], params[9], params[10]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 12 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 13
+ case 12:
+ {
+ int (*func)(int, int, int, int, int, int, int, int, int, int, int, int) = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
+ params[8], params[9], params[10], params[11]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 13 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 14
+ case 13:
+ {
+ int (*func)(int, int, int, int, int, int, int, int, int, int, int, int, int) = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
+ params[8], params[9], params[10], params[11], params[12]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 14 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 15
+ case 14:
+ {
+ int (*func)(int, int, int, int, int, int, int, int, int, int, int, int, int, int)
+ = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
+ params[8], params[9], params[10], params[11], params[12], params[13]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 15 */
+#if SHELL_PARAMETER_MAX_NUMBER >= 16
+ case 15:
+ {
+ int (*func)(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int)
+ = command->data.cmd.function;
+ ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
+ params[8], params[9], params[10], params[11], params[12], params[13], params[14]);
+ break;
+ }
+#endif /** SHELL_PARAMETER_MAX_NUMBER >= 16 */
+ default:
+ ret = -1;
+ break;
+ }
+
+#if SHELL_USING_FUNC_SIGNATURE == 1
+ if (command->data.cmd.signature != NULL) {
+ index = 0;
+ for (int i = 0; i < argc - 1; i++)
+ {
+ index = shellGetNextParamType(command->data.cmd.signature, index, type);
+ shellExtCleanerPara(shell, type, params[i]);
+ }
+ }
+#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
+
+ return ret;
+}
+
diff --git a/common/letter-shell-master/src/shell_ext.h b/common/letter-shell-master/src/shell_ext.h
new file mode 100644
index 0000000..e379464
--- /dev/null
+++ b/common/letter-shell-master/src/shell_ext.h
@@ -0,0 +1,36 @@
+/**
+ * @file shell_ext.h
+ * @author Letter (NevermindZZT@gmail.com)
+ * @brief shell extensions
+ * @version 3.0.0
+ * @date 2019-12-31
+ *
+ * @copyright (c) 2019 Letter
+ *
+ */
+
+#ifndef __SHELL_EXT_H__
+#define __SHELL_EXT_H__
+
+#include "shell.h"
+
+/**
+ * @brief 鏁板瓧绫诲瀷
+ *
+ */
+typedef enum
+{
+ NUM_TYPE_DEC, /**< 鍗佽繘鍒舵暣鍨 */
+ NUM_TYPE_BIN, /**< 浜岃繘鍒舵暣鍨 */
+ NUM_TYPE_OCT, /**< 鍏繘鍒舵暣鍨 */
+ NUM_TYPE_HEX, /**< 鍗佸叚杩涘埗鏁村瀷 */
+ NUM_TYPE_FLOAT /**< 娴偣鍨 */
+} ShellNumType;
+
+int shellExtParsePara(Shell *shell, char *string, char *type, unsigned int *result);
+#if SHELL_USING_FUNC_SIGNATURE == 1
+int shellExtCleanerPara(Shell *shell, char *type, unsigned int param);
+#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
+int shellExtRun(Shell *shell, ShellCommand *command, int argc, char *argv[]);
+
+#endif
diff --git a/common/letter-shell-master/src/shell_port.c b/common/letter-shell-master/src/shell_port.c
new file mode 100644
index 0000000..6c0d7ba
--- /dev/null
+++ b/common/letter-shell-master/src/shell_port.c
@@ -0,0 +1,77 @@
+/**
+ * @file shell_port.c
+ * @author Letter (NevermindZZT@gmail.com)
+ * @brief
+ * @version 0.1
+ * @date 2019-02-22
+ *
+ * @copyright (c) 2019 Letter
+ *
+ */
+
+#include "shell.h"
+#include "bsp_uart.h"
+#include "log.h"
+#include "stdbool.h"
+
+
+#define SHELL_UART USART1
+#define SHELL_BUF_LENGTH 1024
+Shell shell;
+
+char shellBuffer[SHELL_BUF_LENGTH];
+
+void Uart_Log_Write(char *buffer, short len);
+
+
+// 瀹氫箟log瀵硅薄
+Log uartLog = {
+ .write = Uart_Log_Write,
+ .active = LOG_ENABLE,
+ .level = LOG_ALL
+
+};
+
+
+/**
+ * @brief 鐢ㄦ埛shell鍐
+ *
+ * @param data 鏁版嵁
+ * @param len 鏁版嵁闀垮害
+ *
+ * @return unsigned short 鍐欏叆瀹為檯闀垮害
+ */
+signed short userShellWrite(char *data, unsigned short len)
+{
+ BSP_Uart3_Send_Data(data, len);
+ return len;
+}
+
+// 瀹炵幇log鍐檅uffer鍑芥暟
+void Uart_Log_Write(char *buffer, short len)
+{
+ if (uartLog.shell)
+ {
+//
+// log宸ュ叿鍙互缁撳悎letter shell鐨勫熬琛屾ā寮忥紝瀹炵幇log鍜宻hell鍏辩敤涓涓粓绔紝浣嗕笉褰卞搷shell浜や簰浣撻獙
+ shellWriteEndLine(uartLog.shell, buffer, len);
+ }
+// UART1_SendString((uint8_t *)buffer, len);
+}
+
+
+
+/**
+ * @brief 鐢ㄦ埛shell鍒濆鍖
+ *
+ */
+void userShellInit(void)
+{
+ shell.write = userShellWrite;
+
+ shellInit(&shell, shellBuffer, SHELL_BUF_LENGTH);
+
+ logRegister(&uartLog, &shell);
+}
+
+
diff --git a/common/letter-shell-master/src/shell_port.h b/common/letter-shell-master/src/shell_port.h
new file mode 100644
index 0000000..0fd47ce
--- /dev/null
+++ b/common/letter-shell-master/src/shell_port.h
@@ -0,0 +1,20 @@
+/**
+ * @file shell_port.h
+ * @author Letter (NevermindZZT@gmail.com)
+ * @brief
+ * @version 0.1
+ * @date 2019-02-22
+ *
+ * @copyright (c) 2019 Letter
+ *
+ */
+
+#ifndef __SHELL_PORT_H__
+#define __SHELL_PORT_H__
+
+#include "shell.h"
+
+extern Shell shell;
+
+void userShellInit(void);
+#endif
diff --git a/common/lwrb/lwrb.c b/common/lwrb/lwrb.c
new file mode 100644
index 0000000..9cbf42c
--- /dev/null
+++ b/common/lwrb/lwrb.c
@@ -0,0 +1,646 @@
+/**
+ * \file lwrb.c
+ * \brief Lightweight ring buffer
+ */
+
+/*
+ * Copyright (c) 2023 Tilen MAJERLE
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * This file is part of LwRB - Lightweight ring buffer library.
+ *
+ * Author: Tilen MAJERLE
+ * Version: v3.0.0
+ */
+#include "lwrb.h"
+
+/* Memory set and copy functions */
+#define BUF_MEMSET memset
+#define BUF_MEMCPY memcpy
+
+#define BUF_IS_VALID(b) ((b) != NULL && (b)->buff != NULL && (b)->size > 0)
+#define BUF_MIN(x, y) ((x) < (y) ? (x) : (y))
+#define BUF_MAX(x, y) ((x) > (y) ? (x) : (y))
+#define BUF_SEND_EVT(b, type, bp) \
+ do { \
+ if ((b)->evt_fn != NULL) { \
+ (b)->evt_fn((void*)(b), (type), (bp)); \
+ } \
+ } while (0)
+
+/* Optional atomic opeartions */
+#ifdef LWRB_DISABLE_ATOMIC
+#define LWRB_INIT(var, val) (var) = (val)
+#define LWRB_LOAD(var, type) (var)
+#define LWRB_STORE(var, val, type) (var) = (val)
+#else
+#define LWRB_INIT(var, val) atomic_init(&(var), (val))
+#define LWRB_LOAD(var, type) atomic_load_explicit(&(var), (type))
+#define LWRB_STORE(var, val, type) atomic_store_explicit(&(var), (val), (type))
+#endif
+
+/**
+ * \brief Initialize buffer handle to default values with size and buffer data array
+ * \param[in] buff: Ring buffer instance
+ * \param[in] buffdata: Pointer to memory to use as buffer data
+ * \param[in] size: Size of `buffdata` in units of bytes
+ * Maximum number of bytes buffer can hold is `size - 1`
+ * \return `1` on success, `0` otherwise
+ */
+uint8_t
+lwrb_init(lwrb_t* buff, void* buffdata, lwrb_sz_t size) {
+ if (buff == NULL || buffdata == NULL || size == 0) {
+ return 0;
+ }
+
+ buff->evt_fn = NULL;
+ buff->size = size;
+ buff->buff = buffdata;
+ LWRB_INIT(buff->w, 0);
+ LWRB_INIT(buff->r, 0);
+ return 1;
+}
+
+/**
+ * \brief Check if buff is initialized and ready to use
+ * \param[in] buff: Ring buffer instance
+ * \return `1` if ready, `0` otherwise
+ */
+uint8_t
+lwrb_is_ready(lwrb_t* buff) {
+ return BUF_IS_VALID(buff);
+}
+
+/**
+ * \brief Free buffer memory
+ * \note Since implementation does not use dynamic allocation,
+ * it just sets buffer handle to `NULL`
+ * \param[in] buff: Ring buffer instance
+ */
+void
+lwrb_free(lwrb_t* buff) {
+ if (BUF_IS_VALID(buff)) {
+ buff->buff = NULL;
+ }
+}
+
+/**
+ * \brief Set event function callback for different buffer operations
+ * \param[in] buff: Ring buffer instance
+ * \param[in] evt_fn: Callback function
+ */
+void
+lwrb_set_evt_fn(lwrb_t* buff, lwrb_evt_fn evt_fn) {
+ if (BUF_IS_VALID(buff)) {
+ buff->evt_fn = evt_fn;
+ }
+}
+
+/**
+ * \brief Write data to buffer.
+ * Copies data from `data` array to buffer and marks buffer as full for maximum `btw` number of bytes
+ *
+ * \param[in] buff: Ring buffer instance
+ * \param[in] data: Pointer to data to write into buffer
+ * \param[in] btw: Number of bytes to write
+ * \return Number of bytes written to buffer.
+ * When returned value is less than `btw`, there was no enough memory available
+ * to copy full data array.
+ */
+lwrb_sz_t
+lwrb_write(lwrb_t* buff, const void* data, lwrb_sz_t btw) {
+ lwrb_sz_t written = 0;
+
+ if (lwrb_write_ex(buff, data, btw, &written, 0)) {
+ return written;
+ }
+ return 0;
+}
+
+/**
+ * \brief Write extended functionality
+ *
+ * \param buff: Ring buffer instance
+ * \param data: Pointer to data to write into buffer
+ * \param btw: Number of bytes to write
+ * \param bw: Output pointer to write number of bytes written
+ * \param Flag_ts: Optional Flag_ts.
+ * \ref LWRB_Flag_t_WRITE_ALL: Request to write all data (up to btw).
+ * Will early return if no memory available
+ * \return `1` if write operation OK, `0` otherwise
+ */
+uint8_t
+lwrb_write_ex(lwrb_t* buff, const void* data, lwrb_sz_t btw, lwrb_sz_t* bw, uint16_t Flag_ts) {
+ lwrb_sz_t tocopy, free, buff_w_ptr;
+ const uint8_t* d = data;
+
+ if (!BUF_IS_VALID(buff) || data == NULL || btw == 0) {
+ return 0;
+ }
+
+ /* Calculate maximum number of bytes available to write */
+ free = lwrb_get_free(buff);
+ /* If no memory, or if user wants to write ALL data but no enough space, exit early */
+ if (free == 0 || (free < btw && (Flag_ts & LWRB_Flag_t_WRITE_ALL)))
+ {
+ return 0;
+ }
+ btw = BUF_MIN(free, btw);
+ buff_w_ptr = LWRB_LOAD(buff->w, memory_order_acquire);
+
+ /* Step 1: Write data to linear part of buffer */
+ tocopy = BUF_MIN(buff->size - buff_w_ptr, btw);
+ BUF_MEMCPY(&buff->buff[buff_w_ptr], d, tocopy);
+ buff_w_ptr += tocopy;
+ btw -= tocopy;
+
+ /* Step 2: Write data to beginning of buffer (overflow part) */
+ if (btw > 0) {
+ BUF_MEMCPY(buff->buff, &d[tocopy], btw);
+ buff_w_ptr = btw;
+ }
+
+ /* Step 3: Check end of buffer */
+ if (buff_w_ptr >= buff->size) {
+ buff_w_ptr = 0;
+ }
+
+ /*
+ * Write final value to the actual running variable.
+ * This is to ensure no read operation can access intermediate data
+ */
+ LWRB_STORE(buff->w, buff_w_ptr, memory_order_release);
+
+ BUF_SEND_EVT(buff, LWRB_EVT_WRITE, tocopy + btw);
+ if (bw != NULL) {
+ *bw = tocopy + btw;
+ }
+ return 1;
+}
+
+/**
+ * \brief Read data from buffer.
+ * Copies data from buffer to `data` array and marks buffer as free for maximum `btr` number of bytes
+ *
+ * \param[in] buff: Ring buffer instance
+ * \param[out] data: Pointer to output memory to copy buffer data to
+ * \param[in] btr: Number of bytes to read
+ * \return Number of bytes read and copied to data array
+ */
+lwrb_sz_t
+lwrb_read(lwrb_t* buff, void* data, lwrb_sz_t btr) {
+ lwrb_sz_t read = 0;
+
+ if (lwrb_read_ex(buff, data, btr, &read, 0)) {
+ return read;
+ }
+ return 0;
+}
+
+/**
+ * \brief Write extended functionality
+ *
+ * \param buff: Ring buffer instance
+ * \param data: Pointer to memory to write read data from buffer
+ * \param btr: Number of bytes to read
+ * \param br: Output pointer to write number of bytes read
+ * \param Flag_ts: Optional Flag_ts
+ * \ref LWRB_Flag_t_READ_ALL: Request to read all data (up to btr).
+ * Will early return if no enough bytes in the buffer
+ * \return `1` if read operation OK, `0` otherwise
+ */
+uint8_t
+lwrb_read_ex(lwrb_t* buff, void* data, lwrb_sz_t btr, lwrb_sz_t* br, uint16_t Flag_ts) {
+ lwrb_sz_t tocopy, full, buff_r_ptr;
+ uint8_t* d = data;
+
+ if (!BUF_IS_VALID(buff) || data == NULL || btr == 0) {
+ return 0;
+ }
+
+ /* Calculate maximum number of bytes available to read */
+ full = lwrb_get_full(buff);
+ if (full == 0 || (full < btr && (Flag_ts & LWRB_Flag_t_READ_ALL))) {
+ return 0;
+ }
+ btr = BUF_MIN(full, btr);
+ buff_r_ptr = LWRB_LOAD(buff->r, memory_order_acquire);
+
+ /* Step 1: Read data from linear part of buffer */
+ tocopy = BUF_MIN(buff->size - buff_r_ptr, btr);
+ BUF_MEMCPY(d, &buff->buff[buff_r_ptr], tocopy);
+ buff_r_ptr += tocopy;
+ btr -= tocopy;
+
+ /* Step 2: Read data from beginning of buffer (overflow part) */
+ if (btr > 0) {
+ BUF_MEMCPY(&d[tocopy], buff->buff, btr);
+ buff_r_ptr = btr;
+ }
+
+ /* Step 3: Check end of buffer */
+ if (buff_r_ptr >= buff->size) {
+ buff_r_ptr = 0;
+ }
+
+ /*
+ * Write final value to the actual running variable.
+ * This is to ensure no write operation can access intermediate data
+ */
+ LWRB_STORE(buff->r, buff_r_ptr, memory_order_release);
+
+ BUF_SEND_EVT(buff, LWRB_EVT_READ, tocopy + btr);
+ if (br != NULL) {
+ *br = tocopy + btr;
+ }
+ return 1;
+}
+
+/**
+ * \brief Read from buffer without changing read pointer (peek only)
+ * \param[in] buff: Ring buffer instance
+ * \param[in] skip_count: Number of bytes to skip before reading data
+ * \param[out] data: Pointer to output memory to copy buffer data to
+ * \param[in] btp: Number of bytes to peek
+ * \return Number of bytes peeked and written to output array
+ */
+lwrb_sz_t
+lwrb_peek(const lwrb_t* buff, lwrb_sz_t skip_count, void* data, lwrb_sz_t btp) {
+ lwrb_sz_t full, tocopy, r;
+ uint8_t* d = data;
+
+ if (!BUF_IS_VALID(buff) || data == NULL || btp == 0) {
+ return 0;
+ }
+
+ /*
+ * Calculate maximum number of bytes available to read
+ * and check if we can even fit to it
+ */
+ full = lwrb_get_full(buff);
+ if (skip_count >= full) {
+ return 0;
+ }
+ r = LWRB_LOAD(buff->r, memory_order_relaxed);
+ r += skip_count;
+ full -= skip_count;
+ if (r >= buff->size) {
+ r -= buff->size;
+ }
+
+ /* Check maximum number of bytes available to read after skip */
+ btp = BUF_MIN(full, btp);
+ if (btp == 0) {
+ return 0;
+ }
+
+ /* Step 1: Read data from linear part of buffer */
+ tocopy = BUF_MIN(buff->size - r, btp);
+ BUF_MEMCPY(d, &buff->buff[r], tocopy);
+ btp -= tocopy;
+
+ /* Step 2: Read data from beginning of buffer (overflow part) */
+ if (btp > 0) {
+ BUF_MEMCPY(&d[tocopy], buff->buff, btp);
+ }
+ return tocopy + btp;
+}
+
+/**
+ * \brief Get available size in buffer for write operation
+ * \param[in] buff: Ring buffer instance
+ * \return Number of free bytes in memory
+ */
+lwrb_sz_t
+lwrb_get_free(const lwrb_t* buff) {
+ lwrb_sz_t size, w, r;
+
+ if (!BUF_IS_VALID(buff)) {
+ return 0;
+ }
+
+ /*
+ * Copy buffer pointers to local variables with atomic access.
+ *
+ * To ensure thread safety (only when in single-entry, single-exit FIFO mode use case),
+ * it is important to write buffer r and w values to local w and r variables.
+ *
+ * Local variables will ensure below if statements will always use the same value,
+ * even if buff->w or buff->r get changed during interrupt processing.
+ *
+ * They may change during load operation, important is that
+ * they do not change during if-elseif-else operations following these assignments.
+ *
+ * lwrb_get_free is only called for write purpose, and when in FIFO mode, then:
+ * - buff->w pointer will not change by another process/interrupt because we are in write mode just now
+ * - buff->r pointer may change by another process. If it gets changed after buff->r has been loaded to local variable,
+ * buffer will see "free size" less than it actually is. This is not a problem, application can
+ * always try again to write more data to remaining free memory that was read just during copy operation
+ */
+ w = LWRB_LOAD(buff->w, memory_order_relaxed);
+ r = LWRB_LOAD(buff->r, memory_order_relaxed);
+
+ if (w == r) {
+ size = buff->size;
+ } else if (r > w) {
+ size = r - w;
+ } else {
+ size = buff->size - (w - r);
+ }
+
+ /* Buffer free size is always 1 less than actual size */
+ return size - 1;
+}
+
+/**
+ * \brief Get number of bytes currently available in buffer
+ * \param[in] buff: Ring buffer instance
+ * \return Number of bytes ready to be read
+ */
+lwrb_sz_t
+lwrb_get_full(const lwrb_t* buff) {
+ lwrb_sz_t size, w, r;
+
+ if (!BUF_IS_VALID(buff)) {
+ return 0;
+ }
+
+ /*
+ * Copy buffer pointers to local variables.
+ *
+ * To ensure thread safety (only when in single-entry, single-exit FIFO mode use case),
+ * it is important to write buffer r and w values to local w and r variables.
+ *
+ * Local variables will ensure below if statements will always use the same value,
+ * even if buff->w or buff->r get changed during interrupt processing.
+ *
+ * They may change during load operation, important is that
+ * they do not change during if-elseif-else operations following these assignments.
+ *
+ * lwrb_get_full is only called for read purpose, and when in FIFO mode, then:
+ * - buff->r pointer will not change by another process/interrupt because we are in read mode just now
+ * - buff->w pointer may change by another process. If it gets changed after buff->w has been loaded to local variable,
+ * buffer will see "full size" less than it really is. This is not a problem, application can
+ * always try again to read more data from remaining full memory that was written just during copy operation
+ */
+ w = LWRB_LOAD(buff->w, memory_order_relaxed);
+ r = LWRB_LOAD(buff->r, memory_order_relaxed);
+
+ if (w == r) {
+ size = 0;
+ } else if (w > r) {
+ size = w - r;
+ } else {
+ size = buff->size - (r - w);
+ }
+ return size;
+}
+
+/**
+ * \brief Resets buffer to default values. Buffer size is not modified
+ * \note This function is not thread safe.
+ * When used, application must ensure there is no active read/write operation
+ * \param[in] buff: Ring buffer instance
+ */
+void
+lwrb_reset(lwrb_t* buff) {
+ if (BUF_IS_VALID(buff)) {
+ LWRB_STORE(buff->w, 0, memory_order_release);
+ LWRB_STORE(buff->r, 0, memory_order_release);
+ BUF_SEND_EVT(buff, LWRB_EVT_RESET, 0);
+ }
+}
+
+/**
+ * \brief Get linear address for buffer for fast read
+ * \param[in] buff: Ring buffer instance
+ * \return Linear buffer start address
+ */
+void*
+lwrb_get_linear_block_read_address(const lwrb_t* buff) {
+ if (!BUF_IS_VALID(buff)) {
+ return NULL;
+ }
+ return &buff->buff[buff->r];
+}
+
+/**
+ * \brief Get length of linear block address before it overflows for read operation
+ * \param[in] buff: Ring buffer instance
+ * \return Linear buffer size in units of bytes for read operation
+ */
+lwrb_sz_t
+lwrb_get_linear_block_read_length(const lwrb_t* buff) {
+ lwrb_sz_t len, w, r;
+
+ if (!BUF_IS_VALID(buff)) {
+ return 0;
+ }
+
+ /*
+ * Use temporary values in case they are changed during operations.
+ * See lwrb_buff_free or lwrb_buff_full functions for more information why this is OK.
+ */
+ w = LWRB_LOAD(buff->w, memory_order_relaxed);
+ r = LWRB_LOAD(buff->r, memory_order_relaxed);
+
+ if (w > r) {
+ len = w - r;
+ } else if (r > w) {
+ len = buff->size - r;
+ } else {
+ len = 0;
+ }
+ return len;
+}
+
+/**
+ * \brief Skip (ignore; advance read pointer) buffer data
+ * Marks data as read in the buffer and increases free memory for up to `len` bytes
+ *
+ * \note Useful at the end of streaming transfer such as DMA
+ * \param[in] buff: Ring buffer instance
+ * \param[in] len: Number of bytes to skip and mark as read
+ * \return Number of bytes skipped
+ */
+lwrb_sz_t
+lwrb_skip(lwrb_t* buff, lwrb_sz_t len) {
+ lwrb_sz_t full, r;
+
+ if (!BUF_IS_VALID(buff) || len == 0) {
+ return 0;
+ }
+
+ full = lwrb_get_full(buff);
+ len = BUF_MIN(len, full);
+ r = LWRB_LOAD(buff->r, memory_order_acquire);
+ r += len;
+ if (r >= buff->size) {
+ r -= buff->size;
+ }
+ LWRB_STORE(buff->r, r, memory_order_release);
+ BUF_SEND_EVT(buff, LWRB_EVT_READ, len);
+ return len;
+}
+
+/**
+ * \brief Get linear address for buffer for fast read
+ * \param[in] buff: Ring buffer instance
+ * \return Linear buffer start address
+ */
+void*
+lwrb_get_linear_block_write_address(const lwrb_t* buff) {
+ if (!BUF_IS_VALID(buff)) {
+ return NULL;
+ }
+ return &buff->buff[buff->w];
+}
+
+/**
+ * \brief Get length of linear block address before it overflows for write operation
+ * \param[in] buff: Ring buffer instance
+ * \return Linear buffer size in units of bytes for write operation
+ */
+lwrb_sz_t
+lwrb_get_linear_block_write_length(const lwrb_t* buff) {
+ lwrb_sz_t len, w, r;
+
+ if (!BUF_IS_VALID(buff)) {
+ return 0;
+ }
+
+ /*
+ * Use temporary values in case they are changed during operations.
+ * See lwrb_buff_free or lwrb_buff_full functions for more information why this is OK.
+ */
+ w = LWRB_LOAD(buff->w, memory_order_relaxed);
+ r = LWRB_LOAD(buff->r, memory_order_relaxed);
+
+ if (w >= r) {
+ len = buff->size - w;
+ /*
+ * When read pointer is 0,
+ * maximal length is one less as if too many bytes
+ * are written, buffer would be considered empty again (r == w)
+ */
+ if (r == 0) {
+ /*
+ * Cannot overflow:
+ * - If r is not 0, statement does not get called
+ * - buff->size cannot be 0 and if r is 0, len is greater 0
+ */
+ --len;
+ }
+ } else {
+ len = r - w - 1;
+ }
+ return len;
+}
+
+/**
+ * \brief Advance write pointer in the buffer.
+ * Similar to skip function but modifies write pointer instead of read
+ *
+ * \note Useful when hardware is writing to buffer and application needs to increase number
+ * of bytes written to buffer by hardware
+ * \param[in] buff: Ring buffer instance
+ * \param[in] len: Number of bytes to advance
+ * \return Number of bytes advanced for write operation
+ */
+lwrb_sz_t
+lwrb_advance(lwrb_t* buff, lwrb_sz_t len) {
+ lwrb_sz_t free, w;
+
+ if (!BUF_IS_VALID(buff) || len == 0) {
+ return 0;
+ }
+
+ /* Use local variables before writing back to main structure */
+ free = lwrb_get_free(buff);
+ len = BUF_MIN(len, free);
+ w = LWRB_LOAD(buff->w, memory_order_acquire);
+ w += len;
+ if (w >= buff->size) {
+ w -= buff->size;
+ }
+ LWRB_STORE(buff->w, w, memory_order_release);
+ BUF_SEND_EVT(buff, LWRB_EVT_WRITE, len);
+ return len;
+}
+
+/**
+ * \brief Searches for a *needle* in an array, starting from given offset.
+ *
+ * \note This function is not thread-safe.
+ *
+ * \param buff: Ring buffer to search for needle in
+ * \param bts: Constant byte array sequence to search for in a buffer
+ * \param len: Length of the \arg bts array
+ * \param start_offset: Start offset in the buffer
+ * \param found_idx: Pointer to variable to write index in array where bts has been found
+ * Must not be set to `NULL`
+ * \return `1` if \arg bts found, `0` otherwise
+ */
+uint8_t
+lwrb_find(const lwrb_t* buff, const void* bts, lwrb_sz_t len, lwrb_sz_t start_offset, lwrb_sz_t* found_idx) {
+ lwrb_sz_t full, r, max_x;
+ uint8_t found = 0;
+ const uint8_t* needle = bts;
+
+ if (!BUF_IS_VALID(buff) || needle == NULL || len == 0 || found_idx == NULL) {
+ return 0;
+ }
+ *found_idx = 0;
+
+ full = lwrb_get_full(buff);
+ /* Verify initial conditions */
+ if (full < (len + start_offset)) {
+ return 0;
+ }
+
+ /* Max number of for loops is buff_full - input_len - start_offset of buffer length */
+ max_x = full - len;
+ for (lwrb_sz_t skip_x = start_offset; !found && skip_x <= max_x; ++skip_x) {
+ found = 1; /* Found by default */
+
+ /* Prepare the starting point for reading */
+ r = buff->r + skip_x;
+ if (r >= buff->size) {
+ r -= buff->size;
+ }
+
+ /* Search in the buffer */
+ for (lwrb_sz_t i = 0; i < len; ++i) {
+ if (buff->buff[r] != needle[i]) {
+ found = 0;
+ break;
+ }
+ if (++r >= buff->size) {
+ r = 0;
+ }
+ }
+ if (found) {
+ *found_idx = skip_x;
+ }
+ }
+ return found;
+}
diff --git a/common/lwrb/lwrb.h b/common/lwrb/lwrb.h
new file mode 100644
index 0000000..a9522bf
--- /dev/null
+++ b/common/lwrb/lwrb.h
@@ -0,0 +1,157 @@
+/**
+ * \file lwrb.h
+ * \brief LwRB - Lightweight ring buffer
+ */
+
+/*
+ * Copyright (c) 2023 Tilen MAJERLE
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * This file is part of LwRB - Lightweight ring buffer library.
+ *
+ * Author: Tilen MAJERLE
+ * Version: v3.0.0-rc1
+ */
+#ifndef LWRB_HDR_H
+#define LWRB_HDR_H
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * \defgroup LWRB Lightweight ring buffer manager
+ * \brief Lightweight ring buffer manager
+ * \{
+ */
+
+#if !defined(LWRB_DISABLE_ATOMIC) || __DOXYGEN__
+// #include
+
+#define LWRB_DISABLE_ATOMIC
+#ifdef LWRB_DISABLE_ATOMIC
+typedef unsigned long lwrb_sz_atomic_t;
+#else
+#include
+typedef atomic_ulong lwrb_sz_atomic_t;
+#endif
+
+/**
+ * \brief Atomic type for size variable.
+ * Default value is set to be `unsigned 32-bits` type
+ */
+//typedef atomic_ulong lwrb_sz_atomic_t;
+
+/**
+ * \brief Size variable for all library operations.
+ * Default value is set to be `unsigned 32-bits` type
+ */
+typedef unsigned long lwrb_sz_t;
+#else
+typedef unsigned long lwrb_sz_atomic_t;
+typedef unsigned long lwrb_sz_t;
+#endif
+
+/**
+ * \brief Event type for buffer operations
+ */
+typedef enum {
+ LWRB_EVT_READ, /*!< Read event */
+ LWRB_EVT_WRITE, /*!< Write event */
+ LWRB_EVT_RESET, /*!< Reset event */
+} lwrb_evt_type_t;
+
+/**
+ * \brief Buffer structure forward declaration
+ */
+struct lwrb;
+
+/**
+ * \brief Event callback function type
+ * \param[in] buff: Buffer handle for event
+ * \param[in] evt: Event type
+ * \param[in] bp: Number of bytes written or read (when used), depends on event type
+ */
+typedef void (*lwrb_evt_fn)(struct lwrb* buff, lwrb_evt_type_t evt, lwrb_sz_t bp);
+
+/* List of Flag_ts */
+#define LWRB_Flag_t_READ_ALL ((uint16_t)0x0001)
+#define LWRB_Flag_t_WRITE_ALL ((uint16_t)0x0001)
+
+/**
+ * \brief Buffer structure
+ */
+typedef struct lwrb {
+ uint8_t* buff; /*!< Pointer to buffer data. Buffer is considered initialized when `buff != NULL` and `size > 0` */
+ lwrb_sz_t size; /*!< Size of buffer data. Size of actual buffer is `1` byte less than value holds */
+ lwrb_sz_atomic_t r; /*!< Next read pointer. Buffer is considered empty when `r == w` and full when `w == r - 1` */
+ lwrb_sz_atomic_t w; /*!< Next write pointer. Buffer is considered empty when `r == w` and full when `w == r - 1` */
+ lwrb_evt_fn evt_fn; /*!< Pointer to event callback function */
+} lwrb_t;
+
+uint8_t lwrb_init(lwrb_t* buff, void* buffdata, lwrb_sz_t size);
+uint8_t lwrb_is_ready(lwrb_t* buff);
+void lwrb_free(lwrb_t* buff);
+void lwrb_reset(lwrb_t* buff);
+void lwrb_set_evt_fn(lwrb_t* buff, lwrb_evt_fn fn);
+
+/* Read/Write functions */
+lwrb_sz_t lwrb_write(lwrb_t* buff, const void* data, lwrb_sz_t btw);
+lwrb_sz_t lwrb_read(lwrb_t* buff, void* data, lwrb_sz_t btr);
+lwrb_sz_t lwrb_peek(const lwrb_t* buff, lwrb_sz_t skip_count, void* data, lwrb_sz_t btp);
+
+/* Extended read/write functions */
+uint8_t lwrb_write_ex(lwrb_t* buff, const void* data, lwrb_sz_t btw, lwrb_sz_t* bw, uint16_t Flag_ts);
+uint8_t lwrb_read_ex(lwrb_t* buff, void* data, lwrb_sz_t btr, lwrb_sz_t* br, uint16_t Flag_ts);
+
+/* Buffer size information */
+lwrb_sz_t lwrb_get_free(const lwrb_t* buff);
+lwrb_sz_t lwrb_get_full(const lwrb_t* buff);
+
+/* Read data block management */
+void* lwrb_get_linear_block_read_address(const lwrb_t* buff);
+lwrb_sz_t lwrb_get_linear_block_read_length(const lwrb_t* buff);
+lwrb_sz_t lwrb_skip(lwrb_t* buff, lwrb_sz_t len);
+
+/* Write data block management */
+void* lwrb_get_linear_block_write_address(const lwrb_t* buff);
+lwrb_sz_t lwrb_get_linear_block_write_length(const lwrb_t* buff);
+lwrb_sz_t lwrb_advance(lwrb_t* buff, lwrb_sz_t len);
+
+/* Search in buffer */
+uint8_t lwrb_find(const lwrb_t* buff, const void* bts, lwrb_sz_t len, lwrb_sz_t start_offset, lwrb_sz_t* found_idx);
+lwrb_sz_t lwrb_overwrite(lwrb_t* buff, const void* data, lwrb_sz_t btw);
+lwrb_sz_t lwrb_move(lwrb_t* dest, lwrb_t* src);
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LWRB_HDR_H */