添加了 shell和bt串口部分程序

This commit is contained in:
小马_666 2024-12-07 17:09:36 +08:00
parent 8c839a352e
commit 2837f1fb23
36 changed files with 7083 additions and 229 deletions

View File

@ -45,13 +45,13 @@
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.abi.fp.962468442" name="Floating point ABI" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.abi.fp" useByScannerDiscovery="false" value="ilg.gnumcueclipse.managedbuild.cross.riscv.option.abi.fp.none" valueType="enumerated"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.codemodel.1234945999" name="Code model" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.codemodel" useByScannerDiscovery="false" value="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.codemodel.any" valueType="enumerated"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.atomic.985553432" name="Atomic extension (RVA)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.atomic" useByScannerDiscovery="false" value="false" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.nocommon.268238736" name="No common unitialized (-fno-common)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.nocommon" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.rvGcc.1841559060" name="RISC-V Compiler" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.rvGcc" value="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.rvGcc.12" valueType="enumerated"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.b.301320797" name="Bit extension (RVB)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.b" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.xw.947493390" name="Extra Compressed extension (RVXW)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.xw" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.mrs.highcode.2079961129" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.mrs.highcode" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.nocommon.268238736" name="No common unitialized (-fno-common)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.nocommon" useByScannerDiscovery="true" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.rvGcc.1841559060" name="RISC-V Compiler" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.rvGcc" useByScannerDiscovery="false" value="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.rvGcc.12" valueType="enumerated"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.b.301320797" name="Bit extension (RVB)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.b" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.xw.947493390" name="Extra Compressed extension (RVXW)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.xw" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.mrs.highcode.2079961129" name="Optimize unused sections declared as high code (--param=highcode-gen-section-name=1)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.mrs.highcode" useByScannerDiscovery="true" value="true" valueType="boolean"/>
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="ilg.gnumcueclipse.managedbuild.cross.riscv.targetPlatform.1944008784" isAbstract="false" osList="all" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.targetPlatform"/>
<builder buildPath="${workspace_loc:/BLE_TYQ_BJQ_CH584M}/obj" id="ilg.gnumcueclipse.managedbuild.cross.riscv.builder.1421508906" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.builder"/>
<builder buildPath="${workspace_loc:/BLE_TYQ_BJQ_CH584M}/obj" id="ilg.gnumcueclipse.managedbuild.cross.riscv.builder.1421508906" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="GNU Make 构建器" parallelBuildOn="true" parallelizationNumber="optimal" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.builder"/>
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.assembler.1244756189" name="GNU RISC-V Cross Assembler" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.assembler">
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.assembler.usepreprocessor.1692176068" name="Use preprocessor" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.assembler.usepreprocessor" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.assembler.include.paths.1034038285" name="Include paths (-I)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.assembler.include.paths" useByScannerDiscovery="true" valueType="includePath"/>
@ -60,6 +60,9 @@
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler.1731377187" name="GNU RISC-V Cross C Compiler" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.paths.1567947810" name="Include paths (-I)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.paths" useByScannerDiscovery="true" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Startup}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/common/letter-shell-master/src}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/common/lwrb}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/bsp/inc}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/APP/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Profile/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/StdPeriphDriver/inc}&quot;"/>
@ -70,7 +73,7 @@
</option>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.std.2020844713" name="Language standard" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.std" useByScannerDiscovery="true" value="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.std.gnu99" valueType="enumerated"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.defs.177116515" name="Defined symbols (-D)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.defs" useByScannerDiscovery="true" valueType="definedSymbols">
<listOptionValue builtIn="false" value="DEBUG=0"/>
<listOptionValue builtIn="false" value="DEBUG=3"/>
<listOptionValue builtIn="false" value="CLK_OSC32K=0"/>
</option>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.files.288896968" name="Include files (-include)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.files" useByScannerDiscovery="true" valueType="includeFiles"/>
@ -144,6 +147,8 @@
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="RVMSIS"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Startup"/>
<entry excluding="CH59x_pwm.c|CH59x_usbdev.c|CH59x_usbhostClass.c|CH59x_usbhostBase.c|CH59x_spi0.c|CH59x_timer0.c|CH59x_timer1.c|CH59x_timer2.c|CH59x_timer3.c|CH59x_uart0.c|CH59x_uart2.c|CH59x_uart3.c" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="StdPeriphDriver"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="bsp"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="common"/>
</sourceEntries>
</configuration>
</storageModule>
@ -165,5 +170,5 @@
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
<storageModule moduleId="refreshScope"/>
</cproject>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<projectDescription>
<name>BLE_TYQ_BJQ_CH584M</name>
<comment></comment>
<comment/>
<projects>
</projects>
<buildSpec>
@ -26,7 +26,7 @@
<filteredResources>
<filter>
<id>1602468250279</id>
<name></name>
<name/>
<type>22</type>
<matcher>
<id>org.eclipse.ui.ide.multiFilter</id>

View File

@ -5,7 +5,7 @@
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="-485857071755691248" id="ilg.gnumcueclipse.managedbuild.cross.riscv.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT RISC-V Cross GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} ${cross_toolchain_flags} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="-542427368104949551" id="ilg.gnumcueclipse.managedbuild.cross.riscv.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT RISC-V Cross GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} ${cross_toolchain_flags} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>

View File

@ -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

23
.vscode/settings.json vendored Normal file
View File

@ -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"
}
}

View File

@ -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

View File

@ -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 //10100ms~320032s
// 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, &centralBondCB, &centralRoleCB);
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],

View File

@ -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();
}

View File

@ -3,7 +3,7 @@
* Author : WCH
* Version : V1.2
* Date : 2022/01/18
* Description : BLE和硬件初始化
* Description : Ӳ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>BLE<EFBFBD><EFBFBD>Ӳ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD>
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
@ -11,7 +11,7 @@
*******************************************************************************/
/******************************************************************************/
/* 头文件包含 */
/* ͷ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
#include "HAL.h"
tmosTaskID halTaskID;
@ -19,7 +19,7 @@ uint32_t g_LLE_IRQLibHandlerLocation;
/*******************************************************************************
* @fn Lib_Calibration_LSI
*
* @brief 32k校准
* @brief <EFBFBD>ڲ<EFBFBD>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)
{

View File

@ -3,7 +3,7 @@
* Author : WCH
* Version : V1.2
* Date : 2022/01/18
* Description :
* Description : <EFBFBD><EFBFBD><EFBFBD><EFBFBD>˵<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD><EFBFBD>ֵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ԥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>޸ĵ<EFBFBD>ǰֵ
*********************************************************************************
* 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.c修改Mac地址定义
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 flash最后512字节( :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];

View File

@ -71,6 +71,9 @@ SECTIONS
*(.glue_7t)
*(.gnu.linkonce.t.*)
. = ALIGN(4);
_shell_command_start = .;
KEEP (*(shellCommand))
_shell_command_end = .;
} >FLASH AT>FLASH
.fini :

View File

@ -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;
}

50
bsp/inc/bsp_flash.h Normal file
View File

@ -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读操作是有硬件加扰的FFDataFlash读操作没有硬件加扰
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中的地址4hardfault复位
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__

28
bsp/inc/bsp_tim.h Normal file
View File

@ -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__

67
bsp/inc/bsp_uart.h Normal file
View File

@ -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__

95
bsp/src/bsp_flash.c Normal file
View File

@ -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);
}
}

30
bsp/src/bsp_tim.c Normal file
View File

@ -0,0 +1,30 @@
#include "bsp_tim.h"
// tick_1ms_cnt在SysTick_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++;
}

243
bsp/src/bsp_uart.c Normal file
View File

@ -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;
}
}

View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

10
common/letter-shell-master/.gitignore vendored Normal file
View File

@ -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

View File

@ -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.

View File

@ -0,0 +1,655 @@
# letter shell 3.x
![version](https://img.shields.io/badge/version-3.2.0-brightgreen.svg)
![standard](https://img.shields.io/badge/standard-c99-brightgreen.svg)
![build](https://img.shields.io/badge/build-2023.04.15-brightgreen.svg)
![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)
一个功能强大的嵌入式shell
![shell_info.png](doc/img/shell_info.png)
- [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```控制
命令导出方式支持keilIAR以及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)日志输出的效果如下:
![end line mode](doc/img/shell_end_line_mode.gif)
## 建议终端软件
- 对于基于串口移植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
```

View File

@ -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 */

View File

@ -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

View File

@ -0,0 +1,215 @@
# log
![version](https://img.shields.io/badge/version-1.0.1-brightgreen.svg)
![standard](https://img.shields.io/badge/standard-c99-brightgreen.svg)
![build](https://img.shields.io/badge/build-2020.08.02-brightgreen.svg)
![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)
嵌入式日志打印工具
![preview](../../doc/img/log_preview.png)
- [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
```
即可单独控制某个文件的日志开关

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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), &param) == 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, &params[i]) != 0)
{
return -1;
}
}
else
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
{
if (shellExtParsePara(shell, argv[i + 1], NULL, &params[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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

646
common/lwrb/lwrb.c Normal file
View File

@ -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 <tilen@majerle.eu>
* 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;
}

157
common/lwrb/lwrb.h Normal file
View File

@ -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 <tilen@majerle.eu>
* Version: v3.0.0-rc1
*/
#ifndef LWRB_HDR_H
#define LWRB_HDR_H
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* \defgroup LWRB Lightweight ring buffer manager
* \brief Lightweight ring buffer manager
* \{
*/
#if !defined(LWRB_DISABLE_ATOMIC) || __DOXYGEN__
// #include <stdatomic.h>
#define LWRB_DISABLE_ATOMIC
#ifdef LWRB_DISABLE_ATOMIC
typedef unsigned long lwrb_sz_atomic_t;
#else
#include <stdatomic.h>
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 */