diff --git a/.cproject b/.cproject
index af464b6..e8a3a08 100644
--- a/.cproject
+++ b/.cproject
@@ -45,13 +45,13 @@
-
-
-
-
-
+
+
+
+
+
-
+
@@ -60,6 +60,9 @@
@@ -144,6 +147,8 @@
+
+
@@ -165,5 +170,5 @@
-
+
diff --git a/.project b/.project
index 87e00ab..2aa28b2 100644
--- a/.project
+++ b/.project
@@ -1,7 +1,7 @@
-
+
BLE_TYQ_BJQ_CH584M
-
+
@@ -26,7 +26,7 @@
1602468250279
-
+
22
org.eclipse.ui.ide.multiFilter
diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml
index 4e6ba13..9c6a2bd 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..52b711d
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,13 @@
+eclipse.preferences.version=1
+encoding//APP/multiCentral.c=UTF-8
+encoding//APP/multiCentral_main.c=GBK
+encoding//HAL/MCU.c=GBK
+encoding//HAL/include/HAL.h=GBK
+encoding//LIB/CH58xBLE_LIB.h=GBK
+encoding//Ld/Link.ld=GBK
+encoding//StdPeriphDriver/CH58x_gpio.c=GBK
+encoding//StdPeriphDriver/CH58x_sys.c=GBK
+encoding//StdPeriphDriver/CH58x_uart3.c=GBK
+encoding//StdPeriphDriver/inc/CH585SFR.h=GBK
+encoding//StdPeriphDriver/inc/CH58x_gpio.h=GBK
+encoding//bsp/src/bsp_flash.c=GBK
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..351511d
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,23 @@
+{
+ "MicroPython.executeButton": [
+ {
+ "text": "▶",
+ "tooltip": "运行",
+ "alignment": "left",
+ "command": "extension.executeFile",
+ "priority": 3.5
+ }
+ ],
+ "MicroPython.syncButton": [
+ {
+ "text": "$(sync)",
+ "tooltip": "同步",
+ "alignment": "left",
+ "command": "extension.execute",
+ "priority": 4
+ }
+ ],
+ "files.associations": {
+ "config.h": "c"
+ }
+}
\ No newline at end of file
diff --git a/APP/include/multiCentral.h b/APP/include/multiCentral.h
index 64ff517..adcfc21 100644
--- a/APP/include/multiCentral.h
+++ b/APP/include/multiCentral.h
@@ -1,3 +1,13 @@
+/***
+ * @Author: mbw
+ * @Date: 2024-12-03 11:13:13
+ * @LastEditors: mbw && 1600520629@qq.com
+ * @LastEditTime: 2024-12-06 14:47:52
+ * @FilePath: \ble_-tyq_-bjq_-ch584-m\APP\include\multiCentral.h
+ * @Description:
+ * @
+ * @Copyright (c) 2024 by ${git_name_email}, All Rights Reserved.
+ */
/********************************** (C) COPYRIGHT *******************************
* File Name : multiCentral.h
* Author : WCH
@@ -26,19 +36,34 @@ extern "C" {
*/
// Simple BLE Observer Task Events
+// 定义设备启动事件的常量
#define START_DEVICE_EVT 0x0001
+// 定义启动发现过程事件的常量
#define START_DISCOVERY_EVT 0x0002
+// 定义启动扫描过程事件的常量
#define START_SCAN_EVT 0x0004
+// 定义启动服务发现事件的常量
#define START_SVC_DISCOVERY_EVT 0x0008
+// 定义启动参数更新事件的常量
#define START_PARAM_UPDATE_EVT 0x0010
+// 定义启动读或写操作事件的常量
#define START_READ_OR_WRITE_EVT 0x0020
+// 定义启动读取RSSI事件的常量
#define START_READ_RSSI_EVT 0x0040
+// 定义建立连接超时事件的常量
#define ESTABLISH_LINK_TIMEOUT_EVT 0x0080
+// 定义启动写入CCCD(客户端特性配置描述符)事件的常量
#define START_WRITE_CCCD_EVT 0x0100
#define CONNECT0_ITEM 0
-#define CONNECT1_ITEM 1
-#define CONNECT2_ITEM 2
+ #define CONNECT1_ITEM 1
+// #define CONNECT2_ITEM 2
+// #define CONNECT3_ITEM 3
+// #define CONNECT4_ITEM 4
+// #define CONNECT5_ITEM 5
+// #define CONNECT6_ITEM 6
+// #define CONNECT7_ITEM 7
+
/*********************************************************************
* MACROS
diff --git a/APP/multiCentral.c b/APP/multiCentral.c
index e1c28b4..a167609 100644
--- a/APP/multiCentral.c
+++ b/APP/multiCentral.c
@@ -1,20 +1,15 @@
-/********************************** (C) COPYRIGHT *******************************
-* File Name : multiCentral.c
-* Author : WCH
-* Version : V1.0
-* Date : 2018/11/12
-* Description : ̣ɨΧ豸ӻ豸ַ
-* ѰԶִждӻʹ,
- ӻ豸ַΪĿַӻ豸ַĬΪ
- (84:C2:E4:03:02:02)(84:C2:E4:03:02:03)(84:C2:E4:03:02:04)
-*******************************************************************************/
-
/*********************************************************************
* INCLUDES
*/
#include "CONFIG.h"
#include "gattprofile.h"
#include "multiCentral.h"
+#include "bsp_uart.h"
+#include "log.h"
+
+#undef LOG_ENABLE
+#define LOG_ENABLE 1
+
/*********************************************************************
* MACROS
@@ -27,19 +22,19 @@
* CONSTANTS
*/
// Maximum number of scan responses
-#define DEFAULT_MAX_SCAN_RES 10
+#define DEFAULT_MAX_SCAN_RES 10
-// Scan duration in 0.625ms
+// Scan duration in 0.625ms 扫描窗口要小于等于扫描间隔
#define DEFAULT_SCAN_DURATION 2400
// Connection min interval in 1.25ms
-#define DEFAULT_MIN_CONNECTION_INTERVAL 20
+#define DEFAULT_MIN_CONNECTION_INTERVAL 20 //25ms连接一次 范围为7.5ms~4s之间
// Connection max interval in 1.25ms
-#define DEFAULT_MAX_CONNECTION_INTERVAL 100
+#define DEFAULT_MAX_CONNECTION_INTERVAL 100 //125ms连接一次
// Connection supervision timeout in 10ms
-#define DEFAULT_CONNECTION_TIMEOUT 100
+#define DEFAULT_CONNECTION_TIMEOUT 100 //10(100ms)~3200(32s)
// Discovey mode (limited, general, all)
#define DEFAULT_DISCOVERY_MODE DEVDISC_MODE_ALL
@@ -48,6 +43,7 @@
#define DEFAULT_DISCOVERY_ACTIVE_SCAN TRUE
// TRUE to use white list during discovery
+// #define DEFAULT_DISCOVERY_WHITE_LIST FALSE
#define DEFAULT_DISCOVERY_WHITE_LIST FALSE
// TRUE to use high scan duty cycle when creating link
@@ -60,16 +56,15 @@
#define DEFAULT_RSSI_PERIOD 2400
// Minimum connection interval (units of 1.25ms)
-#define DEFAULT_UPDATE_MIN_CONN_INTERVAL 20
+#define DEFAULT_UPDATE_MIN_CONN_INTERVAL 100
// Maximum connection interval (units of 1.25ms)
-#define DEFAULT_UPDATE_MAX_CONN_INTERVAL 100
-
+#define DEFAULT_UPDATE_MAX_CONN_INTERVAL 400
// Slave latency to use parameter update
-#define DEFAULT_UPDATE_SLAVE_LATENCY 0
+#define DEFAULT_UPDATE_SLAVE_LATENCY 5
// Supervision timeout value (units of 10ms)
-#define DEFAULT_UPDATE_CONN_TIMEOUT 600
+#define DEFAULT_UPDATE_CONN_TIMEOUT 200
// Default passcode
#define DEFAULT_PASSCODE 0
@@ -93,7 +88,7 @@
#define DEFAULT_PARAM_UPDATE_DELAY 3200
// Default read or write timer delay in 0.625ms
-#define DEFAULT_READ_OR_WRITE_DELAY 1600
+#define DEFAULT_READ_OR_WRITE_DELAY 100
// Default write CCCD delay in 0.625ms
#define DEFAULT_WRITE_CCCD_DELAY 1600
@@ -149,16 +144,16 @@ static gapDevRec_t centralDevList[DEFAULT_MAX_SCAN_RES];
// Peer device address
static peerAddrDefItem_t PeerAddrDef[CENTRAL_MAX_CONNECTION] = {
- {0x02, 0x02, 0x03, 0xE4, 0xC2, 0x84},
- {0x03, 0x02, 0x03, 0xE4, 0xC2, 0x84},
- {0x04, 0x02, 0x03, 0xE4, 0xC2, 0x84}
+ // {0xcf, 0xb4, 0x8f, 0x10, 0x53, 0x5c},
+ {0xe1, 0X51, 0x89, 0x88, 0x19, 0x70},
+ // {0x03, 0x02, 0x03, 0xE4, 0xC2, 0x84},
};
// Connection item list
static centralConnItem_t centralConnList[CENTRAL_MAX_CONNECTION];
// Value to write
-static uint8_t centralCharVal = 0x5A;
+static uint8_t centralCharVal = 0x01;
// Value read/write toggle
static uint8_t centralDoWrite = TRUE;
@@ -181,6 +176,8 @@ static void centralAddDeviceInfo(uint8_t *pAddr, uint8_t addrType);
static void centralInitConnItem(uint8_t task_id, centralConnItem_t *centralConnList);
static uint8_t centralAddrCmp(peerAddrDefItem_t *PeerAddrDef, uint8_t *addr);
+static void centralConnIistStartDiscovery_1(void);
+static uint16_t connect1_ProcessEvent(uint8_t task_id, uint16_t events);
/*********************************************************************
* PROFILE CALLBACKS
*/
@@ -194,8 +191,8 @@ static gapCentralRoleCB_t centralRoleCB = {
// Bond Manager Callbacks
static gapBondCBs_t centralBondCB = {
- centralPasscodeCB,
- centralPairStateCB
+ centralPasscodeCB,// 连接时需要使用有密码的函数。
+ centralPairStateCB//配对时的回调函数
};
/*********************************************************************
@@ -218,21 +215,23 @@ static gapBondCBs_t centralBondCB = {
*/
void Central_Init()
{
- centralTaskId = TMOS_ProcessEventRegister(Central_ProcessEvent);
+ centralTaskId = TMOS_ProcessEventRegister(Central_ProcessEvent);//注册事件处理函数,将返回的任务赋值给变量
// Setup GAP
- GAP_SetParamValue(TGAP_DISC_SCAN, DEFAULT_SCAN_DURATION);
- GAP_SetParamValue(TGAP_CONN_EST_INT_MIN, DEFAULT_MIN_CONNECTION_INTERVAL);
- GAP_SetParamValue(TGAP_CONN_EST_INT_MAX, DEFAULT_MAX_CONNECTION_INTERVAL);
- GAP_SetParamValue(TGAP_CONN_EST_SUPERV_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);
-
+ GAP_SetParamValue(TGAP_DISC_SCAN_WIND, DEFAULT_SCAN_DURATION);//扫描窗口
+ GAP_SetParamValue(TGAP_DISC_SCAN, DEFAULT_SCAN_DURATION);//扫描连续时间,单位0.625ms 4800*0.625ms = 3000ms
+ GAP_SetParamValue(TGAP_DISC_SCAN_INT, DEFAULT_SCAN_DURATION);////扫描间隔,需要>=扫描窗口
+ GAP_SetParamValue(TGAP_CONN_EST_INT_MIN, DEFAULT_MIN_CONNECTION_INTERVAL);////设置链路层连接间隔,这里是最小连接间隔
+ GAP_SetParamValue(TGAP_CONN_EST_INT_MAX, DEFAULT_MAX_CONNECTION_INTERVAL);//最大连续间隔
+ GAP_SetParamValue(TGAP_CONN_EST_SUPERV_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);//连接超时时间
+
// Setup the GAP Bond Manager
{
- uint32_t passkey = DEFAULT_PASSCODE;
- uint8_t pairMode = DEFAULT_PAIRING_MODE;
- uint8_t mitm = DEFAULT_MITM_MODE;
- uint8_t ioCap = DEFAULT_IO_CAPABILITIES;
- uint8_t bonding = DEFAULT_BONDING_MODE;
+ uint32_t passkey = DEFAULT_PASSCODE;//PASSKEY 是在配对过程中用于验证设备身份的一种方式
+ uint8_t pairMode = DEFAULT_PAIRING_MODE;//(配对模式)配对模式定义了设备进行配对过程中的行为和要求
+ uint8_t mitm = DEFAULT_MITM_MODE;//MIMT模式定义了是否需要保护免受中间人攻击的配对过程
+ uint8_t ioCap = DEFAULT_IO_CAPABILITIES;//IO能力定义了设备对配对过程的输入输出能力
+ uint8_t bonding = DEFAULT_BONDING_MODE;//(绑定模式)绑定模式定义了设备在配对后是否应该建立安全的绑定关系
GAPBondMgr_SetParameter(GAPBOND_CENT_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey);
GAPBondMgr_SetParameter(GAPBOND_CENT_PAIRING_MODE, sizeof(uint8_t), &pairMode);
@@ -244,7 +243,7 @@ void Central_Init()
// Init Connection Item
centralInitConnItem(centralTaskId, centralConnList);
// Initialize GATT Client
- GATT_InitClient();
+ GATT_InitClient();//初始化通用规范属性客户端
// Register to receive incoming ATT Indications/Notifications
GATT_RegisterForInd(centralTaskId);
// Setup a delayed profile startup
@@ -266,7 +265,6 @@ static void centralInitConnItem(uint8_t task_id, centralConnItem_t *centralConnL
uint8_t connItem;
for(connItem = 0; connItem < CENTRAL_MAX_CONNECTION; connItem++)
{
- // ÿӵͨtaskID
centralConnList[connItem].taskID = TMOS_ProcessEventRegister(Central_ProcessEvent);
centralConnList[connItem].connHandle = GAP_CONNHANDLE_INIT;
centralConnList[connItem].state = BLE_STATE_IDLE;
@@ -285,7 +283,7 @@ static void centralInitConnItem(uint8_t task_id, centralConnItem_t *centralConnL
* @brief Central Application Task event processor. This function
* is called to process all events for the task. Events
* include timers, messages and any other user defined events.
- *
+ *中央应用程序任务事件处理器。调用此函数来处理任务的所有事件。事件包括定时器、消息和任何其他用户定义的事件。
* @param task_id - The TMOS assigned task ID.
* @param events - events to process. This is a bit map and can
* contain more than one event.
@@ -310,30 +308,29 @@ uint16_t Central_ProcessEvent(uint8_t task_id, uint16_t events)
if(events & START_DEVICE_EVT)
{
- // Start the Device
+ // Start the Device 启动设备
GAPRole_CentralStartDevice(centralTaskId, ¢ralBondCB, ¢ralRoleCB);
return (events ^ START_DEVICE_EVT);
}
-
+ //终止现有连接
if(events & ESTABLISH_LINK_TIMEOUT_EVT)
{
GAPRole_TerminateLink(INVALID_CONNHANDLE);
return (events ^ ESTABLISH_LINK_TIMEOUT_EVT);
}
- // 0
if(task_id == centralConnList[CONNECT0_ITEM].taskID)
{
return connect0_ProcessEvent(task_id, events);
}
- // 1
else if(task_id == centralConnList[CONNECT1_ITEM].taskID)
{
+ return connect1_ProcessEvent(task_id, events);
}
- // 2
- else if(task_id == centralConnList[CONNECT2_ITEM].taskID)
- {
- }
+ // else if(task_id == centralConnList[CONNECT2_ITEM].taskID)
+ // {
+ // return connect2_ProcessEvent(task_id, events);
+ // }
// Discard unknown events
return 0;
}
@@ -453,6 +450,122 @@ static uint16_t connect0_ProcessEvent(uint8_t task_id, uint16_t events)
// Discard unknown events
return 0;
}
+
+/*********************************************************************
+ * @fn connect1_ProcessEvent
+ *
+ * @brief Process an incoming task message.
+ *
+ * @param pMsg - message to process
+ *
+ * @return none
+ */
+static uint16_t connect1_ProcessEvent(uint8_t task_id, uint16_t events)
+{
+ if(events & START_SVC_DISCOVERY_EVT)
+ {
+ // start service discovery
+ centralConnIistStartDiscovery_1();
+ return (events ^ START_SVC_DISCOVERY_EVT);
+ }
+
+ if(events & START_READ_OR_WRITE_EVT)
+ {
+ if(centralConnList[CONNECT0_ITEM].procedureInProgress == FALSE)
+ {
+ if(centralDoWrite)
+ {
+ // Do a write
+ attWriteReq_t req;
+
+ req.cmd = FALSE;
+ req.sig = FALSE;
+ req.handle = centralConnList[CONNECT1_ITEM].charHdl;
+ req.len = 1;//发送的长度
+ req.pValue = GATT_bm_alloc(centralConnList[CONNECT1_ITEM].connHandle, ATT_WRITE_REQ, req.len, NULL, 0);
+ if(req.pValue != NULL)
+ {
+ *req.pValue = centralCharVal;//发送的数据
+
+ if(GATT_WriteCharValue(centralConnList[CONNECT1_ITEM].connHandle, &req, centralTaskId) == SUCCESS)//发送函数
+ {
+ centralConnList[CONNECT1_ITEM].procedureInProgress = TRUE;
+ centralDoWrite = !centralDoWrite;
+ tmos_start_task(centralConnList[CONNECT1_ITEM].taskID, START_READ_OR_WRITE_EVT, DEFAULT_READ_OR_WRITE_DELAY);
+ }
+ else
+ {
+ GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ);
+ }
+ }
+ }
+ else
+ {
+ // Do a read
+ attReadReq_t req;
+
+ req.handle = centralConnList[CONNECT1_ITEM].charHdl;
+ if(GATT_ReadCharValue(centralConnList[CONNECT1_ITEM].connHandle, &req, centralTaskId) == SUCCESS)//读取函数
+ {
+ centralConnList[CONNECT1_ITEM].procedureInProgress = TRUE;
+ centralDoWrite = !centralDoWrite;
+ }
+ }
+ }
+ return (events ^ START_READ_OR_WRITE_EVT);
+ }
+
+ if(events & START_PARAM_UPDATE_EVT)
+ {
+ // start connect parameter update
+ GAPRole_UpdateLink(centralConnList[CONNECT1_ITEM].connHandle,
+ DEFAULT_UPDATE_MIN_CONN_INTERVAL,
+ DEFAULT_UPDATE_MAX_CONN_INTERVAL,
+ DEFAULT_UPDATE_SLAVE_LATENCY,
+ DEFAULT_UPDATE_CONN_TIMEOUT);
+
+ return (events ^ START_PARAM_UPDATE_EVT);
+ }
+ if(events & START_WRITE_CCCD_EVT)
+ {
+ if(centralConnList[CONNECT1_ITEM].procedureInProgress == FALSE)
+ {
+ // Do a write
+ attWriteReq_t req;
+
+ req.cmd = FALSE;
+ req.sig = FALSE;
+ req.handle = centralConnList[CONNECT1_ITEM].cccHdl;
+ req.len = 2;
+ req.pValue = GATT_bm_alloc(centralConnList[CONNECT1_ITEM].connHandle, ATT_WRITE_REQ, req.len, NULL, 0);
+ if(req.pValue != NULL)
+ {
+ req.pValue[0] = 1;
+ req.pValue[1] = 0;
+
+ if(GATT_WriteCharValue(centralConnList[CONNECT1_ITEM].connHandle, &req, centralTaskId) == SUCCESS)
+ {
+ centralConnList[CONNECT1_ITEM].procedureInProgress = TRUE;
+ }
+ else
+ {
+ GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ);
+ }
+ }
+ }
+ return (events ^ START_WRITE_CCCD_EVT);
+ }
+
+ if(events & START_READ_RSSI_EVT)
+ {
+ GAPRole_ReadRssiCmd(centralConnList[CONNECT1_ITEM].connHandle);
+ tmos_start_task(centralConnList[CONNECT1_ITEM].taskID, START_READ_RSSI_EVT, DEFAULT_RSSI_PERIOD);
+ return (events ^ START_READ_RSSI_EVT);
+ }
+ // Discard unknown events
+ return 0;
+}
+
/*********************************************************************
* @fn central_ProcessTMOSMsg
*
@@ -501,22 +614,21 @@ static void centralProcessGATTMsg(gattMsgEvent_t *pMsg)
return;
}
- if((pMsg->method == ATT_EXCHANGE_MTU_RSP) ||
- ((pMsg->method == ATT_ERROR_RSP) &&
+ if((pMsg->method == ATT_EXCHANGE_MTU_RSP) || ((pMsg->method == ATT_ERROR_RSP) &&
(pMsg->msg.errorRsp.reqOpcode == ATT_EXCHANGE_MTU_REQ)))
{
if(pMsg->method == ATT_ERROR_RSP)
{
uint8_t status = pMsg->msg.errorRsp.errCode;
- PRINT("Exchange MTU Error: %x\n", status);
+ PRINT("Exchange MTU Error: %x\r\n", status);
}
centralConnList[connItem].procedureInProgress = FALSE;
}
if(pMsg->method == ATT_MTU_UPDATED_EVENT)
{
- PRINT("MTU: %x\n", pMsg->msg.mtuEvt.MTU);
+ PRINT("MTU: %x\r\n", pMsg->msg.mtuEvt.MTU);
}
if((pMsg->method == ATT_READ_RSP) ||
@@ -527,12 +639,12 @@ static void centralProcessGATTMsg(gattMsgEvent_t *pMsg)
{
uint8_t status = pMsg->msg.errorRsp.errCode;
- PRINT("Read Error: %x\n", status);
+ PRINT("Read Error: %x\r\n", status);
}
else
{
// After a successful read, display the read value
- PRINT("Read rsp: %x\n", *pMsg->msg.readRsp.pValue);
+ PRINT("Read rsp: %x\r\n", *pMsg->msg.readRsp.pValue);
}
centralConnList[connItem].procedureInProgress = FALSE;
}
@@ -544,19 +656,20 @@ static void centralProcessGATTMsg(gattMsgEvent_t *pMsg)
{
uint8_t status = pMsg->msg.errorRsp.errCode;
- PRINT("Write Error: %x\n", status);
+ PRINT("Write Error: %x\r\n", status);
}
else
{
// After a succesful write, display the value that was written and increment value
- PRINT("Write sent: %x\n", centralCharVal);
+ PRINT("Write sent: %x\r\n", centralCharVal);
+
}
centralConnList[connItem].procedureInProgress = FALSE;
}
else if(pMsg->method == ATT_HANDLE_VALUE_NOTI)
{
- PRINT("Receive noti: %x\n", *pMsg->msg.handleValueNoti.pValue);
+ PRINT("Receive noti: %x\r\n", *pMsg->msg.handleValueNoti.pValue);
}
else if(centralConnList[connItem].discState != BLE_DISC_STATE_IDLE)
{
@@ -577,7 +690,7 @@ static void centralProcessGATTMsg(gattMsgEvent_t *pMsg)
*/
static void centralRssiCB(uint16_t connHandle, int8_t rssi)
{
- PRINT("RSSI -%d dB Conn - %x \n", -rssi, connHandle);
+ PRINT("RSSI -%d dB Conn - %x \r\n", -rssi, connHandle);
}
/*********************************************************************
@@ -605,14 +718,15 @@ static void centralHciMTUChangeCB(uint16_t connHandle, uint16_t maxTxOctets, uin
*/
static void centralEventCB(gapRoleEvent_t *pEvent)
{
+ uint8_t advdata[32] = {0};
switch(pEvent->gap.opcode)
{
case GAP_DEVICE_INIT_DONE_EVENT:
{
- PRINT("Discovering...\n");
+ PRINT("Discovering...\r\n");
GAPRole_CentralStartDiscovery(DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
- DEFAULT_DISCOVERY_WHITE_LIST);
+ DEFAULT_DISCOVERY_WHITE_LIST);//开始扫描设备
}
break;
@@ -622,33 +736,31 @@ static void centralEventCB(gapRoleEvent_t *pEvent)
centralAddDeviceInfo(pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType);
}
break;
-
case GAP_DEVICE_DISCOVERY_EVENT:
{
uint8_t i;
- // See if peer device has been discovered
+ // See if peer device has been discovered
for(i = 0; i < centralScanRes; i++)
{
if(centralAddrCmp(PeerAddrDef, centralDevList[i].addr))
break;
}
- // Peer device not found
+ /*Peer device not found*/
if(i == centralScanRes)
{
- PRINT("Device not found...\n");
+ PRINT("Device not found...\r\n");
+
centralScanRes = 0;
GAPRole_CentralStartDiscovery(DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
DEFAULT_DISCOVERY_WHITE_LIST);
- PRINT("Discovering...\n");
- }
-
- // Peer device found
- else
+ PRINT("Discovering...\r\n");
+ }
+ else// Peer device found
{
- PRINT("Device found...\n");
+ PRINT("Device found...\r\n");
GAPRole_CentralEstablishLink(DEFAULT_LINK_HIGH_DUTY_CYCLE,
DEFAULT_LINK_WHITE_LIST,
centralDevList[i].addrType,
@@ -656,18 +768,17 @@ static void centralEventCB(gapRoleEvent_t *pEvent)
// Start establish link timeout event
tmos_start_task(centralTaskId, ESTABLISH_LINK_TIMEOUT_EVT, ESTABLISH_LINK_TIMEOUT);
- PRINT("Connecting...\n");
+ PRINT("Connecting...\r\n");
}
}
break;
case GAP_LINK_ESTABLISHED_EVENT:
{
- tmos_stop_task(centralTaskId, ESTABLISH_LINK_TIMEOUT_EVT);
+ tmos_stop_task(centralTaskId, ESTABLISH_LINK_TIMEOUT_EVT);//关闭连接超时任务
if(pEvent->gap.hdr.status == SUCCESS)
{
uint8_t connItem;
- // ѯǷпĿ
for(connItem = 0; connItem < CENTRAL_MAX_CONNECTION; connItem++)
{
if(centralConnList[connItem].connHandle == GAP_CONNHANDLE_INIT)
@@ -676,41 +787,38 @@ static void centralEventCB(gapRoleEvent_t *pEvent)
if(connItem == CENTRAL_MAX_CONNECTION)
{
GAPRole_TerminateLink(pEvent->linkCmpl.connectionHandle);
- PRINT("Connection max...\n");
+ PRINT("Connection max...\r\n");
}
else
{
centralConnList[connItem].state = BLE_STATE_CONNECTED;
centralConnList[connItem].connHandle = pEvent->linkCmpl.connectionHandle;
- PRINT("Conn %x - Int %x \n", pEvent->linkCmpl.connectionHandle, pEvent->linkCmpl.connInterval);
+ PRINT("Conn %x - Int %x \r\n", pEvent->linkCmpl.connectionHandle, pEvent->linkCmpl.connInterval);
- // 0
if(connItem == CONNECT0_ITEM)
{
centralConnList[connItem].procedureInProgress = TRUE;
- // Initiate service discovery
+ // Initiate service discovery 开启枚举服务任务
tmos_start_task(centralConnList[connItem].taskID, START_SVC_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY);
- // Initiate connect parameter update
+ // Initiate connect parameter update 开始连接参数更新任务
tmos_start_task(centralConnList[connItem].taskID, START_PARAM_UPDATE_EVT, DEFAULT_PARAM_UPDATE_DELAY);
- // Start RSSI polling
+ // Start RSSI polling
tmos_start_task(centralConnList[connItem].taskID, START_READ_RSSI_EVT, DEFAULT_RSSI_PERIOD);
}
- // 1
- else if(connItem == CONNECT1_ITEM)
- {
- }
+ // else if(connItem == CONNECT1_ITEM)
+ // {
+ // }
- // 2
- else if(connItem == CONNECT2_ITEM)
- {
- }
+ // else if(connItem == CONNECT2_ITEM)
+ // {
+ // }
- PRINT("Connected...\n");
+ PRINT("Connected...\r\n");
// See if need discover again
for(connItem = 0; connItem < CENTRAL_MAX_CONNECTION; connItem++)
@@ -720,7 +828,7 @@ static void centralEventCB(gapRoleEvent_t *pEvent)
}
if(connItem < CENTRAL_MAX_CONNECTION)
{
- PRINT("Discovering...\n");
+ PRINT("Discovering...\r\n");
centralScanRes = 0;
GAPRole_CentralStartDiscovery(DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
@@ -730,17 +838,17 @@ static void centralEventCB(gapRoleEvent_t *pEvent)
}
else
{
- PRINT("Connect Failed...Reason:%X\n", pEvent->gap.hdr.status);
- PRINT("Discovering...\n");
+ PRINT("Connect Failed...Reason:%X\r\n", pEvent->gap.hdr.status);
+ PRINT("Discovering...\r\n");
centralScanRes = 0;
GAPRole_CentralStartDiscovery(DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
- DEFAULT_DISCOVERY_WHITE_LIST);
+ DEFAULT_DISCOVERY_WHITE_LIST);//重新开始扫描
}
}
break;
- case GAP_LINK_TERMINATED_EVENT:
+ case GAP_LINK_TERMINATED_EVENT: //断开连接事件,打印断开连接原因,并重新开始扫描
{
uint8_t connItem;
for(connItem = 0; connItem < CENTRAL_MAX_CONNECTION; connItem++)
@@ -752,7 +860,7 @@ static void centralEventCB(gapRoleEvent_t *pEvent)
{
// Should not go there
}
- PRINT(" %x Disconnected...Reason:%x\n", centralConnList[connItem].connHandle, pEvent->linkTerminate.reason);
+ PRINT(" %x Disconnected...Reason:%x\r\n", centralConnList[connItem].connHandle, pEvent->linkTerminate.reason);
centralConnList[connItem].state = BLE_STATE_IDLE;
centralConnList[connItem].connHandle = GAP_CONNHANDLE_INIT;
centralConnList[connItem].discState = BLE_DISC_STATE_IDLE;
@@ -762,7 +870,7 @@ static void centralEventCB(gapRoleEvent_t *pEvent)
tmos_stop_task(centralConnList[connItem].taskID, START_READ_RSSI_EVT);
- PRINT("Discovering...\n");
+ PRINT("Discovering...\r\n");
GAPRole_CentralStartDiscovery(DEFAULT_DISCOVERY_MODE,
DEFAULT_DISCOVERY_ACTIVE_SCAN,
DEFAULT_DISCOVERY_WHITE_LIST);
@@ -771,7 +879,7 @@ static void centralEventCB(gapRoleEvent_t *pEvent)
case GAP_LINK_PARAM_UPDATE_EVENT:
{
- PRINT("Update %x - Int %x \n", pEvent->linkUpdate.connectionHandle, pEvent->linkUpdate.connInterval);
+ PRINT("Update %x - Int %x \r\n", pEvent->linkUpdate.connectionHandle, pEvent->linkUpdate.connInterval);
}
break;
@@ -791,35 +899,35 @@ static void centralPairStateCB(uint16_t connHandle, uint8_t state, uint8_t statu
{
if(state == GAPBOND_PAIRING_STATE_STARTED)
{
- PRINT("Connection %04x - Pairing started:%d\n", connHandle, status);
+ PRINT("Connection %04x - Pairing started:%d\r\n", connHandle, status);
}
else if(state == GAPBOND_PAIRING_STATE_COMPLETE)
{
if(status == SUCCESS)
{
- PRINT("Connection %04x - Pairing success\n", connHandle);
+ PRINT("Connection %04x - Pairing success\r\n", connHandle);
}
else
{
- PRINT("Connection %04x - Pairing fail\n", connHandle);
+ PRINT("Connection %04x - Pairing fail\r\n", connHandle);
}
}
else if(state == GAPBOND_PAIRING_STATE_BONDED)
{
if(status == SUCCESS)
{
- PRINT("Connection %04x - Bonding success\n", connHandle);
+ PRINT("Connection %04x - Bonding success\r\n", connHandle);
}
}
else if(state == GAPBOND_PAIRING_STATE_BOND_SAVED)
{
if(status == SUCCESS)
{
- PRINT("Connection %04x - Bond save success\n", connHandle);
+ PRINT("Connection %04x - Bond save success\r\n", connHandle);
}
else
{
- PRINT("Connection %04x - Bond save failed: %d\n", connHandle, status);
+ PRINT("Connection %04x - Bond save failed: %d\r\n", connHandle, status);
}
}
}
@@ -842,7 +950,7 @@ static void centralPasscodeCB(uint8_t *deviceAddr, uint16_t connectionHandle,
// Display passcode to user
if(uiOutputs != 0)
{
- PRINT("Passcode:%06d\n", (int)passcode);
+ PRINT("Passcode:%06d\r\n", (int)passcode);
}
// Send passcode response
GAPBondMgr_PasscodeRsp(connectionHandle, SUCCESS, passcode);
@@ -871,7 +979,29 @@ static void centralConnIistStartDiscovery_0(void)
ATT_BT_UUID_SIZE,
centralTaskId);
}
+/*********************************************************************
+ * @fn centralConnIistStartDiscovery_1
+ *
+ * @brief Start connection 1 service discovery.
+ *
+ * @return none
+ */
+static void centralConnIistStartDiscovery_1(void)
+{
+ uint8_t uuid[ATT_BT_UUID_SIZE] = {LO_UINT16(SIMPLEPROFILE_SERV_UUID),
+ HI_UINT16(SIMPLEPROFILE_SERV_UUID)};
+ // Initialize cached handles
+ centralConnList[CONNECT1_ITEM].svcStartHdl = centralConnList[CONNECT1_ITEM].svcEndHdl = centralConnList[CONNECT1_ITEM].charHdl = 0;
+
+ centralConnList[CONNECT1_ITEM].discState = BLE_DISC_STATE_SVC;
+
+ // Discovery simple BLE service
+ GATT_DiscPrimaryServiceByUUID(centralConnList[CONNECT1_ITEM].connHandle,
+ uuid,
+ ATT_BT_UUID_SIZE,
+ centralTaskId);
+}
/*********************************************************************
* @fn centralGATTDiscoveryEvent
*
@@ -882,7 +1012,6 @@ static void centralConnIistStartDiscovery_0(void)
static void centralGATTDiscoveryEvent(uint8_t connItem, gattMsgEvent_t *pMsg)
{
attReadByTypeReq_t req;
- // 0ö
if(connItem == CONNECT0_ITEM)
{
if(centralConnList[connItem].discState == BLE_DISC_STATE_SVC)
@@ -895,7 +1024,88 @@ static void centralGATTDiscoveryEvent(uint8_t connItem, gattMsgEvent_t *pMsg)
centralConnList[connItem].svcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
// Display Profile Service handle range
- PRINT("Found Profile Service handle : %x ~ %x \n", centralConnList[connItem].svcStartHdl, centralConnList[connItem].svcEndHdl);
+ PRINT("Found Profile Service handle : %x ~ %x \r\n", centralConnList[connItem].svcStartHdl, centralConnList[connItem].svcEndHdl);
+ }
+ // If procedure complete
+ if((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP && pMsg->hdr.status == bleProcedureComplete) || (pMsg->method == ATT_ERROR_RSP))
+ {
+ if(centralConnList[connItem].svcStartHdl != 0)
+ {
+ // Discover characteristic
+ centralConnList[connItem].discState = BLE_DISC_STATE_CHAR;
+ req.startHandle = centralConnList[connItem].svcStartHdl;
+ req.endHandle = centralConnList[connItem].svcEndHdl;
+ req.type.len = ATT_BT_UUID_SIZE;
+ req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);
+ req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);
+
+ GATT_ReadUsingCharUUID(centralConnList[connItem].connHandle, &req, centralTaskId);
+ }
+ }
+ }
+ else if(centralConnList[connItem].discState == BLE_DISC_STATE_CHAR)
+ {
+ // Characteristic found, store handle
+ if(pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->msg.readByTypeRsp.numPairs > 0)
+ {
+ centralConnList[connItem].charHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],
+ pMsg->msg.readByTypeRsp.pDataList[1]);
+ centralConnList[connItem].procedureInProgress = FALSE;
+
+ // Start do read or write
+ tmos_start_task(centralConnList[connItem].taskID, START_READ_OR_WRITE_EVT, DEFAULT_READ_OR_WRITE_DELAY);//开启一个读写测试任务
+
+ // Display Characteristic 1 handle
+ PRINT("Found Characteristic 1 handle : %x \r\n", centralConnList[0].charHdl);
+ }
+
+ if((pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->hdr.status == bleProcedureComplete) || (pMsg->method == ATT_ERROR_RSP))
+ {
+ // Discover characteristic
+ centralConnList[connItem].discState = BLE_DISC_STATE_CCCD;
+ req.startHandle = centralConnList[connItem].svcStartHdl;
+ req.endHandle = centralConnList[connItem].svcEndHdl;
+ req.type.len = ATT_BT_UUID_SIZE;
+ req.type.uuid[0] = LO_UINT16(GATT_CLIENT_CHAR_CFG_UUID);
+ req.type.uuid[1] = HI_UINT16(GATT_CLIENT_CHAR_CFG_UUID);
+
+ GATT_ReadUsingCharUUID(centralConnList[connItem].connHandle, &req, centralTaskId);//根据UUID寻找其句柄
+ }
+
+ }
+ else if(centralConnList[connItem].discState == BLE_DISC_STATE_CCCD)
+ {
+ // Characteristic found, store handle
+ if(pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->msg.readByTypeRsp.numPairs > 0)
+ {
+ centralConnList[connItem].cccHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],
+ pMsg->msg.readByTypeRsp.pDataList[1]);
+
+ centralConnList[connItem].procedureInProgress = FALSE;
+
+ // Start do write CCCD
+ tmos_start_task(centralConnList[connItem].taskID, START_WRITE_CCCD_EVT, DEFAULT_WRITE_CCCD_DELAY);//开启一个使能从机notify的定时器任务
+
+ // Display Characteristic 1 handle
+ PRINT("Found client characteristic configuration handle : %x \r\n", centralConnList[connItem].cccHdl);//输出的句柄值
+ }
+ centralConnList[connItem].discState = BLE_DISC_STATE_IDLE;
+ }
+ }
+
+ else if(connItem == CONNECT1_ITEM)
+ {
+ if(centralConnList[connItem].discState == BLE_DISC_STATE_SVC)
+ {
+ // Service found, store handles
+ if(pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
+ pMsg->msg.findByTypeValueRsp.numInfo > 0)
+ {
+ centralConnList[connItem].svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
+ centralConnList[connItem].svcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
+
+ // Display Profile Service handle range
+ PRINT("Found Profile Service handle : %x ~ %x \r\n", centralConnList[connItem].svcStartHdl, centralConnList[connItem].svcEndHdl);
}
// If procedure complete
if((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
@@ -930,7 +1140,7 @@ static void centralGATTDiscoveryEvent(uint8_t connItem, gattMsgEvent_t *pMsg)
tmos_start_task(centralConnList[connItem].taskID, START_READ_OR_WRITE_EVT, DEFAULT_READ_OR_WRITE_DELAY);
// Display Characteristic 1 handle
- PRINT("Found Characteristic 1 handle : %x \n", centralConnList[0].charHdl);
+ PRINT("Found Characteristic 1 handle : %x \r\n", centralConnList[0].charHdl);
}
if((pMsg->method == ATT_READ_BY_TYPE_RSP &&
@@ -964,19 +1174,15 @@ static void centralGATTDiscoveryEvent(uint8_t connItem, gattMsgEvent_t *pMsg)
tmos_start_task(centralConnList[connItem].taskID, START_WRITE_CCCD_EVT, DEFAULT_WRITE_CCCD_DELAY);
// Display Characteristic 1 handle
- PRINT("Found client characteristic configuration handle : %x \n", centralConnList[connItem].cccHdl);
+ PRINT("Found client characteristic configuration handle : %x \r\n", centralConnList[connItem].cccHdl);
}
centralConnList[connItem].discState = BLE_DISC_STATE_IDLE;
}
}
- // 1ö
- else if(connItem == CONNECT1_ITEM)
- {
- }
- // 2ö
- else if(connItem == CONNECT2_ITEM)
- {
- }
+
+// else if(connItem == CONNECT2_ITEM)
+// {
+// }
}
/*********************************************************************
@@ -1007,7 +1213,7 @@ static void centralAddDeviceInfo(uint8_t *pAddr, uint8_t addrType)
// Increment scan result count
centralScanRes++;
// Display device addr
- PRINT("Device %d - Addr %x %x %x %x %x %x \n", centralScanRes,
+ PRINT("Device %d - Addr %x %x %x %x %x %x \r\n", centralScanRes,
centralDevList[centralScanRes - 1].addr[0],
centralDevList[centralScanRes - 1].addr[1],
centralDevList[centralScanRes - 1].addr[2],
diff --git a/APP/multiCentral_main.c b/APP/multiCentral_main.c
index 51aef16..2eaf639 100644
--- a/APP/multiCentral_main.c
+++ b/APP/multiCentral_main.c
@@ -1,3 +1,13 @@
+/*
+ * @Author: mbw
+ * @Date: 2024-12-03 11:13:13
+ * @LastEditors: mbw && 1600520629@qq.com
+ * @LastEditTime: 2024-12-07 16:17:02
+ * @FilePath: \ble_-tyq_-bjq_-ch584-m\APP\multiCentral_main.c
+ * @Description:
+ *
+ * Copyright (c) 2024 by ${git_name_email}, All Rights Reserved.
+ */
/********************************** (C) COPYRIGHT *******************************
* File Name : main.c
* Author : WCH
@@ -11,10 +21,16 @@
*******************************************************************************/
/******************************************************************************/
-/* ͷļ */
#include "CONFIG.h"
#include "HAL.h"
#include "multiCentral.h"
+#include "bsp_uart.h"
+#include "log.h"
+
+
+#undef LOG_ENABLE
+#define LOG_ENABLE 1
+
/*********************************************************************
* GLOBAL TYPEDEFS
@@ -28,7 +44,7 @@ const uint8_t MacAddr[6] = {0x84, 0xC2, 0xE4, 0x03, 0x02, 0x02};
/*********************************************************************
* @fn Main_Circulation
*
- * @brief ѭ
+ * @brief
*
* @return none
*/
@@ -45,7 +61,7 @@ void Main_Circulation()
/*********************************************************************
* @fn main
*
- * @brief
+ * @brief
*
* @return none
*/
@@ -60,18 +76,27 @@ int main(void)
GPIOA_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
GPIOB_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
#endif
-#ifdef DEBUG
+
+ CH58x_BLEInit();
+ HAL_Init();
+ GAPRole_CentralInit();
+ Central_Init();
+#if DEBUG == Debug_UART0
GPIOA_SetBits(GPIO_Pin_14);
GPIOPinRemap(ENABLE, RB_PIN_UART0);
GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU);
GPIOA_ModeCfg(GPIO_Pin_14, GPIO_ModeOut_PP_5mA);
UART0_DefInit();
+#elif DEBUG == Debug_UART1
+ GPIOB_SetBits(GPIO_Pin_9);
+ GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);
+ GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
+ UART1_DefInit();
+#elif DEBUG == Debug_UART3
+ BSP_UART3_Init();
#endif
- PRINT("%s\n", VER_LIB);
- CH58x_BLEInit();
- HAL_Init();
- GAPRole_CentralInit();
- Central_Init();
+ BSP_UART1_Init();//BT UART
+ logDebug("test log debug\n");
Main_Circulation();
}
diff --git a/HAL/MCU.c b/HAL/MCU.c
index c2f2c0f..09a949d 100644
--- a/HAL/MCU.c
+++ b/HAL/MCU.c
@@ -3,7 +3,7 @@
* Author : WCH
* Version : V1.2
* Date : 2022/01/18
- * Description : ӲBLEӲʼ
+ * Description : Ӳ��������������BLE��Ӳ����ʼ��
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
@@ -11,7 +11,7 @@
*******************************************************************************/
/******************************************************************************/
-/* ͷļ */
+/* ͷ�ļ����� */
#include "HAL.h"
tmosTaskID halTaskID;
@@ -19,7 +19,7 @@ uint32_t g_LLE_IRQLibHandlerLocation;
/*******************************************************************************
* @fn Lib_Calibration_LSI
*
- * @brief ڲ32kУ
+ * @brief �ڲ�32kУ
*
* @param None.
*
@@ -70,7 +70,7 @@ uint32_t Lib_Write_Flash(uint32_t addr, uint32_t num, uint32_t *pBuf)
/*******************************************************************************
* @fn CH58x_BLEInit
*
- * @brief BLE ʼ
+ * @brief BLE
*
* @param None.
*
@@ -86,7 +86,7 @@ void CH58x_BLEInit(void)
while(1);
}
- SysTick_Config(SysTick_LOAD_RELOAD_Msk);// SysTickж
+ SysTick_Config(SysTick_LOAD_RELOAD_Msk);
PFIC_DisableIRQ(SysTick_IRQn);
g_LLE_IRQLibHandlerLocation = (uint32_t)LLE_IRQLibHandler;
@@ -113,13 +113,13 @@ void CH58x_BLEInit(void)
cfg.ConnectNumber = (PERIPHERAL_MAX_CONNECTION & 3) | (CENTRAL_MAX_CONNECTION << 2);
cfg.srandCB = SYS_GetSysTickCnt;
#if(defined TEM_SAMPLE) && (TEM_SAMPLE == TRUE)
- cfg.tsCB = HAL_GetInterTempValue; // ¶ȱ仯УRFڲRC( 7϶ )
+ cfg.tsCB = HAL_GetInterTempValue;
#if(CLK_OSC32K)
- cfg.rcCB = Lib_Calibration_LSI; // ڲ32KʱУ
+ cfg.rcCB = Lib_Calibration_LSI;
#endif
#endif
#if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE)
- cfg.idleCB = CH58x_LowPower; // ˯
+ cfg.idleCB = CH58x_LowPower;
#endif
#if(defined(BLE_MAC)) && (BLE_MAC == TRUE)
for(i = 0; i < 6; i++)
@@ -132,7 +132,9 @@ void CH58x_BLEInit(void)
GetMACAddress(MacAddr);
for(i = 0; i < 6; i++)
{
- cfg.MacAddr[i] = MacAddr[i]; // ʹоƬmacַ
+ cfg.MacAddr[i] = MacAddr[i];
+ PRINT("%02x ", cfg.MacAddr[i]);
+
}
}
#endif
@@ -140,7 +142,6 @@ void CH58x_BLEInit(void)
{
while(1);
}
- // BLE_Lib ռVTF Interrupt 2ź3
i = BLE_LibInit(&cfg);
if(i)
{
@@ -152,7 +153,7 @@ void CH58x_BLEInit(void)
/*******************************************************************************
* @fn HAL_ProcessEvent
*
- * @brief Ӳ
+ * @brief
*
* @param task_id - The TMOS assigned task ID.
* @param events - events to process. This is a bit map and can
@@ -165,7 +166,7 @@ tmosEvents HAL_ProcessEvent(tmosTaskID task_id, tmosEvents events)
uint8_t *msgPtr;
if(events & SYS_EVENT_MSG)
- { // HALϢtmos_msg_receiveȡϢɺɾϢ
+ {
msgPtr = tmos_msg_receive(task_id);
if(msgPtr)
{
@@ -192,16 +193,17 @@ tmosEvents HAL_ProcessEvent(tmosTaskID task_id, tmosEvents events)
if(events & HAL_REG_INIT_EVENT)
{
uint8_t x32Kpw;
-#if(defined BLE_CALIBRATION_ENABLE) && (BLE_CALIBRATION_ENABLE == TRUE) // УУʱС10ms
+#if(defined BLE_CALIBRATION_ENABLE) && (BLE_CALIBRATION_ENABLE == TRUE)
#ifndef RF_8K
- BLE_RegInit(); // УRFرRFıRFؼĴʹRFշעУ
+
+ BLE_RegInit();
#endif
#if(CLK_OSC32K)
- Lib_Calibration_LSI(); // УڲRC
+ Lib_Calibration_LSI();
#elif(HAL_SLEEP)
x32Kpw = (R8_XT32K_TUNE & 0xfc) | 0x01;
sys_safe_access_enable();
- R8_XT32K_TUNE = x32Kpw; // LSE͵
+ R8_XT32K_TUNE = x32Kpw;
sys_safe_access_disable();
#endif
tmos_start_task(halTaskID, HAL_REG_INIT_EVENT, MS1_TO_SYSTEM_TIME(BLE_CALIBRATION_PERIOD));
@@ -220,7 +222,7 @@ tmosEvents HAL_ProcessEvent(tmosTaskID task_id, tmosEvents events)
/*******************************************************************************
* @fn HAL_Init
*
- * @brief Ӳʼ
+ * @brief
*
* @param None.
*
@@ -240,17 +242,17 @@ void HAL_Init()
HAL_KeyInit();
#endif
#if(defined BLE_CALIBRATION_ENABLE) && (BLE_CALIBRATION_ENABLE == TRUE)
- tmos_start_task(halTaskID, HAL_REG_INIT_EVENT, 800); // У500msУʱС10ms
+ tmos_start_task(halTaskID, HAL_REG_INIT_EVENT, 800);
#endif
-// tmos_start_task( halTaskID, HAL_TEST_EVENT, 1600 ); // һ
+// tmos_start_task( halTaskID, HAL_TEST_EVENT, 1600 );
}
/*******************************************************************************
* @fn HAL_GetInterTempValue
*
- * @brief ȡڲ¸вֵʹADCжϲڴ˺ʱж.
+ * @brief
*
- * @return ڲ¸вֵ.
+ * @return
*/
uint16_t HAL_GetInterTempValue(void)
{
diff --git a/HAL/include/CONFIG.h b/HAL/include/CONFIG.h
index 277bd5b..941bc5e 100644
--- a/HAL/include/CONFIG.h
+++ b/HAL/include/CONFIG.h
@@ -3,7 +3,7 @@
* Author : WCH
* Version : V1.2
* Date : 2022/01/18
- * Description : ˵ĬֵڹԤĵǰֵ
+ * Description : ����˵����Ĭ��ֵ�������ڹ����������Ԥ�������ĵ�ǰֵ
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
@@ -26,54 +26,8 @@
#include "CH58x_common.h"
/*********************************************************************
- MAC
- BLE_MAC - ǷԶMacַ ( Ĭ:FALSE - ʹоƬMacַ )Ҫmain.cMacַ
-
- DCDC
- DCDC_ENABLE - ǷʹDCDC ( Ĭ:FALSE )
-
- SLEEP
- HAL_SLEEP - Ƿ˯߹ ( Ĭ:FALSE )
- SLEEP_RTC_MIN_TIME - ǿģʽ˯ߵСʱ䣨λһRTCڣ
- SLEEP_RTC_MAX_TIME - ǿģʽ˯ߵʱ䣨λһRTCڣ
- WAKE_UP_RTC_MAX_TIME - ȴ32Mȶʱ䣨λһRTCڣ
- ݲͬ˯ȡֵɷΪ ˯ģʽ/µģʽ - 45 (Ĭ)
- ͣģʽ - 45
- ģʽ - 5
- TEMPERATION
- TEM_SAMPLE - Ƿ¶ȱ仯УĹܣУʱС10ms( Ĭ:TRUE )
-
- CALIBRATION
- BLE_CALIBRATION_ENABLE - ǷʱУĹܣУʱС10ms( Ĭ:TRUE )
- BLE_CALIBRATION_PERIOD - ʱУڣλms( Ĭ:120000 )
-
- SNV
- BLE_SNV - ǷSNVܣڴϢ( Ĭ:TRUE )
- BLE_SNV_ADDR - SNVϢַʹdata flash512ֽ( Ĭ:0x77E00 )
- BLE_SNV_BLOCK - SNVϢС( Ĭ:256 )
- BLE_SNV_NUM - SNVϢ( Ĭ:1 )
-
- RTC
- CLK_OSC32K - RTCʱѡɫʹⲿ32K( 0 ⲿ(32768Hz)Ĭ:1ڲ(32000Hz)2ڲ(32768Hz) )
-
- MEMORY
- BLE_MEMHEAP_SIZE - ЭջʹõRAMСС6K ( Ĭ:(1024*6) )
-
- DATA
- BLE_BUFF_MAX_LEN - ( Ĭ:27 (ATT_MTU=23)ȡֵΧ[27~516] )
- BLE_BUFF_NUM - İ( Ĭ:5 )
- BLE_TX_NUM_EVENT - ¼Էٸݰ( Ĭ:1 )
- BLE_TX_POWER - 书( Ĭ:LL_TX_POWEER_0_DBM (0dBm) )
-
- MULTICONN
- PERIPHERAL_MAX_CONNECTION - ͬʱٴӻɫ( Ĭ:1 )
- CENTRAL_MAX_CONNECTION - ͬʱɫ( Ĭ:3 )
**********************************************************************/
-
-/*********************************************************************
- * Ĭֵ
- */
#ifndef BLE_MAC
#define BLE_MAC FALSE
#endif
@@ -120,10 +74,10 @@
#define BLE_SNV_NUM 1
#endif
#ifndef CLK_OSC32K
-#define CLK_OSC32K 1 // ڴģڹԤģɫʹⲿ32K
+#define CLK_OSC32K 1
#endif
#ifndef BLE_MEMHEAP_SIZE
-#define BLE_MEMHEAP_SIZE (1024*6)
+#define BLE_MEMHEAP_SIZE (1024*12)
#endif
#ifndef BLE_BUFF_MAX_LEN
#define BLE_BUFF_MAX_LEN 27
@@ -141,7 +95,7 @@
#define PERIPHERAL_MAX_CONNECTION 1
#endif
#ifndef CENTRAL_MAX_CONNECTION
-#define CENTRAL_MAX_CONNECTION 3
+#define CENTRAL_MAX_CONNECTION 1
#endif
extern uint32_t MEM_BUF[BLE_MEMHEAP_SIZE / 4];
diff --git a/Ld/Link.ld b/Ld/Link.ld
index b964fb0..f6d6e18 100644
--- a/Ld/Link.ld
+++ b/Ld/Link.ld
@@ -71,6 +71,9 @@ SECTIONS
*(.glue_7t)
*(.gnu.linkonce.t.*)
. = ALIGN(4);
+ _shell_command_start = .;
+ KEEP (*(shellCommand))
+ _shell_command_end = .;
} >FLASH AT>FLASH
.fini :
diff --git a/StdPeriphDriver/CH58x_sys.c b/StdPeriphDriver/CH58x_sys.c
index 85ea973..cce7a1c 100644
--- a/StdPeriphDriver/CH58x_sys.c
+++ b/StdPeriphDriver/CH58x_sys.c
@@ -11,6 +11,7 @@
*******************************************************************************/
#include "CH58x_common.h"
+#include "bsp_uart.h"
volatile MachineMode_Call_func gs_machine_mode_func;
@@ -546,23 +547,24 @@ void mDelaymS(uint16_t t)
#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
- }
+ BSP_Uart3_Send_Data(buf, 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;
}
diff --git a/bsp/inc/bsp_flash.h b/bsp/inc/bsp_flash.h
new file mode 100644
index 0000000..3b31c14
--- /dev/null
+++ b/bsp/inc/bsp_flash.h
@@ -0,0 +1,50 @@
+#ifndef __BSP_FLASH_H__
+#define __BSP_FLASH_H__
+
+#include "CH58x_common.h"
+
+/*CH584具有data_flash和code flash两部分。 codeflash为448k dataflash为32k
+CodeFlash读操作是有硬件加扰的,也就是擦除后读不是全FF,而是四个固定字节的循环,DataFlash读操作没有硬件加扰
+CodeFlash和DataFlash都可以直接指针读数据(指针读经过硬件加扰)。
+0x00000000-0x0006FFFF 用户应用程序存储区 CodeFlash 448KB
+0x00070000-0x00077FFF 用户非易失数据存储区 DataFlash 32KB
+0x00078000-0x0007DFFF 系统引导程序存储区 BootLoader 24KB
+0x0007E000-0x0007FFFF 系统非易失配置信息存储区 InfoFlash 8KB
+*/
+
+/*BLE蓝牙falsh写入注意地址是相对于dataflash的,而dataflash的起始地址是0x3FFF,所以传入的参数是0x3FFF+offset
+并且不要覆盖原有的参数
+https://www.cnblogs.com/JayWellsBlog/p/18223898
+
+接口中的读/写buff缓存在ram中的地址,一定要4字节对齐,否则可能导致hardfault复位
+问题排查
+https://www.cnblogs.com/iot-fan/p/16053937.html
+*/
+#define DATA_FALASH_START_ADDR (0X3FFF) // 传入的是相对地址,用对函数即可
+#define DATA_FLASH_PAGE_SIZE (256U)
+#define FLASH_MAC_INFO_LEN (6U)
+#define FLASH_MAC_NUM_LEN (1U)
+#define FLASH_INFO_WRITE_FLAG_LEN (4U)
+#define FLASH_INFO_ADDR (DATA_FALASH_START_ADDR)
+#define FLASH_MAC_INFO_START_ADDR (DATA_FALASH_START_ADDR) // 绝对地址
+
+#define FLASH_MAC_1_ADDR FLASH_MAC_INFO_START_ADDR
+#define FLASH_MAC_2_ADDR (FLASH_MAC_1_ADDR + FLASH_MAC_INFO_LEN)
+#define FLASH_MAC_3_ADDR (FLASH_MAC_2_ADDR + FLASH_MAC_INFO_LEN)
+#define FLASH_MAC_4_ADDR (FLASH_MAC_3_ADDR + FLASH_MAC_INFO_LEN)
+#define FLASH_MAC_5_ADDR (FLASH_MAC_4_ADDR + FLASH_MAC_INFO_LEN)
+#define FLASH_MAC_6_ADDR (FLASH_MAC_5_ADDR + FLASH_MAC_INFO_LEN)
+#define FLASH_MAC_7_ADDR (FLASH_MAC_6_ADDR + FLASH_MAC_INFO_LEN)
+#define FLASH_MAC_8_ADDR (FLASH_MAC_7_ADDR + FLASH_MAC_INFO_LEN)
+#define FLASH_MAC_NUM_ADDR (FLASH_MAC_8_ADDR + FLASH_MAC_INFO_LEN) // 用来记录有效的mac地址数量
+#define FLASH_INFO_WRITE_FLAG_ADDR (FLASH_MAC_NUM_ADDR + FLASH_MAC_NUM_LEN)
+
+#define FLASH_INFO_WRITE_FLAG_VAL (0XAABBCCDD)
+
+int Flash_Set_Mac_Addr(uint8_t *mac_addr, uint8_t number);
+int Flash_Get_Mac_Addr(uint8_t *mac_addr, uint8_t number);
+int Flash_Set_Valve_Num(uint8_t valve_num);
+uint8_t Flash_Get_Valve_Num(void);
+void BSP_FLASH_Init(void);
+
+#endif // !__BSP_FLASH_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..0e6b849
--- /dev/null
+++ b/bsp/inc/bsp_uart.h
@@ -0,0 +1,67 @@
+/***
+ * @Author: mbw
+ * @Date: 2024-12-06 16:52:29
+ * @LastEditors: mbw && 1600520629@qq.com
+ * @LastEditTime: 2024-12-07 15:10:15
+ * @FilePath: \ble_-tyq_-bjq_-ch584-m\bsp\inc\bsp_uart.h
+ * @Description:
+ * @
+ * @Copyright (c) 2024 by ${git_name_email}, All Rights Reserved.
+ */
+#ifndef __BSP_UART_H__
+#define __BSP_UART_H__
+
+#include "CH58x_common.h"
+
+#define MAX_VALVE_NUM 8
+
+#define UART3_RX_PIN GPIO_Pin_20 //PB20 日志打印
+#define UART3_TX_PIN GPIO_Pin_21 // PB21
+
+
+#define BT_UART_TX_PIN GPIO_Pin_9 //PA9
+#define BT_UART_RX_PIN GPIO_Pin_8 //PA8
+
+typedef struct valve_data valve_data_t;
+
+/*
+无线调压阀编号
+无线调压阀 MAC 地址
+无线调压阀连接状态
+无线调压阀开关状态
+无线调压阀温度
+无线调压阀进口压力
+无线调压阀出口压力
+无线调压阀大气压
+*/
+struct __attribute__((packed)) valve_data
+{
+ uint8_t valve_id;
+ uint8_t valve_mac[6];
+ uint8_t valve_connct_status;
+ uint8_t valve_switch_status;
+ uint8_t valve_temp;
+ uint32_t valve_in_pressure;
+ uint32_t valve_out_pressure;
+ uint32_t valve_atm_pressure;
+};
+
+typedef struct valve_data_list
+{
+ uint8_t valve_num;
+ valve_data_t valve_data[MAX_VALVE_NUM];
+} valve_data_list_t ;
+
+
+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 // !__BSP_UART_H__
diff --git a/bsp/src/bsp_flash.c b/bsp/src/bsp_flash.c
new file mode 100644
index 0000000..a515f5a
--- /dev/null
+++ b/bsp/src/bsp_flash.c
@@ -0,0 +1,95 @@
+/*
+ * @Author: mbw
+ * @Date: 2024-12-06 14:49:21
+ * @LastEditors: mbw && 1600520629@qq.com
+ * @LastEditTime: 2024-12-07 12:58:09
+ * @FilePath: \ble_-tyq_-bjq_-ch584-m\bsp\src\bsp_flash.c
+ * @Description: 主要用于对一些MAC地址等关键信息进行保存和读取操作
+ *
+ * Copyright (c) 2024 by ${git_name_email}, All Rights Reserved.
+ */
+#include "bsp_flash.h"
+#include "CONFIG.h"
+#include "string.h"
+
+void Flash_ErasePage_ReadConfigInfo(uint8_t *page_buf)
+{
+ EEPROM_READ(FLASH_INFO_ADDR, page_buf, DATA_FLASH_PAGE_SIZE);
+ EEPROM_ERASE(FLASH_INFO_ADDR, DATA_FLASH_PAGE_SIZE);
+}
+
+int Flash_Write_ConfigInfo(uint8_t *page_buf)
+{
+ EEPROM_WRITE(FLASH_INFO_ADDR, page_buf, DATA_FLASH_PAGE_SIZE);
+
+ return 0;
+}
+
+/*写入MAC地址*/
+int Flash_Set_Mac_Addr(uint8_t *mac_addr, uint8_t number)
+{
+ uint8_t page_buf[DATA_FLASH_PAGE_SIZE] = {0};
+ uint8_t in_page_offset = ((FLASH_MAC_1_ADDR + number * FLASH_MAC_INFO_LEN) - FLASH_MAC_INFO_START_ADDR);
+ if (mac_addr == NULL)
+ {
+ return -1;
+ }
+ Flash_ErasePage_ReadConfigInfo(page_buf);
+ for (int i = 0; i < FLASH_MAC_INFO_LEN; i++)
+ {
+ page_buf[in_page_offset++] = mac_addr[i];
+ }
+ return Flash_Write_ConfigInfo(page_buf);
+}
+
+/*读取MAC地址*/
+int Flash_Get_Mac_Addr(uint8_t *mac_addr, uint8_t number)
+{
+ __attribute__((aligned(4)))
+ uint8_t data[FLASH_MAC_INFO_LEN + 2]; //读写buf需要4字节对齐
+ char mac[16] = {20};
+ tmos_memcpy(data, (uint8_t *)(FLASH_MAC_1_ADDR + number * FLASH_MAC_INFO_LEN), FLASH_MAC_INFO_LEN);
+ if (data == NULL)
+ {
+ // PRINTF("Flash_Get_Mac_Addr failed");
+ return -1;
+ }
+ // sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x",
+ // data[5], data[4], data[3], data[2], data[1], data[0]);
+ // PRINTF("mac_addr:%s", mac);
+ tmos_memcpy(mac_addr, data, FLASH_MAC_INFO_LEN);
+
+ return 0;
+}
+
+/*设置无线调压器数量*/
+int Flash_Set_Valve_Num(uint8_t valve_num)
+{
+ uint8_t page_buf[DATA_FLASH_PAGE_SIZE] = {0};
+ uint8_t in_page_offset = (FLASH_INFO_ADDR - FLASH_MAC_INFO_START_ADDR);
+ Flash_ErasePage_ReadConfigInfo(page_buf);
+ page_buf[in_page_offset] = valve_num;
+ return Flash_Write_ConfigInfo(page_buf);
+}
+
+/*获取无线调压器数量*/
+uint8_t Flash_Get_Valve_Num(void)
+{
+ uint8_t num;
+ EEPROM_READ(FLASH_MAC_NUM_ADDR, &num, FLASH_MAC_NUM_LEN);
+ return num;
+}
+
+void BSP_FLASH_Init(void)
+{
+ uint32_t flash_write_flag = 0;
+
+ EEPROM_READ(FLASH_INFO_WRITE_FLAG_ADDR, &flash_write_flag, FLASH_INFO_WRITE_FLAG_LEN);
+ if (flash_write_flag != FLASH_INFO_WRITE_FLAG_VAL)//用以防止刷掉写入的数据
+ {
+ flash_write_flag = FLASH_INFO_WRITE_FLAG_VAL;
+ //擦除数据
+ EEPROM_ERASE(FLASH_INFO_ADDR, DATA_FLASH_PAGE_SIZE);//擦除待写区
+ EEPROM_WRITE(FLASH_INFO_ADDR, &flash_write_flag, FLASH_INFO_WRITE_FLAG_LEN);
+ }
+}
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_cntSysTick_Handler()1ms +1
+volatile uint32_t tick_1ms_cnt = 0;
+
+/**
+ * @description: SysTick1msΪ
+ * @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..4911828
--- /dev/null
+++ b/bsp/src/bsp_uart.c
@@ -0,0 +1,243 @@
+#include "bsp_uart.h"
+#include "lwrb.h"
+#include "CH58x_uart.h"
+#include "shell_port.h"
+#include "CH58xBLE_LIB.h"
+
+
+#undef LOG_ENABLE
+#define LOG_ENABLE 1
+
+#include "log.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};
+
+
+static uint8_t BtRxTaskId;
+
+#define BT_START_EVT (0X01 << 0)
+#define BT_REC_EVT (0X01 << 1)
+
+
+//串口接收
+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;
+}
+
+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));
+
+}
+
+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));
+}
+
+__HIGH_CODE
+__attribute__((noinline))
+uint16_t BT_Rx_ProcessEvent(uint8_t task_id, uint16_t events)
+{
+ if(events & BT_START_EVT)
+ {
+ lwrb_reset(&uart1_rx_t);
+ tmos_start_task(task_id, BT_REC_EVT, 20);
+ return (events ^ BT_START_EVT);
+ }
+ else if(events & BT_REC_EVT)
+ {
+ int len = lwrb_get_full(&uart1_rx_t);
+ if (len > 0)
+ {
+ uint8_t buf[128] = {0};
+ char *ptr = (char *)malloc(len + 1);
+ lwrb_read(&uart1_rx_t, ptr, len);
+ logHexDumpAll(ptr, len);
+ free(ptr);
+ }
+ tmos_start_task(task_id, BT_REC_EVT, 20);
+ return (events ^ BT_REC_EVT);
+ }
+ return 0;
+}
+/*BT与报警器主机接口*/
+void BSP_UART1_Init(void)
+{
+ // GPIOPinRemap(ENABLE, RB_PIN_UART1);
+
+ /* 配置串口1:先配置IO口模式,再配置串口 */
+ GPIOB_SetBits(BT_UART_RX_PIN);
+ GPIOB_ModeCfg(BT_UART_RX_PIN, GPIO_ModeIN_PU); // RXD-配置上拉输入
+ GPIOB_ModeCfg(BT_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();
+ BtRxTaskId = TMOS_ProcessEventRegister(BT_Rx_ProcessEvent);
+ tmos_set_event(BtRxTaskId, BT_START_EVT);
+}
+
+
+
+/*调试接口*/
+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_BaudRateCfg(460800);
+ 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:
+ case UART_II_RECV_TOUT: // 接收数据
+ while(R8_UART1_RFC)
+ {
+ data = R8_UART1_RBR;
+ lwrb_write(&uart1_rx_t, &data, 1);
+ }
+ 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/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
+
+
+
+
+
+
+一个功能强大的嵌入式shell
+
+
+
+- [letter shell 3.x](#letter-shell-3x)
+ - [简介](#简介)
+ - [功能](#功能)
+ - [移植说明](#移植说明)
+ - [使用方式](#使用方式)
+ - [函数定义](#函数定义)
+ - [main函数形式](#main函数形式)
+ - [普通C函数形式](#普通c函数形式)
+ - [变量使用](#变量使用)
+ - [在函数中获取当前shell对象](#在函数中获取当前shell对象)
+ - [执行未导出函数](#执行未导出函数)
+ - [命令定义](#命令定义)
+ - [定义方式](#定义方式)
+ - [定义宏说明](#定义宏说明)
+ - [命令属性字段说明](#命令属性字段说明)
+ - [代理函数和代理参数解析](#代理函数和代理参数解析)
+ - [函数签名](#函数签名)
+ - [自定义类型解析](#自定义类型解析)
+ - [权限系统说明](#权限系统说明)
+ - [锁说明](#锁说明)
+ - [伴生对象](#伴生对象)
+ - [尾行模式](#尾行模式)
+ - [建议终端软件](#建议终端软件)
+ - [命令遍历工具](#命令遍历工具)
+ - [x86 demo](#x86-demo)
+
+## 简介
+
+[letter shell](https://github.com/NevermindZZT/letter-shell)是一个C语言编写的,可以嵌入在程序中的嵌入式shell,主要面向嵌入式设备,以C语言函数为运行单位,可以通过命令行调用,运行程序中的函数
+
+相对2.x版本,letter 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版本对读写函数原型的修改
+
+## 功能
+
+- 命令自动补全
+- 快捷键功能定义
+- 命令权限管理
+- 用户管理
+- 变量支持
+- 代理函数和参数代理解析
+
+## 移植说明
+
+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()`为获取系统tick函数,使能tab双击操作,用户长帮助补全
+
+8. 配置宏
+
+ shell_cfg.h文件中包含了所有用于配置shell的宏,在使用前,需要根据需要进行配置
+
+ 建议采用 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 | 管理的最大shell数量 |
+ | 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[])`以及形如普通C函数的定义`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
+```
+
+#### 普通C函数形式
+
+使用此方式,shell会自动对参数进行转化处理,目前支持二进制,八进制,十进制,十六进制整形,字符,字符串的自动处理,如果需要其他类型的参数,请使用代理参数解析的方式(参考[代理函数和代理参数解析](#代理函数和代理参数解析)),或者使用字符串的方式作为参数,自行进行处理,例子如下:
+
+```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采取一个静态数组对定义的多个shell进行管理,shell数量可以修改宏`SHELL_MAX_NUMBER`定义(为了不使用动态内存分配,此处通过数据进行管理),从而,在shell执行的函数中,可以调用`shellGetCurrent()`获得当前活动的shell对象,从而可以实现某一个函数在不同的shell对象中发生不同的行为,也可以通过这种方式获得shell对象后,调用`shellWriteString(shell, string)`进行shell的输出
+
+### 执行未导出函数
+
+letter shell支持通过函数地址直接执行函数,可以方便执行那些没有导出,但是又临时需要使用的函数,使用命令`exec [addr] [args]`执行,使用此功能需要开启`SHELL_EXEC_UNDEF_FUNC`宏,注意,由于直接操作函数地址执行,如果给进的地址有误,可能引起程序崩溃
+
+函数的地址可以通过编译生成的文件查找,比如说对于keil,可以在`.map`文件中查找到每个函数的地址,但是要注意有些平台可能需要要对地址做进一步处理,比如说对于 arm 平台,如果使用的是 Thumb 指令集,那么需要将地址的最低位置 1,比如说`shellClear`函数地址为`0x08028620`,则通过`exec`执行应为`exec 0x08028621`
+
+其他编译器查找函数地址的方式和地址偏移的处理,请参考各编译器手册
+
+## 命令定义
+
+letter shell 3.x将可执行的函数命令定义,用户定义,按键定义以及变量定义统一归为命令定义,使用相同的结构储存,查找和执行
+
+### 定义方式
+
+letter shell 支持使用命令导出方式和命令表方式进行命令的添加,定义,通过宏```SHELL_USING_CMD_EXPORT```控制
+
+命令导出方式支持keil,IAR以及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的target 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 \
+ }
+ ```
+
+ 按键键值为在终端输入按键会发送的字符串序列,以大端模式表示,比如在SecureCRT中断,按下Tab键,会发送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)`快速声明
+
+## 代理函数和代理参数解析
+
+letter shell 3.x原生支持将整数,字符,字符串参数,以及在某些情况下的浮点参数直接传递给执行命令的函数,一般情况下,这几种参数类型完全可以满足调试需要,然而在某些情况下,用户确实需要传递其他类型的参数,此时,可以选择将命令定义成main函数形式,使用字符串传递参数,然后自行对参数进行解析,除此之外,letter shell还提供了代理函数的机制,可以对任意类型的参数进行自定义解析
+
+关于代理函数的实现原理和具体使用示例,可以参考[letter-shell代理函数解析](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/)
+
+使用代理函数,用户需要自定义代理参数解析器,即一个将基本参数(整数,字符,字符串参数)转换成目标类型参数的函数或者宏,letter shell默认实现了浮点类型的参数解析器`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));
+```
+
+相比常规的命令导出,代理函数命令导出前4个参数和常规形式的命令导出一致,之后的参数即传递至目标函数的参数,letter shell默认实现的代理函数定义支持最多7个参数,p1~p7,对于不需要代理参数解析的参数,只需要对应写入`px(x为1~7)`即可,比如上方示例的`p1`和`p3`,而需要代理参数解析的参数,则需要使用对应的参数解析器,比如上方示例的`p2`和`p4`
+
+## 函数签名
+
+letter shell 3.2.x 之后,引入了函数签名的概念,以便于参数自动解析
+
+之前的版本里,如果声明的命令是 `SHELL_TYPE_CMD_FUNC`,shell 会自动进行参数的转换,但是参数转换后的类型是猜出来的,无法保证转换后的数据类型是正确的,一旦猜错了,就容易导致程序挂掉
+
+由此,借鉴 Java 等语言的函数签名,新版也引入了函数签名的概念,在声明命令时,可以给定最终执行命令的函数的签名,shell 根据这个签名进行参数转换,使用此功能时,需要打开宏 `SHELL_USING_FUNC_SIGNATURE`
+
+函数签名是一个字符串,通过这个字符串声明表达函数的参数类型,返回值不声明,比如一个函数`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");
+```
+
+### 自定义类型解析
+
+由于函数签名的引用,我们就可以使用函数签名描述任何参数,对应的,在参数类型已知的情况下,也可以定义对应的参数解析器进行参数解析,自定义的参数类型签名需要以 `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);
+```
+
+同时,我们需要对自定义的类型定义解析器,使用 `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 皆可,第二个参数就是解析器对应的类型签名,第三个参数是解析器函数,第四个参数是清理函数,清理函数在参数解析失败或者命令执行完毕后会被调用,一般用于清理解析器分配的内存,如果不需要清理函数,填 `NULL` 即可
+
+解析器函数接收两个参数,第一个参数是输入的字符串,也就是命令行输入的参数,第二个参数是解析后的参数,解析成功后,需要将解析后的参数赋值给第二个参数,解析成功返回 0,解析失败返回 -1
+
+清理函数接收一个参数,就是解析器函数解析得到的结果
+
+## 权限系统说明
+
+letter shell 3.x的权限管理同用户定义紧密相关,letter shell 3.x使用8个bit位表示命令权限,当用户和命令的权限按位与为真,或者命令权限为0时,表示该用户拥有此命令的权限,可以调用该命令
+
+## 锁说明
+
+letter shell 3.1增加了shell锁,主要目的是为了防止shell输出和其他输入(比如说日志)对终端的竞争,导致输出混乱的现象,如果使用场景中没有出现终端输出混乱的情况,可以不使用shell锁
+
+注意: 请使用支持嵌套的锁
+
+1. 使能宏并实现锁
+
+ 使能`SHELL_USING_LOCK`宏,实现shell上锁和解锁函数,函数原型如下:
+
+ ```c
+ /**
+ * @brief shell上锁
+ *
+ * @param struct shell_def shell对象
+ *
+ * @return 0
+ */
+ typedef int (*shellLock)(struct shell_def *);
+
+ /**
+ * @brief shell解锁
+ *
+ * @param struct shell_def shell对象
+ *
+ * @return 0
+ */
+ typedef int (*shellLock)(struct shell_def *);
+ ```
+
+2. 使用锁
+
+ 在可能产生终端竞争的地方,加上shell锁,比如如果调用`shellPrint`进行格式化输出
+
+ ```C
+ SHELL_LOCK(shell);
+ shellPrint(shell, ...);
+ SHELL_UNLOCK(shell);
+ ```
+
+3. 注意
+
+ - 不要在shell命令中调用shell锁,除非实现的shell锁为可嵌套的锁
+
+## 伴生对象
+
+letter shell 3.0.3版本引入了伴生对象的概念,通过宏`SHELL_USING_COMPANION`开启或者关闭,若使用伴生对象的功能,需要同时将shell_companion.c文件加入到工程中,伴生对象可以用于需要将某个对象同shell关联的场景,比如说,通过快捷键控制shell终端对应的日志打印对象
+
+一般情况下,使用`shellCompanionAdd`将伴生对象同shell对象进行关联,之后,可以在shell操作中,通过`shellCompanionGet`获取相应的伴生对象,以达到在不同的shell中,操作不同对象的目的
+
+## 尾行模式
+
+letter shell 3.0.4版本新增了尾行模式,适用于需要在shell所使用的交互终端同时输入其他信息(比如说日志)时,防止其他信息的输出,导致shell交互体验极差的情况,使用时,使能宏`SHELL_SUPPORT_END_LINE`,然后对于其他需要使用终端输入信息的地方,调用`shellWriteEndLine`接口将信息输入,此时,调用`shellWriteEndLine`进行输入的内容将会插入到命令行上方,终端会一直保持shell命令行位于最后一行
+
+使用letter shell尾行模式结合[log](./extensions/log/readme.md)日志输出的效果如下:
+
+
+
+## 建议终端软件
+
+- 对于基于串口移植,letter shell建议使用secureCRT软件,letter shell中的相关按键映射都是按照secureCRT进行设计的,使用其他串口软件时,可能需要修改键值
+
+## 命令遍历工具
+
+letter shell 3.x提供了一个用于遍历工程中命令导出的工具,位于tools/shellTools.py,需要python3环境运行,可以列出工程中,所有使用`SHELL_EXPORT_XXX`导出的命令名,以及位置,结合VS Code可以直接进行跳转
+
+```sh
+python shellTools.py project
+```
+
+注意:shellTools会遍历指定目录中所有文件,所以当工程中文件较多时,速度会比较慢,建议只用于遍历用户模块的目录
+
+## x86 demo
+
+letter shell 3.x提供了一个x86的demo,可以直接编译运行,其中包含了一条按键键值测试命令,可以测试按键键值,用于快捷键的定义,编译运行方法如下:
+
+```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 解锁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写buffer
+ *
+ * @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 /**< 允许注册的最大log对象数量 */
+#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__ /**< 自定添加的TAG */
+#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) /**< 所有已注册的log对象 */
+
+/**
+ * 终端字体颜色代码
+ */
+#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); /**< 写buffer */
+ char active; /**< 是否激活 */
+ LogLevel level; /**< 日志级别 */
+#if LOG_USING_LOCK == 1
+ int (*lock)(struct log_def *); /**< log 加锁 */
+ int (*unlock)(struct log_def *); /**< log 解锁 */
+#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是一个用于嵌入式系统的日志打印工具,可以为日志定义不同的级别,然后设置日志工具的打印级别,可以进行日志打印的控制
+
+此外,log通过letter shell的伴生对象功能,可以和letter shell结合,实现log和shell的绑定等功能
+
+## 使用
+
+1. 实现log写buffer函数
+
+ ```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文件中的宏,可以对log工具进行配置
+
+| 宏 | 意义 |
+| --------------- | ------------------------- |
+| LOG_BUFFER_SIZE | log输出缓冲大小 |
+| LOG_USING_COLOR | 是否使用颜色 |
+| LOG_MAX_NUMBER | 允许注册的最大log对象数量 |
+| LOG_AUTO_TAG | 是否自动添加TAG |
+| LOG_END | log信息结尾 |
+| LOG_TAG | 自定添加的TAG |
+| LOG_TIME_STAMP | 设置获取系统时间戳 |
+
+## API
+
+以下是log工具部分API的说明
+
+### logPrintln
+
+宏声明,用于一般的打印输出
+
+```C
+#define logPrintln(format, ...)
+```
+
+- 参数
+ - `format` 输出格式
+ - `...` 可变参数
+
+### logError
+
+宏声明,错误日志级别输出
+
+```C
+#define logError(fmt, ...)
+```
+
+- 参数
+ - `fmt` 输出格式
+ - `...` 可变参数
+
+### logWarning
+
+宏声明,警告日志级别输出,函数原型及参数说明参考`logError`
+
+### logInfo
+
+宏声明,信息日志级别输出,函数原型及参数说明参考`logError`
+
+### logDebug
+
+宏声明,调试日志级别输出,函数原型及参数说明参考`logError`
+
+### logVerbose
+
+宏声明,冗余日志级别输出,函数原型及参数说明参考`logError`
+
+### logAssert
+
+宏声明,断言
+
+```C
+#define logAssert(expr, action)
+```
+
+- 参数
+ - `expr` 表达式
+ - `action` 断言失败执行操作
+
+### logRegister
+
+注册log对象
+
+```C
+void logRegister(Log *log, Shell *shell)
+```
+
+- 参数
+ - `log` log对象
+ - `shell` 关联的shell对象
+
+### 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和shell共用一个终端,但不影响shell交互体验
+
+1. 使用`shellWriteEndLine`事项log写buffer函数
+
+ ```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 解析参数
+ *
+ * @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 代理函数名
+ */
+#define SHELL_AGENCY_FUNC_NAME(_func) agency##_func
+
+/**
+ * @brief shell代理函数定义
+ *
+ * @param _func 被代理的函数
+ * @param ... 代理参数
+ */
+#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 代理命令定义
+ *
+ * @param _attr 命令属性
+ * @param _name 命令名
+ * @param _func 命令函数
+ * @param _desc 命令描述
+ * @param ... 代理参数
+ */
+ #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 代理按键定义
+ *
+ * @param _attr 按键属性
+ * @param _value 按键键值
+ * @param _func 按键函数
+ * @param _desc 按键描述
+ * @param ... 代理参数
+ */
+ #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 参数解析器定义
+ *
+ * @param _attr 参数解析器属性
+ * @param _type 参数解析器类型
+ * @param _parser 参数解析器函数
+ * @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 参数解析器item定义
+ *
+ * @param _attr 参数解析器属性
+ * @param _type 参数解析器类型
+ * @param _parser 参数解析器函数
+ * @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, /**< 参数解析器 */
+#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 解锁 */
+#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 **); /**< 解析函数 */
+ int (*cleaner)(void *); /**< 清理器 */
+ } paramParser; /**< 参数解析器 */
+#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..c1ddd76
--- /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"
+#include "stdlib.h"
+
+#ifdef SHELL_CFG_USER
+#include SHELL_CFG_USER
+#endif
+
+
+#ifndef SHELL_TASK_WHILE
+/**
+ * @brief 是否使用默认shell任务while循环
+ * 使能此宏,则`shellTask()`函数会一直循环读取输入,一般使用操作系统建立shell
+ * 任务时使能此宏,关闭此宏的情况下,一般适用于无操作系统,在主循环中调用`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作为命令行回车触发
+ * 可以和SHELL_ENTER_CR同时开启
+ */
+#define SHELL_ENTER_LF 1
+#endif /** SHELL_ENTER_LF */
+
+#ifndef SHELL_ENTER_CR
+/**
+ * @brief 使用CR作为命令行回车触发
+ * 可以和SHELL_ENTER_LF同时开启
+ */
+#define SHELL_ENTER_CR 1
+#endif /** SHELL_ENTER_CR */
+
+#ifndef SHELL_ENTER_CRLF
+/**
+ * @brief 使用CRLF作为命令行回车触发
+ * 不可以和SHELL_ENTER_LF或SHELL_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 快速帮助
+ * 作用于双击tab的场景,当使能此宏时,双击tab不会对命令进行help补全,而是直接显示对应命令的帮助信息
+ */
+#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 管理的最大shell数量
+ */
+#define SHELL_MAX_NUMBER 5
+#endif /** SHELL_MAX_NUMBER */
+
+#ifndef SHELL_PRINT_BUFFER
+/**
+ * @brief shell格式化输出的缓冲大小
+ * 为0时不使用shell格式化输出
+ */
+#define SHELL_PRINT_BUFFER 256
+#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)
+ * 定义此宏为获取系统Tick,如`HAL_GetTick()`
+ * @note 此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定
+ */
+#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) malloc(size)
+#endif /** SHELL_MALLOC */
+
+#ifndef SHELL_FREE
+/**
+ * @brief shell内存释放
+ * shell本身不需要此接口,若使用shell伴生对象,需要进行定义
+ */
+#define SHELL_FREE(obj) 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 "ch584_Master"
+#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 使用函数签名
+ * 使能后,可以在声明命令时,指定函数的签名,shell 会根据函数签名进行参数转换,
+ * 而不是自动判断参数的类型,如果参数和函数签名不匹配,会停止执行命令
+ */
+#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* 伴生对象,无匹配对象时返回NULL
+ */
+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 解析字符参数
+ *
+ * @param string 字符串参数
+ * @return char 解析出的字符
+ */
+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 解析字符串参数
+ *
+ * @param string 字符串参数
+ * @return char* 解析出的字符串
+ */
+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 解析数字参数
+ *
+ * @param string 字符串参数
+ * @return unsigned int 解析出的数字
+ */
+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 解析变量参数
+ *
+ * @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 解析参数
+ *
+ * @param shell shell对象
+ * @param string 参数
+ * @param type 参数类型
+ * @param result 解析结果
+ *
+ * @return int 0 解析成功 --1 解析失败
+ */
+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写buffer函数
+void Uart_Log_Write(char *buffer, short len)
+{
+ if (uartLog.shell)
+ {
+//
+// log工具可以结合letter shell的尾行模式,实现log和shell共用一个终端,但不影响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 */