移植了letter shell 和 at command 测试通过
This commit is contained in:
parent
2479103ee4
commit
9ce31192c3
18
.cproject
18
.cproject
|
@ -14,7 +14,7 @@
|
|||
</extensions>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" cleanCommand="${cross_rm} -rf" description="" id="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release.1008047074" name="obj" parent="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release">
|
||||
<configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" cleanCommand="${cross_rm} -rf" description="" errorParsers="org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GmakeErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.CWDLocator;org.eclipse.cdt.core.GCCErrorParser" id="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release.1008047074" name="obj" parent="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release">
|
||||
<folderInfo id="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release.1008047074." name="/" resourcePath="">
|
||||
<toolChain id="ilg.gnumcueclipse.managedbuild.cross.riscv.toolchain.elf.release.231146001" name="RISC-V Cross GCC" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.toolchain.elf.release">
|
||||
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.addtools.createflash.1311852988" name="Create flash image" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.addtools.createflash" useByScannerDiscovery="false" value="true" valueType="boolean"/>
|
||||
|
@ -60,13 +60,18 @@
|
|||
<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=""${workspace_loc:/${ProjName}/Startup}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/APP/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/Profile/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/common/AT-Command-master/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/common/letter-shell-master/src}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/common/LwUtil}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/common/lwrb}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/StdPeriphDriver/inc}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/HAL/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/Ld}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/LIB}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/RVMSIS}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/bsp/inc}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/APP/include}""/>
|
||||
</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">
|
||||
|
@ -90,7 +95,7 @@
|
|||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/Ld/Link.ld}""/>
|
||||
</option>
|
||||
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.nostart.913830613" name="Do not use standard start files (-nostartfiles)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.nostart" useByScannerDiscovery="false" value="true" valueType="boolean"/>
|
||||
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.usenewlibnano.239404511" name="Use newlib-nano (--specs=nano.specs)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.usenewlibnano" useByScannerDiscovery="false" value="true" valueType="boolean"/>
|
||||
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.usenewlibnano.239404511" name="Use newlib-nano (--specs=nano.specs)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.usenewlibnano" useByScannerDiscovery="false" value="false" valueType="boolean"/>
|
||||
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.usenewlibnosys.351964161" name="Do not use syscalls (--specs=nosys.specs)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.usenewlibnosys" useByScannerDiscovery="false" value="true" valueType="boolean"/>
|
||||
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.otherobjs.16994550" name="Other objects" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.otherobjs" useByScannerDiscovery="false" valueType="userObjs"/>
|
||||
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.strip.1780837525" name="Omit all symbol information (-s)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.strip" useByScannerDiscovery="false" value="false" valueType="boolean"/>
|
||||
|
@ -120,7 +125,9 @@
|
|||
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.cpp.linker.usenewlibnano.1540675679" name="Use newlib-nano (--specs=nano.specs)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.cpp.linker.usenewlibnano" value="true" valueType="boolean"/>
|
||||
</tool>
|
||||
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.archiver.1292785366" name="GNU RISC-V Cross Archiver" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.archiver"/>
|
||||
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.createflash.1801165667" name="GNU RISC-V Cross Create Flash Image" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.createflash"/>
|
||||
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.createflash.1801165667" name="GNU RISC-V Cross Create Flash Image" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.createflash">
|
||||
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createflash.choice.941633854" name="Output file format (-O)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createflash.choice" useByScannerDiscovery="false" value="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createflash.choice.binary" valueType="enumerated"/>
|
||||
</tool>
|
||||
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.createlisting.1356766765" name="GNU RISC-V Cross Create Listing" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.createlisting">
|
||||
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.source.2052761852" name="Display source (--source|-S)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.source" useByScannerDiscovery="false" value="true" valueType="boolean"/>
|
||||
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.allheaders.439659821" name="Display all headers (--all-headers|-x)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.allheaders" useByScannerDiscovery="false" value="true" valueType="boolean"/>
|
||||
|
@ -145,6 +152,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>
|
||||
|
@ -166,4 +175,5 @@
|
|||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
|
||||
<storageModule moduleId="refreshScope"/>
|
||||
</cproject>
|
||||
|
|
|
@ -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="-427504215130445276" 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 "${INPUTS}"" prefer-non-shared="true">
|
||||
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="-612898175655791173" 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 "${INPUTS}"" prefer-non-shared="true">
|
||||
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||
</provider>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding//APP/bsp_wf5803.c=GBK
|
||||
encoding//APP/include/bsp_wf5803.h=GBK
|
||||
encoding//APP/peripheral_main.c=GBK
|
||||
encoding//HAL/MCU.c=GBK
|
||||
encoding//HAL/SLEEP.c=GBK
|
||||
encoding//HAL/include/CONFIG.h=GBK
|
||||
encoding//LIB/CH58xBLE_LIB.h=GBK
|
||||
encoding//LIB/CH58xBLE_ROM.h=GBK
|
||||
encoding//Ld/Link.ld=GBK
|
||||
encoding//RVMSIS/core_riscv.h=GBK
|
||||
encoding//Startup/startup_CH585.S=GBK
|
||||
encoding//StdPeriphDriver/CH58x_sys.c=GBK
|
||||
encoding//StdPeriphDriver/CH58x_uart1.c=GBK
|
||||
encoding//StdPeriphDriver/inc/CH585SFR.h=GBK
|
||||
encoding//bsp/inc/bsp_ml307r.h=GBK
|
||||
encoding//bsp/inc/bsp_uart.h=GBK
|
||||
encoding//bsp/src/bsp_ml307r.c=GBK
|
||||
encoding//bsp/src/bsp_tim.c=GBK
|
||||
encoding//bsp/src/bsp_uart.c=GBK
|
||||
encoding//common/lwrb/lwrb.c=GBK
|
||||
encoding//common/lwrb/lwrb.h=GBK
|
|
@ -16,8 +16,19 @@
|
|||
#include "HAL.h"
|
||||
#include "gattprofile.h"
|
||||
#include "peripheral.h"
|
||||
|
||||
#include "bsp_wf5803.h"
|
||||
#include "bsp_ml307r.h"
|
||||
#include "bsp_uart.h"
|
||||
#include "log.h"
|
||||
|
||||
#undef LOG_ENABLE
|
||||
#define LOG_ENABLE 1
|
||||
|
||||
#define SYSTICK_INTERVAL (1)
|
||||
|
||||
|
||||
uint8_t TxBuff[] = "This is a uart3 tx example\r\n";
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* GLOBAL TYPEDEFS
|
||||
|
@ -39,10 +50,11 @@ __HIGH_CODE
|
|||
__attribute__((noinline))
|
||||
void Main_Circulation()
|
||||
{
|
||||
PRINT("Main_Circulation\n");
|
||||
logDebug("Main_Circulation\n");
|
||||
while(1)
|
||||
{
|
||||
TMOS_SystemProcess();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,45 +72,37 @@ int main(void)
|
|||
#endif
|
||||
HSECFG_Capacitance(HSECap_18p);
|
||||
SetSysClock(CLK_SOURCE_HSE_PLL_62_4MHz);
|
||||
#if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE)
|
||||
SysTick_Config( GetSysClock() / 1000 * SYSTICK_INTERVAL); //É趨àÖàªÊ±¼ä1ms
|
||||
#if(defined(HAL_SLEEP)) && (HAL_SLEEP == FALSE)
|
||||
GPIOA_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
|
||||
GPIOB_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
// GPIOA_SetBits(GPIO_Pin_14);
|
||||
// GPIOPinRemap(ENABLE, RB_PIN_UART0);
|
||||
// GPIOA_ModeCfg(GPIO_Pin_14, GPIO_ModeOut_PP_5mA);
|
||||
// UART0_DefInit();
|
||||
// GPIOA_SetBits(GPIO_Pin_9);
|
||||
// // GPIOPinRemap(ENABLE, RB_PIN_UART1);
|
||||
// GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
|
||||
// // UART0_DefInit();
|
||||
// UART1_DefInit();
|
||||
|
||||
/* 配置串口1:先配置IO口模式,再配置串口 */
|
||||
GPIOA_SetBits(GPIO_Pin_9);
|
||||
GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); // RXD-配置上拉输入
|
||||
GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); // TXD-配置推挽输出,注意先让IO口输出高电平
|
||||
UART1_DefInit();
|
||||
BSP_UART3_Init();
|
||||
|
||||
logDebug("%s", TxBuff);
|
||||
|
||||
#endif
|
||||
PRINT("Start @ChipID=%02X\n", R8_CHIP_ID);
|
||||
PRINT("%s\n", VER_LIB);
|
||||
|
||||
CH58x_BLEInit();
|
||||
PRINT("BLE init ok\n");
|
||||
HAL_Init();
|
||||
PRINT("HAL init ok\n");
|
||||
GAPRole_PeripheralInit();
|
||||
PRINT("GAP init ok\n");
|
||||
Peripheral_Init();
|
||||
PRINT("Peripheral init ok\n");
|
||||
BSP_Ml307r_Init();
|
||||
logDebug("Start @ChipID=%02X\n", R8_CHIP_ID);
|
||||
logDebug("%s\n", VER_LIB);
|
||||
|
||||
BSP_PRESS_Init();
|
||||
PRINT("BSP init ok\n");
|
||||
// CH58x_BLEInit();
|
||||
// logDebug("BLE init ok\n");
|
||||
// HAL_Init();
|
||||
// logDebug("HAL init ok\n");
|
||||
// GAPRole_PeripheralInit();
|
||||
// logDebug("GAP init ok\n");
|
||||
// Peripheral_Init();
|
||||
// logDebug("Peripheral init ok\n");
|
||||
|
||||
// BSP_PRESS_Init();
|
||||
// logDebug("BSP init ok\n");
|
||||
|
||||
Main_Circulation();
|
||||
PRINT("Main_Circulation\n");
|
||||
logDebug("Main_Circulation\n");
|
||||
}
|
||||
|
||||
/******************************** endfile @ main ******************************/
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
/******************************************************************************/
|
||||
/* 头文件包含 */
|
||||
#include "HAL.h"
|
||||
#include "bsp_uart.h"
|
||||
|
||||
tmosTaskID halTaskID;
|
||||
uint32_t g_LLE_IRQLibHandlerLocation;
|
||||
|
@ -233,6 +234,8 @@ void HAL_Init()
|
|||
#if(defined HAL_SLEEP) && (HAL_SLEEP == TRUE)
|
||||
HAL_SleepInit();
|
||||
#endif
|
||||
|
||||
|
||||
#if(defined HAL_LED) && (HAL_LED == TRUE)
|
||||
HAL_LedInit();
|
||||
#endif
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
__HIGH_CODE
|
||||
uint32_t CH58x_LowPower(uint32_t time)
|
||||
{
|
||||
#if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE)
|
||||
#if(defined(HAL_SLEEP)) && (HAL_SLEEP == FALSE)
|
||||
volatile uint32_t i;
|
||||
uint32_t time_tign, time_sleep, time_curr;
|
||||
unsigned long irq_status;
|
||||
|
|
|
@ -66,6 +66,9 @@ SECTIONS
|
|||
*(.text.*)
|
||||
*(.rodata)
|
||||
*(.rodata*)
|
||||
_shell_command_start = .;
|
||||
KEEP (*(shellCommand))
|
||||
_shell_command_end = .;
|
||||
*(.sdata2.*)
|
||||
*(.glue_7)
|
||||
*(.glue_7t)
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
*******************************************************************************/
|
||||
|
||||
#include "CH58x_common.h"
|
||||
#include "bsp_uart.h"
|
||||
|
||||
|
||||
volatile MachineMode_Call_func gs_machine_mode_func;
|
||||
|
||||
|
@ -543,31 +545,37 @@ void mDelaymS(uint16_t t)
|
|||
}while(--t);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
//#ifdef DEBUG
|
||||
//int _write(int fd, char *buf, int size)
|
||||
//{
|
||||
// int i;
|
||||
// for(i = 0; i < size; i++)
|
||||
// {
|
||||
//#if DEBUG == Debug_UART0
|
||||
// while(R8_UART0_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
|
||||
// R8_UART0_THR = *buf++; /* 发送数据 */
|
||||
//#elif DEBUG == Debug_UART1
|
||||
// while(R8_UART1_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
|
||||
// R8_UART1_THR = *buf++; /* 发送数据 */
|
||||
//#elif DEBUG == Debug_UART2
|
||||
// while(R8_UART2_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
|
||||
// R8_UART2_THR = *buf++; /* 发送数据 */
|
||||
//#elif DEBUG == Debug_UART3
|
||||
// while(R8_UART3_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
|
||||
// R8_UART3_THR = *buf++; /* 发送数据 */
|
||||
//#endif
|
||||
// }
|
||||
// return size;
|
||||
//}
|
||||
|
||||
//#endif
|
||||
|
||||
int _write(int fd, char *buf, int size)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
#if DEBUG == Debug_UART0
|
||||
while(R8_UART0_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
|
||||
R8_UART0_THR = *buf++; /* 发送数据 */
|
||||
#elif DEBUG == Debug_UART1
|
||||
while(R8_UART1_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
|
||||
R8_UART1_THR = *buf++; /* 发送数据 */
|
||||
#elif DEBUG == Debug_UART2
|
||||
while(R8_UART2_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
|
||||
R8_UART2_THR = *buf++; /* 发送数据 */
|
||||
#elif DEBUG == Debug_UART3
|
||||
while(R8_UART3_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
|
||||
R8_UART3_THR = *buf++; /* 发送数据 */
|
||||
#endif
|
||||
}
|
||||
BSP_Uart3_Send_Data(buf, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*********************************************************************
|
||||
* @fn _sbrk
|
||||
*
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef __BSP_ML307R_H__
|
||||
#define __BSP_ML307R_H__
|
||||
|
||||
|
||||
#include "CH58x_common.h"
|
||||
|
||||
/*4G 친욥왠齡多신*/
|
||||
#define ENABLE_3_8_V GPIO_Pin_3 //PB3
|
||||
|
||||
#define USIM_DECT_PIN GPIO_Pin_5 //PB5
|
||||
#define ML307_PWR_PIN GPIO_Pin_6 //PB6
|
||||
#define ML307_RST_PIN GPIO_Pin_7 //PB7
|
||||
|
||||
#define ML307_UART_TX_PIN GPIO_Pin_13 //PB13
|
||||
#define ML307_UART_RX_PIN GPIO_Pin_12 //PB12
|
||||
|
||||
void BSP_Ml307r_Init(void);
|
||||
void Ml307r_Loop(void);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //!@__BSP_ML307R_H__
|
||||
|
||||
|
||||
|
||||
|
|
@ -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__
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef __BSP_UART_H__
|
||||
#define __BSP_UART_H__
|
||||
|
||||
|
||||
#include "CH585SFR.h"
|
||||
|
||||
|
||||
#define UART3_RX_PIN GPIO_Pin_20 //PB20 ÈÕÖ¾´òÓ¡
|
||||
#define UART3_TX_PIN GPIO_Pin_21 // PB21
|
||||
|
||||
|
||||
void BSP_UART1_Init(void);
|
||||
unsigned int BSP_Uart1_Receive_Data(void *buf, unsigned int len);
|
||||
unsigned int BSP_Uart1_Send_Data(const void *buf, unsigned int len);
|
||||
|
||||
void BSP_UART3_Init(void);
|
||||
unsigned int BSP_Uart3_Receive_Data(void *buf, unsigned int len);
|
||||
unsigned int BSP_Uart3_Send_Data(const void *buf, unsigned int len);
|
||||
void BSP_Shell_Loop(void);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
#include "bsp_ml307r.h"
|
||||
#include "at_chat.h"
|
||||
#include "at_port.h"
|
||||
#include "bsp_uart.h"
|
||||
#include "log.h"
|
||||
#include "stdio.h"
|
||||
#include "CONFIG.h"
|
||||
|
||||
|
||||
|
||||
#undef LOG_ENABLE
|
||||
#define LOG_ENABLE 1
|
||||
|
||||
#define ML307_PERIODIC_EVT (0x0001 << 0)
|
||||
|
||||
static tmosTaskID ml307_task_id = INVALID_TASK_ID;
|
||||
|
||||
at_obj_t *ml307r_obj;
|
||||
|
||||
void At_Debug(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
}
|
||||
|
||||
void Ml307_Delay_Ms(uint16_t ms)
|
||||
{
|
||||
DelayMs(ms);
|
||||
}
|
||||
|
||||
void Ml307_Delay_us(uint16_t us)
|
||||
{
|
||||
DelayUs(us);
|
||||
}
|
||||
|
||||
static const at_adapter_t at_adapter =
|
||||
{
|
||||
.write = BSP_Uart1_Send_Data,
|
||||
.read = BSP_Uart1_Receive_Data,
|
||||
.debug = At_Debug,
|
||||
.recv_bufsize = 256,
|
||||
.urc_bufsize = 512
|
||||
};
|
||||
|
||||
//模块准备就绪
|
||||
static int URC_Module_Ready_Cb(at_urc_info_t *info)
|
||||
{
|
||||
logDebug("module ready complete\r\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief urc订阅表
|
||||
*/
|
||||
static const urc_item_t urc_table[] =
|
||||
{
|
||||
{.prefix = "+CPIN: READY", .endmark = '\n', .handler = URC_Module_Ready_Cb},//模块准备就绪
|
||||
};
|
||||
|
||||
static void Ml307_Power_On(void)
|
||||
{
|
||||
#define ML307_POWER_ON_TIME 3
|
||||
|
||||
GPIOB_SetBits(ML307_PWR_PIN);
|
||||
Ml307_Delay_Ms(ML307_POWER_ON_TIME * 1000);
|
||||
GPIOB_ResetBits(ML307_PWR_PIN);
|
||||
}
|
||||
|
||||
static void Ml307_Power_Off(void)
|
||||
{
|
||||
#define M307_POWER_OFF_TIME 4
|
||||
GPIOB_SetBits(ML307_PWR_PIN);
|
||||
Ml307_Delay_Ms(M307_POWER_OFF_TIME * 1000);
|
||||
GPIOB_ResetBits(ML307_PWR_PIN);
|
||||
Ml307_Delay_Ms(100);
|
||||
}
|
||||
|
||||
int Ml307r_Gpio_Init(void)
|
||||
{
|
||||
GPIOB_SetBits(ENABLE_3_8_V);
|
||||
GPIOB_ModeCfg(ENABLE_3_8_V, GPIO_ModeOut_PP_5mA);
|
||||
|
||||
GPIOB_ResetBits(ML307_RST_PIN);
|
||||
GPIOB_ModeCfg(ML307_RST_PIN, GPIO_ModeOut_PP_5mA);
|
||||
|
||||
GPIOB_ModeCfg(USIM_DECT_PIN, GPIO_ModeIN_Floating);
|
||||
|
||||
GPIOB_ModeCfg(ML307_PWR_PIN, GPIO_ModeOut_PP_5mA);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Module_Read_State(at_env_t *e)
|
||||
{
|
||||
switch (e->state)
|
||||
{
|
||||
case 0:
|
||||
e->obj->adap->debug("Check if the Module is ready\r\n");
|
||||
e->println(e, "AT");
|
||||
e->reset_timer(e);
|
||||
e->state++;
|
||||
break;
|
||||
case 1:
|
||||
if (e->contains(e, "OK"))
|
||||
{
|
||||
e->recvclr(e);
|
||||
e->finish(e, AT_RESP_OK);
|
||||
e->obj->adap->debug("Module is ready\r\n");
|
||||
}
|
||||
if (e->is_timeout(e, 5000))
|
||||
{
|
||||
e->state--;
|
||||
if (++e->i > 3)
|
||||
{
|
||||
e->obj->adap->debug("Module error\r\n");
|
||||
e->finish(e, AT_RESP_ERROR);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Module_Get_Imei_Cb(at_response_t *r)
|
||||
{
|
||||
char imei[16] = {0};
|
||||
if (r->code == AT_RESP_OK)
|
||||
{
|
||||
if (sscanf(r->prefix, "+GSN:%s", imei) == 1)
|
||||
{
|
||||
r->obj->adap->debug("imei: %s\r\n", r->prefix);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
r->obj->adap->debug("'AT+GSN=1' command response failed!\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief 获取IMEI数据
|
||||
* @return
|
||||
*/
|
||||
static void Module_Get_Imei(void)
|
||||
{
|
||||
at_attr_t attr;
|
||||
at_attr_deinit(&attr);
|
||||
attr.prefix = "+GSN:";
|
||||
attr.cb = Module_Get_Imei_Cb;
|
||||
at_send_singlline(ml307r_obj, &attr, "AT+GSN=1");
|
||||
}
|
||||
|
||||
static void BSP_Read_Module(void)
|
||||
{
|
||||
at_do_work(ml307r_obj, NULL, Module_Read_State); // 重启后生效
|
||||
}
|
||||
|
||||
static void Module_Get_Imsi_Cb(at_response_t *r)
|
||||
{
|
||||
char imsi[60] = {0};
|
||||
if (r->code == AT_RESP_OK)
|
||||
{
|
||||
if (sscanf(r->prefix, "%s\r\n", imsi) == 1)
|
||||
{
|
||||
r->obj->adap->debug("imsi:%s\r\n", imsi);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
r->obj->adap->debug("'AT+CIMI' command response failed!\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief 获取IMSI数据
|
||||
* @return
|
||||
*/
|
||||
static void Module_Get_Imsi(void)
|
||||
{
|
||||
at_attr_t attr;
|
||||
at_attr_deinit(&attr);
|
||||
attr.prefix = "46";
|
||||
attr.suffix = "\r\n";
|
||||
attr.cb = Module_Get_Imsi_Cb;
|
||||
at_send_singlline(ml307r_obj, &attr, "AT+CIMI");
|
||||
}
|
||||
|
||||
static void Module_Get_Iccid_Cb(at_response_t *r)
|
||||
{
|
||||
char iccid[21] = {0};
|
||||
|
||||
if (r->code == AT_RESP_OK)
|
||||
{
|
||||
if (sscanf(r->prefix, "+MCCID: %s\r\n", iccid) == 1)
|
||||
{
|
||||
r->obj->adap->debug("iccid: %s\r\n", iccid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
r->obj->adap->debug("'AT+QCCID' command response failed!\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief 获取ICCID数据
|
||||
* @return
|
||||
*/
|
||||
static void Module_Get_Iccid(void)
|
||||
{
|
||||
at_attr_t attr;
|
||||
at_attr_deinit(&attr);
|
||||
attr.prefix = "+MCCID:";
|
||||
attr.cb = Module_Get_Iccid_Cb;
|
||||
at_send_singlline(ml307r_obj, &attr, "AT+MCCID");
|
||||
}
|
||||
|
||||
|
||||
void Ml307r_Loop(void)
|
||||
{
|
||||
at_obj_process(ml307r_obj);
|
||||
}
|
||||
|
||||
|
||||
__HIGH_CODE
|
||||
__attribute__((noinline))
|
||||
uint16_t Ml307r_Handle(uint8_t task_id, uint16_t events)
|
||||
{
|
||||
if (events & ML307_PERIODIC_EVT)
|
||||
{
|
||||
Ml307r_Loop();
|
||||
tmos_start_task(ml307_task_id, ML307_PERIODIC_EVT, MS1_TO_SYSTEM_TIME(5));
|
||||
return (events ^ ML307_PERIODIC_EVT);
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void BSP_Ml307r_Init(void)
|
||||
{
|
||||
Ml307r_Gpio_Init();
|
||||
Ml307_Power_On();
|
||||
BSP_UART1_Init();
|
||||
|
||||
ml307r_obj = at_obj_create(&at_adapter);//Create AT communication object
|
||||
at_obj_set_urc(ml307r_obj, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
|
||||
|
||||
ml307_task_id = TMOS_ProcessEventRegister(Ml307r_Handle);
|
||||
tmos_set_event(ml307_task_id, ML307_PERIODIC_EVT);
|
||||
|
||||
BSP_Read_Module();
|
||||
Module_Get_Imei();
|
||||
Module_Get_Imsi();
|
||||
Module_Get_Iccid();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#include "bsp_tim.h"
|
||||
|
||||
|
||||
// tick_1ms_cnt在SysTick_Handler()中1ms +1
|
||||
volatile uint32_t tick_1ms_cnt = 0;
|
||||
|
||||
/**
|
||||
* @description: 以SysTick(1ms)为基础
|
||||
* @return {uint32_t}
|
||||
*/
|
||||
uint32_t BSP_Get_Tick(void)
|
||||
{
|
||||
/* Platform implementation */
|
||||
return tick_1ms_cnt;
|
||||
}
|
||||
|
||||
|
||||
// SysTick中断函数
|
||||
__INTERRUPT
|
||||
__HIGH_CODE
|
||||
void SysTick_Handler()
|
||||
{
|
||||
static uint8_t cnt_ms = 0;
|
||||
|
||||
SysTick->SR = 0; // 清除中断标志
|
||||
tick_1ms_cnt++;
|
||||
|
||||
cnt_ms++;
|
||||
}
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
#include "bsp_uart.h"
|
||||
#include "bsp_ml307r.h"
|
||||
#include "lwrb.h"
|
||||
#include "CH58x_uart.h"
|
||||
#include "shell_port.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#define UART1_RX_BUFFER_LENGTH 512U
|
||||
#define UART1_TX_BUFFER_LENGTH 512U
|
||||
|
||||
#define UART3_RX_BUFFER_LENGTH 1024U
|
||||
#define UART3_TX_BUFFER_LENGTH 1024U
|
||||
|
||||
|
||||
lwrb_t uart1_rx_t;
|
||||
lwrb_t uart1_tx_t;
|
||||
uint8_t uart1_tx_buf[UART1_TX_BUFFER_LENGTH] = {0};
|
||||
uint8_t uart1_rx_buf[UART1_RX_BUFFER_LENGTH] = {0};
|
||||
|
||||
lwrb_t uart3_rx_t;
|
||||
lwrb_t uart3_tx_t;
|
||||
uint8_t uart3_tx_buf[UART3_TX_BUFFER_LENGTH] = {0};
|
||||
uint8_t uart3_rx_buf[UART3_RX_BUFFER_LENGTH] = {0};
|
||||
|
||||
|
||||
//串口接收
|
||||
unsigned int BSP_Uart1_Receive_Data(void *buf, unsigned int len)
|
||||
{
|
||||
return lwrb_read(&uart1_rx_t, buf, len);
|
||||
}
|
||||
|
||||
//串口发送数据
|
||||
unsigned int BSP_Uart1_Send_Data(const void *buf, unsigned int len)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
ret = lwrb_write(&uart1_tx_t, buf, len);
|
||||
UART1_INTCfg(ENABLE, RB_IER_THR_EMPTY);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//串口接收
|
||||
unsigned int BSP_Uart3_Receive_Data(void *buf, unsigned int len)
|
||||
{
|
||||
return lwrb_write(&uart3_rx_t, buf, len);
|
||||
}
|
||||
|
||||
//串口发送数据
|
||||
unsigned int BSP_Uart3_Send_Data(const void *buf, unsigned int len)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
ret = lwrb_write(&uart3_tx_t, buf, len);
|
||||
UART3_INTCfg(ENABLE, RB_IER_THR_EMPTY);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Buffer event function
|
||||
*/
|
||||
static void Uart1_evt_fn(struct lwrb* buff, lwrb_evt_type_t evt, lwrb_sz_t bp)
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case LWRB_EVT_RESET:
|
||||
printf("[EVT] Buffer reset event!\r\n");
|
||||
break;
|
||||
case LWRB_EVT_READ:
|
||||
printf("[EVT] Buffer read event: %d byte(s)!\r\n", (int)bp);
|
||||
break;
|
||||
case LWRB_EVT_WRITE:
|
||||
printf("[EVT] Buffer write event: %d byte(s)!\r\n", (int)bp);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void UART1_FifoInit(void)
|
||||
{
|
||||
lwrb_init(&uart1_tx_t, uart1_tx_buf, sizeof(uart1_tx_buf));
|
||||
lwrb_init(&uart1_rx_t, uart1_rx_buf, sizeof(uart1_rx_buf));
|
||||
|
||||
// lwrb_set_evt_fn(&uart1_rx_t, Uart1_evt_fn);
|
||||
}
|
||||
|
||||
void UART3_FifoInit(void)
|
||||
{
|
||||
lwrb_init(&uart3_tx_t, uart3_tx_buf, sizeof(uart3_tx_buf));
|
||||
lwrb_init(&uart3_rx_t, uart3_rx_buf, sizeof(uart3_rx_buf));
|
||||
|
||||
lwrb_set_evt_fn(&uart1_rx_t, Uart1_evt_fn);
|
||||
}
|
||||
|
||||
|
||||
void BSP_UART1_Init(void)
|
||||
{
|
||||
GPIOPinRemap(ENABLE, RB_PIN_UART1);
|
||||
|
||||
/* 配置串口1:先配置IO口模式,再配置串口 */
|
||||
GPIOB_SetBits(ML307_UART_RX_PIN);
|
||||
GPIOB_ModeCfg(ML307_UART_RX_PIN, GPIO_ModeIN_PU); // RXD-配置上拉输入
|
||||
GPIOB_ModeCfg(ML307_UART_TX_PIN, GPIO_ModeOut_PP_5mA); // TXD-配置推挽输出,注意先让IO口输出高电平
|
||||
UART1_DefInit();
|
||||
UART1_ByteTrigCfg(UART_1BYTE_TRIG);
|
||||
// 中断方式接收数据
|
||||
UART1_INTCfg(ENABLE, RB_IER_LINE_STAT | RB_IER_RECV_RDY | RB_IER_THR_EMPTY);
|
||||
PFIC_EnableIRQ(UART1_IRQn);
|
||||
|
||||
UART1_FifoInit();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void BSP_UART3_Init(void)
|
||||
{
|
||||
GPIOPinRemap(ENABLE, RB_PIN_UART3);
|
||||
/* 配置串口3:先配置IO口模式,再配置串口 */
|
||||
GPIOB_SetBits(UART3_TX_PIN);
|
||||
GPIOB_ModeCfg(UART3_RX_PIN, GPIO_ModeIN_PU); // RXD-配置上拉输入
|
||||
GPIOB_ModeCfg(UART3_TX_PIN, GPIO_ModeOut_PP_5mA); // TXD-配置推挽输出,注意先让IO口输出高电平
|
||||
UART3_DefInit();
|
||||
UART3_ByteTrigCfg(UART_1BYTE_TRIG);
|
||||
// 中断方式接收数据
|
||||
UART3_INTCfg(ENABLE, RB_IER_LINE_STAT | RB_IER_RECV_RDY | RB_IER_THR_EMPTY);
|
||||
PFIC_EnableIRQ(UART3_IRQn);
|
||||
|
||||
UART3_FifoInit();
|
||||
userShellInit();
|
||||
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn UART1_IRQHandler
|
||||
*
|
||||
* @brief UART1中断函数
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
__INTERRUPT
|
||||
__HIGH_CODE
|
||||
void UART1_IRQHandler(void)
|
||||
{
|
||||
uint8_t data;
|
||||
switch(UART1_GetITFlag())
|
||||
{
|
||||
case UART_II_LINE_STAT: // 线路状态错误
|
||||
{
|
||||
// UART1_GetLinSTA();
|
||||
break;
|
||||
}
|
||||
case UART_II_RECV_RDY:
|
||||
data = UART1_RecvByte();
|
||||
lwrb_write(&uart1_rx_t, &data, 1);
|
||||
break;
|
||||
case UART_II_RECV_TOUT: // 接收数据
|
||||
break;
|
||||
|
||||
case UART_II_THR_EMPTY: // 发送缓存区空,可继续发送
|
||||
if(lwrb_read(&uart1_tx_t, &data, 1))
|
||||
{
|
||||
UART1_SendByte(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
UART1_INTCfg(DISABLE, RB_IER_THR_EMPTY);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case UART_II_MODEM_CHG: // 只支持串口0
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* @fn UART3_IRQHandler
|
||||
*
|
||||
* @brief UART1中断函数
|
||||
*
|
||||
* @return none
|
||||
*/
|
||||
__INTERRUPT
|
||||
__HIGH_CODE
|
||||
void UART3_IRQHandler(void)
|
||||
{
|
||||
uint8_t data;
|
||||
switch(UART3_GetITFlag())
|
||||
{
|
||||
case UART_II_LINE_STAT: // 线路状态错误
|
||||
{
|
||||
// UART1_GetLinSTA();
|
||||
break;
|
||||
}
|
||||
case UART_II_RECV_RDY:
|
||||
case UART_II_RECV_TOUT: //接收超时
|
||||
while(R8_UART3_RFC)
|
||||
{
|
||||
shellHandler(&shell, R8_UART3_RBR);
|
||||
}
|
||||
break;
|
||||
case UART_II_THR_EMPTY: // 发送缓存区空,可继续发送
|
||||
if(lwrb_get_full(&uart3_tx_t))
|
||||
{
|
||||
lwrb_read(&uart3_tx_t, &data, 1);
|
||||
UART3_SendByte(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
UART3_INTCfg(DISABLE, RB_IER_THR_EMPTY);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case UART_II_MODEM_CHG: // 只支持串口0
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,347 @@
|
|||
/******************************************************************************
|
||||
* @brief AT command communication management V2
|
||||
*
|
||||
* Copyright (c) 2020~2022, <morro_luo@163.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apathe-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2020-01-02 roger.luo Initial version
|
||||
* 2021-01-20 roger.luo Add debugging interface, and at the same time
|
||||
* solve the problem of uninitialized linked list
|
||||
* leading to segment fault.
|
||||
* 2021-03-03 roger.luo Fix the problem of frequent print receive timeout
|
||||
* caused by not clearing the URC counter.
|
||||
* 2022-10-01 roger.luo Redesign the entire AT framework and release V2.0.
|
||||
* 2023-01-09 roger.luo Optimized the processing logic when multi-line
|
||||
* command execution fails.
|
||||
* 2023-01-10 roger.luo Resolve URC message containing ':' character
|
||||
* causing parsing failure.
|
||||
* 2023-02-23 roger.luo Added a temporary disable interface for URC to
|
||||
* solve the problem of URC matching exception when
|
||||
* receiving binary data from the modem.
|
||||
* 2023-10-27 roger.luo Added the function of transparent data transmission.
|
||||
******************************************************************************/
|
||||
#ifndef _AT_CHAT_H_
|
||||
#define _AT_CHAT_H_
|
||||
|
||||
#include "at_port.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
struct at_obj;
|
||||
struct at_adapter;
|
||||
struct at_response;
|
||||
|
||||
/**
|
||||
*@brief AT work running state.
|
||||
*/
|
||||
typedef enum {
|
||||
AT_WORK_STAT_IDLE,
|
||||
AT_WORK_STAT_READY,
|
||||
AT_WORK_STAT_RUN,
|
||||
AT_WORK_STAT_FINISH,
|
||||
AT_WORK_STAT_ABORT,
|
||||
} at_work_state;
|
||||
|
||||
/**
|
||||
*@brief AT command response code
|
||||
*/
|
||||
typedef enum {
|
||||
AT_RESP_OK = 0,
|
||||
AT_RESP_ERROR,
|
||||
AT_RESP_TIMEOUT,
|
||||
AT_RESP_ABORT
|
||||
} at_resp_code;
|
||||
|
||||
/**
|
||||
*@brief AT command request priority
|
||||
*/
|
||||
typedef enum {
|
||||
AT_PRIORITY_LOW = 0,
|
||||
AT_PRIORITY_HIGH
|
||||
} at_cmd_priority;
|
||||
|
||||
/**
|
||||
*@brief URC frame receiving status
|
||||
*/
|
||||
typedef enum {
|
||||
URC_RECV_OK = 0, /* URC frame received successfully. */
|
||||
URC_RECV_TIMEOUT /* Receive timeout (The frame prefix is matched but the suffix is not matched within AT_URC_TIMEOUT) */
|
||||
} urc_recv_status;
|
||||
|
||||
/**
|
||||
* @brief URC frame info.
|
||||
*/
|
||||
typedef struct {
|
||||
urc_recv_status status; /* URC frame receiving status.*/
|
||||
char *urcbuf; /* URC frame buffer*/
|
||||
int urclen; /* URC frame buffer length*/
|
||||
} at_urc_info_t;
|
||||
|
||||
/**
|
||||
* @brief URC subscription item.
|
||||
*/
|
||||
typedef struct {
|
||||
const char *prefix; /* URC frame prefix,such as '+CSQ:'*/
|
||||
const char endmark; /* URC frame end mark (can only be selected from AT_URC_END_MARKS)*/
|
||||
/**
|
||||
* @brief URC handler (triggered when matching prefix and mark are match)
|
||||
* @params info - URC frame info.
|
||||
* @return Indicates the remaining unreceived bytes of the current URC frame.
|
||||
* @retval 0 Indicates that the current URC frame has been completely received
|
||||
* @retval n It still needs to wait to receive n bytes (AT manager continues
|
||||
* to receive the remaining data and continues to call back this interface).
|
||||
*/
|
||||
int (*handler)(at_urc_info_t *info);
|
||||
} urc_item_t;
|
||||
|
||||
/**
|
||||
* @brief AT response information
|
||||
*/
|
||||
typedef struct {
|
||||
struct at_obj *obj; /* AT object*/
|
||||
void *params; /* User parameters (referenced from ->at_attr_t.params)*/
|
||||
at_resp_code code; /* AT command response code.*/
|
||||
unsigned short recvcnt; /* Receive data length*/
|
||||
char *recvbuf; /* Receive buffer (raw data)*/
|
||||
/* Pointer to the receiving content prefix, valid when code=AT_RESP_OK,
|
||||
if no prefix is specified, it pointer to recvbuf
|
||||
*/
|
||||
char *prefix;
|
||||
/* Pointer to the receiving content suffix, valid when code=AT_RESP_OK,
|
||||
if no suffix is specified, it pointer to recvbuf
|
||||
*/
|
||||
char *suffix;
|
||||
} at_response_t;
|
||||
|
||||
/**
|
||||
* @brief The configuration for transparent transmission mode.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Exit command(Example:AT+TRANS=0). When this command is matched through the
|
||||
* read interface, the on_exit event is generated.
|
||||
*/
|
||||
const char *exit_cmd;
|
||||
/**
|
||||
* @brief Exit event, triggered when the exit command is currently matched. At this time,
|
||||
* you can invoke 'at_raw_transport_exit' to exit the transparent transport mode.
|
||||
*/
|
||||
void (*on_exit)(void);
|
||||
/**
|
||||
* @brief Writting interface for transparent data transmission
|
||||
* @param buf Data buffer
|
||||
* @param len data length
|
||||
* @return Indicates the length of the written data
|
||||
*/
|
||||
unsigned int (*write)(const void *buf, unsigned int len);
|
||||
/**
|
||||
* @brief Reading interface for transparent data transmission
|
||||
* @param buf Data buffer
|
||||
* @param len data length
|
||||
* @return The length of the data actually read
|
||||
*/
|
||||
unsigned int (*read) (void *buf, unsigned int len);
|
||||
} at_raw_trans_conf_t;
|
||||
|
||||
/**
|
||||
* @brief AT interface adapter
|
||||
*/
|
||||
typedef struct {
|
||||
//Lock, used in OS environment, fill in NULL if not required.
|
||||
void (*lock)(void);
|
||||
//Unlock, used in OS environment, fill in NULL if not required.
|
||||
void (*unlock)(void);
|
||||
/**
|
||||
* @brief Data write operation (non-blocking)
|
||||
* @param buf Data buffer
|
||||
* @param len data length
|
||||
* @return Indicates the length of the written data
|
||||
*/
|
||||
unsigned int (*write)(const void *buf, unsigned int len);
|
||||
/**
|
||||
* @brief Data read operation (non-blocking)
|
||||
* @param buf Data buffer
|
||||
* @param len data length
|
||||
* @return The length of the data actually read
|
||||
*/
|
||||
unsigned int (*read)(void *buf, unsigned int len);
|
||||
/**
|
||||
* @brief AT error event ( if not required, fill in NULL)
|
||||
*/
|
||||
void (*error)(at_response_t *);
|
||||
/**
|
||||
* @brief Log output interface, which can print the complete AT interaction
|
||||
* process, fill in NULL if not required.
|
||||
*/
|
||||
void (*debug)(const char *fmt, ...);
|
||||
#if AT_URC_WARCH_EN
|
||||
//URC buffer size, set according to the actual maximum URC frame when used.
|
||||
unsigned short urc_bufsize;
|
||||
#endif
|
||||
//Command response receiving buffer size, set according to the actual maximum command response length
|
||||
unsigned short recv_bufsize;
|
||||
} at_adapter_t;
|
||||
|
||||
/**
|
||||
* @brief Public environment for AT work
|
||||
*/
|
||||
typedef struct at_env {
|
||||
struct at_obj *obj;
|
||||
//Public variables (add as needed), these values are reset every time a new work starts.
|
||||
int i, j, state;
|
||||
//User parameters (referenced from ->at_attr_t.params)
|
||||
void *params;
|
||||
//Set the next polling wait interval (only takes effect once)
|
||||
void (*next_wait)(struct at_env *self, unsigned int ms);
|
||||
//Reset timer
|
||||
void (*reset_timer)(struct at_env *self);
|
||||
//Timeout indication
|
||||
bool (*is_timeout)(struct at_env *self, unsigned int ms);
|
||||
//Formatted printout with newlines
|
||||
void (*println)(struct at_env *self, const char *fmt, ...);
|
||||
//Find a keyword from the received content
|
||||
char * (*contains)(struct at_env *self, const char *str);
|
||||
//Get receives buffer
|
||||
char * (*recvbuf)(struct at_env *self);
|
||||
//Get receives buffer length
|
||||
unsigned int(*recvlen)(struct at_env *self);
|
||||
//Clear the receives buffer
|
||||
void (*recvclr)(struct at_env *self);
|
||||
//Indicates whether the current work has been abort
|
||||
bool (*disposing)(struct at_env *self);
|
||||
//End the work and set the response code
|
||||
void (*finish)(struct at_env *self, at_resp_code code);
|
||||
} at_env_t;
|
||||
|
||||
/**
|
||||
*@brief AT execution callback
|
||||
*@param r AT response information (including execution results, content information returned by the device)
|
||||
*/
|
||||
typedef void (*at_callback_t)(at_response_t *r);
|
||||
|
||||
/**
|
||||
*@brief AT work polling handler
|
||||
*@param env The public operating environment for AT work, including some common
|
||||
* variables and relevant interfaces needed to communicate AT commands.
|
||||
*@return Work processing status, which determines whether to continue running the
|
||||
* work on the next polling cycle.
|
||||
*
|
||||
* @retval true Indicates that the current work processing has been finished,
|
||||
* and the work response code is set to AT_RESP_OK
|
||||
* @retval false Indicates unfinished work processing, keep running.
|
||||
*@note It must be noted that if the env->finish() operation is invoked in the
|
||||
* current work, the work will be forcibly terminated regardless of the return value.
|
||||
*/
|
||||
typedef int (*at_work_t)(at_env_t *env);
|
||||
|
||||
#if AT_WORK_CONTEXT_EN
|
||||
|
||||
/**
|
||||
*@brief AT work item context (used to monitor the entire life cycle of AT work item)
|
||||
*/
|
||||
typedef struct {
|
||||
at_work_state work_state; /* Indicates the state at which the AT work item is running. */
|
||||
at_resp_code code; /* Indicates the response code after the AT command has been run.*/
|
||||
unsigned short bufsize; /* Indicates receive buffer size*/
|
||||
unsigned short resplen; /* Indicates the actual response valid data length*/
|
||||
unsigned char *respbuf; /* Point to the receive buffer*/
|
||||
} at_context_t;
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
*@brief AT attributes
|
||||
*/
|
||||
typedef struct {
|
||||
#if AT_WORK_CONTEXT_EN
|
||||
at_context_t *ctx; /* Pointer to the Work context. */
|
||||
#endif
|
||||
void *params; /* User parameter, fill in NULL if not required. */
|
||||
const char *prefix; /* Response prefix, fill in NULL if not required. */
|
||||
const char *suffix; /* Response suffix, fill in NULL if not required. */
|
||||
at_callback_t cb; /* Response callback handler, Fill in NULL if not required. */
|
||||
unsigned short timeout; /* Response timeout(ms).. */
|
||||
unsigned char retry; /* Response error retries. */
|
||||
at_cmd_priority priority; /* Command execution priority. */
|
||||
} at_attr_t;
|
||||
|
||||
/**
|
||||
*@brief AT object.
|
||||
*/
|
||||
typedef struct at_obj {
|
||||
const at_adapter_t *adap;
|
||||
#if AT_RAW_TRANSPARENT_EN
|
||||
const at_raw_trans_conf_t *raw_conf;
|
||||
#endif
|
||||
void *user_data;
|
||||
} at_obj_t;
|
||||
|
||||
at_obj_t *at_obj_create(const at_adapter_t *);
|
||||
|
||||
void at_obj_destroy(at_obj_t *at);
|
||||
|
||||
bool at_obj_busy(at_obj_t *at);
|
||||
|
||||
void at_obj_set_user_data(at_obj_t *at, void *user_data);
|
||||
|
||||
void *at_obj_get_user_data(at_obj_t *at);
|
||||
#if AT_URC_WARCH_EN
|
||||
void at_obj_set_urc(at_obj_t *at, const urc_item_t *tbl, int count);
|
||||
|
||||
int at_obj_get_urcbuf_count(at_obj_t *at);
|
||||
|
||||
void at_obj_urc_set_enable(at_obj_t *at, int enable, unsigned short timeout);
|
||||
|
||||
#endif
|
||||
|
||||
void at_obj_process(at_obj_t *at);
|
||||
|
||||
void at_attr_deinit(at_attr_t *attr);
|
||||
|
||||
bool at_exec_cmd(at_obj_t *at, const at_attr_t *attr, const char *cmd, ...);
|
||||
|
||||
bool at_exec_vcmd(at_obj_t *at, const at_attr_t *attr, const char *cmd, va_list va);
|
||||
|
||||
bool at_send_singlline(at_obj_t *at, const at_attr_t *attr, const char *singlline);
|
||||
|
||||
bool at_send_multiline(at_obj_t *at, const at_attr_t *attr, const char **multiline);
|
||||
|
||||
bool at_send_data(at_obj_t *at, const at_attr_t *attr, const void *databuf, unsigned int bufsize);
|
||||
|
||||
bool at_custom_cmd(at_obj_t *at, const at_attr_t *attr, void (*sender)(at_env_t *env));
|
||||
|
||||
bool at_do_work(at_obj_t *at, void *params, at_work_t work);
|
||||
|
||||
void at_work_abort_all(at_obj_t *at);
|
||||
|
||||
#if AT_MEM_WATCH_EN
|
||||
unsigned int at_max_used_memory(void);
|
||||
|
||||
unsigned int at_cur_used_memory(void);
|
||||
#endif
|
||||
|
||||
#if AT_WORK_CONTEXT_EN
|
||||
|
||||
void at_context_init(at_context_t *ctx, void *respbuf, unsigned bufsize);
|
||||
|
||||
void at_context_attach(at_attr_t *attr, at_context_t *ctx);
|
||||
|
||||
at_work_state at_work_get_state(at_context_t *ctx);
|
||||
|
||||
bool at_work_is_finish(at_context_t *ctx);
|
||||
|
||||
at_resp_code at_work_get_result(at_context_t *ctx);
|
||||
|
||||
#endif //End of AT_WORK_CONTEXT_EN
|
||||
|
||||
#if AT_RAW_TRANSPARENT_EN
|
||||
void at_raw_transport_enter(at_obj_t *obj, const at_raw_trans_conf_t *conf);
|
||||
|
||||
void at_raw_transport_exit(at_obj_t *obj);
|
||||
#endif //End of AT_RAW_TRANSPARENT_EN
|
||||
|
||||
#endif //End of _AT_CHAT_H_
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* @Brief: The AT component drives the interface implementation
|
||||
* @Author: roger.luo
|
||||
* @Date: 2021-04-04
|
||||
* @Last Modified by: roger.luo
|
||||
* @Last Modified time: 2021-11-27
|
||||
*/
|
||||
#ifndef __AT_PORT_H__
|
||||
#define __AT_PORT_H__
|
||||
|
||||
/**
|
||||
*@brief Default correct response identifier.
|
||||
*/
|
||||
#define AT_DEF_RESP_OK "OK"
|
||||
/**
|
||||
*@brief Default error response identifier.
|
||||
*/
|
||||
#define AT_DEF_RESP_ERR "ERROR"
|
||||
|
||||
/**
|
||||
*@brief Default command timeout (ms)
|
||||
*/
|
||||
#define AT_DEF_TIMEOUT 500
|
||||
|
||||
/**
|
||||
*@brief Number of retries when a command timeout/error occurs.
|
||||
*/
|
||||
#define AT_DEF_RETRY 3
|
||||
|
||||
/**
|
||||
*@brief Default URC frame receive timeout (ms).
|
||||
*/
|
||||
#define AT_URC_TIMEOUT 500
|
||||
|
||||
/**
|
||||
*@brief Maximum AT command send data length (only for variable parameter commands).
|
||||
*/
|
||||
#define AT_MAX_CMD_LEN 256
|
||||
|
||||
/**
|
||||
*@brief Maximum number of work in queue (limit memory usage).
|
||||
*/
|
||||
#define AT_LIST_WORK_COUNT 32
|
||||
|
||||
/**
|
||||
*@brief Enable URC watcher.
|
||||
*/
|
||||
#define AT_URC_WARCH_EN 1
|
||||
|
||||
/**
|
||||
*@brief A list of specified URC end marks (fill in as needed, the fewer the better).
|
||||
*/
|
||||
#define AT_URC_END_MARKS ":,\n"
|
||||
/**
|
||||
*@brief Enable memory watcher.
|
||||
*/
|
||||
#define AT_MEM_WATCH_EN 1u
|
||||
|
||||
/**
|
||||
*@brief Maximum memory usage limit (Valid when AT_MEM_WATCH_EN is enabled)
|
||||
*/
|
||||
#define AT_MEM_LIMIT_SIZE (8 * 1024)
|
||||
|
||||
/**
|
||||
*@brief Enable AT work context interfaces.
|
||||
*/
|
||||
#define AT_WORK_CONTEXT_EN 1u
|
||||
|
||||
/**
|
||||
* @brief Supports raw data transparent transmission
|
||||
*/
|
||||
#define AT_RAW_TRANSPARENT_EN 1u
|
||||
|
||||
void *at_malloc(unsigned int nbytes);
|
||||
|
||||
void at_free(void *ptr);
|
||||
|
||||
unsigned int at_get_ms(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,705 @@
|
|||
#ifndef _LINUX_LIST_H
|
||||
#define _LINUX_LIST_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if !defined(__GNUC__)
|
||||
#define typeof (struct list_head)
|
||||
#endif
|
||||
|
||||
#undef offsetof
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
|
||||
/**
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
* @ptr: the pointer to the member.
|
||||
* @type: the type of the container struct this is embedded in.
|
||||
* @member: the name of the member within the struct.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(__GNUC__)
|
||||
#define container_of(ptr, type, member) ( \
|
||||
(type *)( (char *)(ptr) - offsetof(type,member) ))
|
||||
#else
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
#endif
|
||||
|
||||
/* copy from <linux/poison.h>, */
|
||||
/*
|
||||
* used to verify that nobody uses non-initialized list entries.
|
||||
*/
|
||||
#define LIST_POISON1 ((void *) 0x0)
|
||||
#define LIST_POISON2 ((void *) 0x0)
|
||||
|
||||
|
||||
#ifndef ARCH_HAS_PREFETCH
|
||||
#define ARCH_HAS_PREFETCH
|
||||
static inline void prefetch(const void *x) {;}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simple doubly linked list implementation.
|
||||
*
|
||||
* Some of the internal functions ("__xxx") are useful when
|
||||
* manipulating whole lists rather than single entries, as
|
||||
* sometimes we already know the next/prev entries and we can
|
||||
* generate better code by using them directly rather than
|
||||
* using the generic single-entry routines.
|
||||
*/
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
static inline void INIT_LIST_HEAD(struct list_head *list)
|
||||
{
|
||||
list->next = list;
|
||||
list->prev = list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a new entry between two known consecutive entries.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it after
|
||||
*
|
||||
* Insert a new entry after the specified head.
|
||||
* This is good for implementing stacks.
|
||||
*/
|
||||
static inline void list_add(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add_tail - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it before
|
||||
*
|
||||
* Insert a new entry before the specified head.
|
||||
* This is useful for implementing queues.
|
||||
*/
|
||||
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a list entry by making the prev/next entries
|
||||
* point to each other.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __list_del(struct list_head *prev, struct list_head *next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del - deletes entry from list.
|
||||
* @entry: the element to delete from the list.
|
||||
* Note: list_empty() on entry does not return true after this, the entry is
|
||||
* in an undefined state.
|
||||
*/
|
||||
static inline void list_del(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
entry->next = LIST_POISON1;
|
||||
entry->prev = LIST_POISON2;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_replace - replace old entry by new one
|
||||
* @old : the element to be replaced
|
||||
* @new : the new element to insert
|
||||
*
|
||||
* If @old was empty, it will be overwritten.
|
||||
*/
|
||||
static inline void list_replace(struct list_head *old,
|
||||
struct list_head *new)
|
||||
{
|
||||
new->next = old->next;
|
||||
new->next->prev = new;
|
||||
new->prev = old->prev;
|
||||
new->prev->next = new;
|
||||
}
|
||||
|
||||
static inline void list_replace_init(struct list_head *old,
|
||||
struct list_head *new)
|
||||
{
|
||||
list_replace(old, new);
|
||||
INIT_LIST_HEAD(old);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del_init - deletes entry from list and reinitialize it.
|
||||
* @entry: the element to delete from the list.
|
||||
*/
|
||||
static inline void list_del_init(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
INIT_LIST_HEAD(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_move - delete from one list and add as another's head
|
||||
* @list: the entry to move
|
||||
* @head: the head that will precede our entry
|
||||
*/
|
||||
static inline void list_move(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
__list_del(list->prev, list->next);
|
||||
list_add(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_move_tail - delete from one list and add as another's tail
|
||||
* @list: the entry to move
|
||||
* @head: the head that will follow our entry
|
||||
*/
|
||||
static inline void list_move_tail(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
__list_del(list->prev, list->next);
|
||||
list_add_tail(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_last - tests whether @list is the last entry in list @head
|
||||
* @list: the entry to test
|
||||
* @head: the head of the list
|
||||
*/
|
||||
static inline int list_is_last(const struct list_head *list,
|
||||
const struct list_head *head)
|
||||
{
|
||||
return list->next == head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_empty - tests whether a list is empty
|
||||
* @head: the list to test.
|
||||
*/
|
||||
static inline int list_empty(const struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_empty_careful - tests whether a list is empty and not being modified
|
||||
* @head: the list to test
|
||||
*
|
||||
* Description:
|
||||
* tests whether a list is empty _and_ checks that no other CPU might be
|
||||
* in the process of modifying either member (next or prev)
|
||||
*
|
||||
* NOTE: using list_empty_careful() without synchronization
|
||||
* can only be safe if the only activity that can happen
|
||||
* to the list entry is list_del_init(). Eg. it cannot be used
|
||||
* if another CPU could re-list_add() it.
|
||||
*/
|
||||
static inline int list_empty_careful(const struct list_head *head)
|
||||
{
|
||||
struct list_head *next = head->next;
|
||||
return (next == head) && (next == head->prev);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_singular - tests whether a list has just one entry.
|
||||
* @head: the list to test.
|
||||
*/
|
||||
static inline int list_is_singular(const struct list_head *head)
|
||||
{
|
||||
return !list_empty(head) && (head->next == head->prev);
|
||||
}
|
||||
|
||||
static inline void __list_cut_position(struct list_head *list,
|
||||
struct list_head *head, struct list_head *entry)
|
||||
{
|
||||
struct list_head *new_first = entry->next;
|
||||
list->next = head->next;
|
||||
list->next->prev = list;
|
||||
list->prev = entry;
|
||||
entry->next = list;
|
||||
head->next = new_first;
|
||||
new_first->prev = head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_cut_position - cut a list into two
|
||||
* @list: a new list to add all removed entries
|
||||
* @head: a list with entries
|
||||
* @entry: an entry within head, could be the head itself
|
||||
* and if so we won't cut the list
|
||||
*
|
||||
* This helper moves the initial part of @head, up to and
|
||||
* including @entry, from @head to @list. You should
|
||||
* pass on @entry an element you know is on @head. @list
|
||||
* should be an empty list or a list you do not care about
|
||||
* losing its data.
|
||||
*
|
||||
*/
|
||||
static inline void list_cut_position(struct list_head *list,
|
||||
struct list_head *head, struct list_head *entry)
|
||||
{
|
||||
if (list_empty(head))
|
||||
return;
|
||||
if (list_is_singular(head) &&
|
||||
(head->next != entry && head != entry))
|
||||
return;
|
||||
if (entry == head)
|
||||
INIT_LIST_HEAD(list);
|
||||
else
|
||||
__list_cut_position(list, head, entry);
|
||||
}
|
||||
|
||||
static inline void __list_splice(const struct list_head *list,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
struct list_head *first = list->next;
|
||||
struct list_head *last = list->prev;
|
||||
|
||||
first->prev = prev;
|
||||
prev->next = first;
|
||||
|
||||
last->next = next;
|
||||
next->prev = last;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice - join two lists, this is designed for stacks
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*/
|
||||
static inline void list_splice(const struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list))
|
||||
__list_splice(list, head, head->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_tail - join two lists, each list being a queue
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*/
|
||||
static inline void list_splice_tail(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list))
|
||||
__list_splice(list, head->prev, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_init - join two lists and reinitialise the emptied list.
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*
|
||||
* The list at @list is reinitialised
|
||||
*/
|
||||
static inline void list_splice_init(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list)) {
|
||||
__list_splice(list, head, head->next);
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_tail_init - join two lists and reinitialise the emptied list
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*
|
||||
* Each of the lists is a queue.
|
||||
* The list at @list is reinitialised
|
||||
*/
|
||||
static inline void list_splice_tail_init(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list)) {
|
||||
__list_splice(list, head->prev, head);
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_entry - get the struct for this entry
|
||||
* @ptr: the &struct list_head pointer.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
|
||||
/**
|
||||
* list_first_entry - get the first element from a list
|
||||
* @ptr: the list head to take the element from.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Note, that list is expected to be not empty.
|
||||
*/
|
||||
#define list_first_entry(ptr, type, member) \
|
||||
list_entry((ptr)->next, type, member)
|
||||
|
||||
/**
|
||||
* list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each(pos, head) \
|
||||
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
|
||||
pos = pos->next)
|
||||
|
||||
/**
|
||||
* __list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
*
|
||||
* This variant differs from list_for_each() in that it's the
|
||||
* simplest possible list iteration code, no prefetching is done.
|
||||
* Use this for code that knows the list to be very short (empty
|
||||
* or 1 entry) most of the time.
|
||||
*/
|
||||
#define __list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_prev - iterate over a list backwards
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_prev(pos, head) \
|
||||
for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
|
||||
pos = pos->prev)
|
||||
|
||||
/**
|
||||
* list_for_each_safe - iterate over a list safe against removal of list entry
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @n: another &struct list_head to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_safe(pos, n, head) \
|
||||
for (pos = (head)->next, n = pos->next; pos != (head); \
|
||||
pos = n, n = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @n: another &struct list_head to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_prev_safe(pos, n, head) \
|
||||
for (pos = (head)->prev, n = pos->prev; \
|
||||
prefetch(pos->prev), pos != (head); \
|
||||
pos = n, n = pos->prev)
|
||||
|
||||
/**
|
||||
* list_for_each_entry - iterate over list of given type
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry(pos, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member); \
|
||||
prefetch(pos->member.next), &pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_reverse - iterate backwards over list of given type.
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_reverse(pos, head, member) \
|
||||
for (pos = list_entry((head)->prev, typeof(*pos), member); \
|
||||
prefetch(pos->member.prev), &pos->member != (head); \
|
||||
pos = list_entry(pos->member.prev, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
|
||||
* @pos: the type * to use as a start point
|
||||
* @head: the head of the list
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Prepares a pos entry for use as a start point in list_for_each_entry_continue().
|
||||
*/
|
||||
#define list_prepare_entry(pos, head, member) \
|
||||
((pos) ? : list_entry(head, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_continue - continue iteration over list of given type
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Continue to iterate over list of given type, continuing after
|
||||
* the current position.
|
||||
*/
|
||||
#define list_for_each_entry_continue(pos, head, member) \
|
||||
for (pos = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
prefetch(pos->member.next), &pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_continue_reverse - iterate backwards from the given point
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Start to iterate over list of given type backwards, continuing after
|
||||
* the current position.
|
||||
*/
|
||||
#define list_for_each_entry_continue_reverse(pos, head, member) \
|
||||
for (pos = list_entry(pos->member.prev, typeof(*pos), member); \
|
||||
prefetch(pos->member.prev), &pos->member != (head); \
|
||||
pos = list_entry(pos->member.prev, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_from - iterate over list of given type from the current point
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Iterate over list of given type, continuing from current position.
|
||||
*/
|
||||
#define list_for_each_entry_from(pos, head, member) \
|
||||
for (; prefetch(pos->member.next), &pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_safe(pos, n, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_continue
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Iterate over list of given type, continuing after current point,
|
||||
* safe against removal of list entry.
|
||||
*/
|
||||
#define list_for_each_entry_safe_continue(pos, n, head, member) \
|
||||
for (pos = list_entry(pos->member.next, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_from
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Iterate over list of given type from current point, safe against
|
||||
* removal of list entry.
|
||||
*/
|
||||
#define list_for_each_entry_safe_from(pos, n, head, member) \
|
||||
for (n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_reverse
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Iterate backwards over list of given type, safe against removal
|
||||
* of list entry.
|
||||
*/
|
||||
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
|
||||
for (pos = list_entry((head)->prev, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.prev, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.prev, typeof(*n), member))
|
||||
|
||||
/*
|
||||
* Double linked lists with a single pointer list head.
|
||||
* Mostly useful for hash tables where the two pointer list head is
|
||||
* too wasteful.
|
||||
* You lose the ability to access the tail in O(1).
|
||||
*/
|
||||
|
||||
struct hlist_head {
|
||||
struct hlist_node *first;
|
||||
};
|
||||
|
||||
struct hlist_node {
|
||||
struct hlist_node *next, **pprev;
|
||||
};
|
||||
|
||||
#define HLIST_HEAD_INIT { .first = NULL }
|
||||
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
|
||||
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
|
||||
static inline void INIT_HLIST_NODE(struct hlist_node *h)
|
||||
{
|
||||
h->next = NULL;
|
||||
h->pprev = NULL;
|
||||
}
|
||||
|
||||
static inline int hlist_unhashed(const struct hlist_node *h)
|
||||
{
|
||||
return !h->pprev;
|
||||
}
|
||||
|
||||
static inline int hlist_empty(const struct hlist_head *h)
|
||||
{
|
||||
return !h->first;
|
||||
}
|
||||
|
||||
static inline void __hlist_del(struct hlist_node *n)
|
||||
{
|
||||
struct hlist_node *next = n->next;
|
||||
struct hlist_node **pprev = n->pprev;
|
||||
*pprev = next;
|
||||
if (next)
|
||||
next->pprev = pprev;
|
||||
}
|
||||
|
||||
static inline void hlist_del(struct hlist_node *n)
|
||||
{
|
||||
__hlist_del(n);
|
||||
n->next = LIST_POISON1;
|
||||
n->pprev = LIST_POISON2;
|
||||
}
|
||||
|
||||
static inline void hlist_del_init(struct hlist_node *n)
|
||||
{
|
||||
if (!hlist_unhashed(n)) {
|
||||
__hlist_del(n);
|
||||
INIT_HLIST_NODE(n);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
|
||||
{
|
||||
struct hlist_node *first = h->first;
|
||||
n->next = first;
|
||||
if (first)
|
||||
first->pprev = &n->next;
|
||||
h->first = n;
|
||||
n->pprev = &h->first;
|
||||
}
|
||||
|
||||
/* next must be != NULL */
|
||||
static inline void hlist_add_before(struct hlist_node *n,
|
||||
struct hlist_node *next)
|
||||
{
|
||||
n->pprev = next->pprev;
|
||||
n->next = next;
|
||||
next->pprev = &n->next;
|
||||
*(n->pprev) = n;
|
||||
}
|
||||
|
||||
static inline void hlist_add_after(struct hlist_node *n,
|
||||
struct hlist_node *next)
|
||||
{
|
||||
next->next = n->next;
|
||||
n->next = next;
|
||||
next->pprev = &n->next;
|
||||
|
||||
if(next->next)
|
||||
next->next->pprev = &next->next;
|
||||
}
|
||||
|
||||
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
|
||||
|
||||
#define hlist_for_each(pos, head) \
|
||||
for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
|
||||
pos = pos->next)
|
||||
|
||||
#define hlist_for_each_safe(pos, n, head) \
|
||||
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
|
||||
pos = n)
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry - iterate over list of given type
|
||||
* @tpos: the type * to use as a loop cursor.
|
||||
* @pos: the &struct hlist_node to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry(tpos, pos, head, member) \
|
||||
for (pos = (head)->first; \
|
||||
pos && ({ prefetch(pos->next); 1;}) && \
|
||||
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
|
||||
pos = pos->next)
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry_continue - iterate over a hlist continuing after current point
|
||||
* @tpos: the type * to use as a loop cursor.
|
||||
* @pos: the &struct hlist_node to use as a loop cursor.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry_continue(tpos, pos, member) \
|
||||
for (pos = (pos)->next; \
|
||||
pos && ({ prefetch(pos->next); 1;}) && \
|
||||
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
|
||||
pos = pos->next)
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry_from - iterate over a hlist continuing from current point
|
||||
* @tpos: the type * to use as a loop cursor.
|
||||
* @pos: the &struct hlist_node to use as a loop cursor.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry_from(tpos, pos, member) \
|
||||
for (; pos && ({ prefetch(pos->next); 1;}) && \
|
||||
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
|
||||
pos = pos->next)
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||
* @tpos: the type * to use as a loop cursor.
|
||||
* @pos: the &struct hlist_node to use as a loop cursor.
|
||||
* @n: another &struct hlist_node to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
|
||||
for (pos = (head)->first; \
|
||||
pos && ({ n = pos->next; 1; }) && \
|
||||
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
|
||||
pos = n)
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* @Brief: The AT component drives the interface implementation
|
||||
* @Author: roger.luo
|
||||
* @Date: 2021-04-04
|
||||
* @Last Modified by: roger.luo
|
||||
* @Last Modified time: 2021-11-27
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include "bsp_tim.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief Custom malloc for AT component.
|
||||
*/
|
||||
void *at_malloc(unsigned int nbytes)
|
||||
{
|
||||
return malloc(nbytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Custom free for AT component.
|
||||
*/
|
||||
void at_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the total number of milliseconds in the system.
|
||||
*/
|
||||
unsigned int at_get_ms(void)
|
||||
{
|
||||
return BSP_Get_Tick();
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
# C language utility library
|
||||
|
||||
Set of different functions and macros usually used in the various applications.
|
||||
|
||||
## Features
|
||||
|
||||
- Support for C99 or later
|
||||
- Support for minimal and maximal values
|
||||
- Support for absolute values
|
||||
- Support for unused macros
|
||||
- Support for storing and loading data from array in little- or big- endian formats
|
||||
- Support for retrieving number of elements for statically allocated array
|
||||
- Support for bitwise operations to set, clear, check or toggle bits
|
||||
- Compilation-time assert feature
|
||||
- User friendly MIT license
|
||||
|
||||
## How to use
|
||||
|
||||
Usage is very simply. Add `lwutil.c` file to compilation flag and make sure compiler has access to `lwutil.h` for include paths.
|
||||
|
||||
## Contribute
|
||||
|
||||
This is a generic library and welcomes different contributions.
|
||||
We accept issue reports or, even better, pull-requests with bugs or new features.
|
||||
|
||||
Use [c-code-style](https://github.com/MaJerle/c-code-style) rules for coding and keep the same coding consistency.
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* @Author : stark1898y 1658608470@qq.com
|
||||
* @Date : 2024-06-14 13:25:23
|
||||
* @LastEditors : stark1898y 1658608470@qq.com
|
||||
* @LastEditTime : 2024-06-14 13:25:48
|
||||
* @FilePath : \wuxi_alarm_ch32v303rct6_rtt\common\LwUtil\lwutil.c
|
||||
* @Description :
|
||||
*
|
||||
* Copyright (c) 2024 by yzy, All Rights Reserved.
|
||||
*/
|
||||
/**
|
||||
* \file lwutil.c
|
||||
* \brief Lightweight utility library
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 Tilen MAJERLE
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* This file is part of LwUTIL - Lightweight utility library.
|
||||
*
|
||||
* Author: Tilen MAJERLE <tilen@majerle.eu>
|
||||
* Version: v1.3.0
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "lwutil.h"
|
||||
|
||||
/**
|
||||
* \brief Makes ascii char array from `unsigned 8-bit` value
|
||||
* \param[in] hex: Hexadecimal data to be converted
|
||||
* \param[out] ascii: Minimum `3-bytes` long array to write value to
|
||||
*/
|
||||
void
|
||||
lwutil_u8_to_2asciis(uint8_t hex, char* ascii) {
|
||||
for (uint8_t idx = 2U; idx != 0; --idx) {
|
||||
uint8_t aux = (uint8_t)(hex >> (4U * (idx - 1U))) & 0x0FU;
|
||||
|
||||
aux = (aux <= 9U) ? (aux + 0x30U) : (aux + 0x57U);
|
||||
ascii[2U - idx] = (char)aux;
|
||||
}
|
||||
ascii[2U] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Makes ascii char array from `unsigned 16-bit` value
|
||||
* \param[in] hex: Hexadecimal data to be converted
|
||||
* \param[out] ascii: Minimum `5-bytes` long array to write value to
|
||||
*/
|
||||
void
|
||||
lwutil_u16_to_4asciis(uint16_t hex, char* ascii) {
|
||||
for (uint8_t idx = 4U; idx != 0; --idx) {
|
||||
uint8_t aux = (uint8_t)(hex >> (4U * (idx - 1U))) & 0x0FU;
|
||||
|
||||
aux = (aux <= 9U) ? (aux + 0x30U) : (aux + 0x57U);
|
||||
ascii[4U - idx] = (char)aux;
|
||||
}
|
||||
ascii[4] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Makes ascii char array from `unsigned 32-bit` value
|
||||
* \param[in] hex: Hexadecimal data to be converted
|
||||
* \param[out] ascii: Minimum `9-bytes` long array to write value to
|
||||
*/
|
||||
void
|
||||
lwutil_u32_to_8asciis(uint32_t hex, char* ascii) {
|
||||
for (uint8_t idx = 8U; idx != 0; --idx) {
|
||||
uint8_t aux = (uint8_t)(hex >> (4U * (idx - 1U))) & 0x0FU;
|
||||
|
||||
aux = (aux <= 9U) ? (aux + 0x30U) : (aux + 0x57U);
|
||||
ascii[8U - idx] = (char)aux;
|
||||
}
|
||||
ascii[8] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load variable length integer from the byte stream to the variable.
|
||||
*
|
||||
* Variable length integers (short varints) are stored with continuation bit (bit 7, 0x80),
|
||||
* which tells us if there is a next byte to be used as part of the number sequence.
|
||||
*
|
||||
* 32-bit integer can be stored with anything between `1` and `5` bytes.
|
||||
*
|
||||
* \param ptr: Array pointer to load data from
|
||||
* \param ptr_len: Input array length
|
||||
* \param val_out: Pointer to variable to write result value
|
||||
* \return Number of bytes written (stored). `0` in case of an error.
|
||||
*/
|
||||
uint8_t
|
||||
lwutil_ld_u32_varint(const void* ptr, size_t ptr_len, uint32_t* val_out) {
|
||||
size_t cnt = 0;
|
||||
uint32_t val = 0;
|
||||
const uint8_t* p_data = ptr;
|
||||
uint8_t byt;
|
||||
|
||||
if (ptr == NULL || ptr_len == 0 || val_out == NULL) {
|
||||
return 0;
|
||||
}
|
||||
do {
|
||||
byt = *p_data++;
|
||||
val |= ((uint32_t)(byt & 0x7FU)) << (cnt * 7U);
|
||||
++cnt;
|
||||
} while (--ptr_len > 0 && (byt & 0x80U) > 0);
|
||||
|
||||
/* Check memory length */
|
||||
if ((byt & 0x80U) > 0) {
|
||||
val = 0;
|
||||
cnt = 0;
|
||||
}
|
||||
*val_out = val;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Store an integer into variable length byte sequence array.
|
||||
*
|
||||
* Variable length integers (short varints) are stored with continuation bit (bit 7, 0x80),
|
||||
* which tells us if there is a next byte to be used as part of the number sequence.
|
||||
*
|
||||
* 32-bit integer can be stored with anything between `1` and `5` bytes.
|
||||
*
|
||||
* \param val: Value to encode into byte sequence
|
||||
* \param ptr: Array to write output result
|
||||
* \param ptr_len: Length of an input array
|
||||
* \return Number of bytes written (stored). `0` in case of an error.
|
||||
*/
|
||||
uint8_t
|
||||
lwutil_st_u32_varint(uint32_t val, void* ptr, size_t ptr_len) {
|
||||
uint8_t* p_data = ptr;
|
||||
size_t cnt = 0;
|
||||
|
||||
if (ptr == NULL || ptr_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
do {
|
||||
*p_data++ = (val & 0x7FU) | (val > 0x7FU ? 0x80U : 0x00U);
|
||||
val >>= 7U;
|
||||
++cnt;
|
||||
} while (--ptr_len > 0 && val > 0);
|
||||
|
||||
/* Memory check */
|
||||
if (val > 0) {
|
||||
cnt = 0;
|
||||
}
|
||||
return cnt;
|
||||
}
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* @Author : stark1898y 1658608470@qq.com
|
||||
* @Date : 2024-06-14 13:25:23
|
||||
* @LastEditors : stark1898y 1658608470@qq.com
|
||||
* @LastEditTime : 2024-06-14 13:25:40
|
||||
* @FilePath : \wuxi_alarm_ch32v303rct6_rtt\common\LwUtil\lwutil.h
|
||||
* @Description :
|
||||
*
|
||||
* Copyright (c) 2024 by yzy, All Rights Reserved.
|
||||
*/
|
||||
/**
|
||||
* \file lwutil.h
|
||||
* \brief Lightweight utility library
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 Tilen MAJERLE
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* This file is part of LwUTIL - Lightweight utility library.
|
||||
*
|
||||
* Author: Tilen MAJERLE <tilen@majerle.eu>
|
||||
* Version: v1.3.0
|
||||
*/
|
||||
#ifndef LWUTIL_HDR_H
|
||||
#define LWUTIL_HDR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
* \defgroup LWUTIL Lightweight utility
|
||||
* \brief Lightweight utility
|
||||
* \{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Get size of statically allocated array
|
||||
* Array must be declared in a form of `type var_name[element_count]`
|
||||
*
|
||||
* \param[in] x: Object to get array size of
|
||||
* \return Number of elements in array (`element_count`)
|
||||
*/
|
||||
#define LWUTIL_ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
/**
|
||||
* \brief Get size of statically allocated array
|
||||
* Array must be declared in a form of `type var_name[element_count]`
|
||||
*
|
||||
* \note This is an alias of \ref LWUTIL_ARRAYSIZE
|
||||
*
|
||||
* \param[in] x: Object to get array size of
|
||||
* \return Number of elements in array (`element_count`)
|
||||
*/
|
||||
#define LWUTIL_ASZ(x) LWUTIL_ARRAYSIZE(x)
|
||||
|
||||
/**
|
||||
* \brief Get larger value out of 2 different inputs
|
||||
* \param[in] x: First input
|
||||
* \param[in] y: Second input
|
||||
* \return Larger of both inputs
|
||||
*/
|
||||
#define LWUTIL_MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
/**
|
||||
* \brief Get smaller value out of 2 different inputs
|
||||
* \param[in] x: First input
|
||||
* \param[in] y: Second input
|
||||
* \return Smaller of both inputs
|
||||
*/
|
||||
#define LWUTIL_MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
/**
|
||||
* \brief Constrains an input number within a range
|
||||
* \param[in] x: Number to constrain
|
||||
* \param[in] a: Minimum allowed number
|
||||
* \param[in] b: Maximum allowed number
|
||||
*
|
||||
* \return `x` if `a < x < b`
|
||||
* \return `a` if `x <= a`
|
||||
* \return `b` if `x >= b`
|
||||
*
|
||||
* \note Function does not check if `a < b`. This must be handled by the user.
|
||||
* \note This is implemented as macro and return data type depends on the input number types.
|
||||
*/
|
||||
#define LWUTIL_CONSTRAIN(x, a, b) LWUTIL_MIN(LWUTIL_MAX((x), (a)), (b))
|
||||
|
||||
/**
|
||||
* \brief Maps the input number with the min and max range to the map of the output min and max range
|
||||
*
|
||||
* Mathematical calculation is:
|
||||
*
|
||||
* (x - in_min) * (out_max - out_min)
|
||||
* y = ------------------------------------ + out_min
|
||||
* (in_max - in_min)
|
||||
*
|
||||
* \note Data types depend on the user inputs. If high precision is required,
|
||||
* user can cast the input variables to appropriate type (double or float),
|
||||
* or use integer types if decimal precision is not required.
|
||||
*
|
||||
* \note Input data is not constrained between out min and out max values.
|
||||
* This may sometimes be useful for the application.
|
||||
* Use \ref LWUTIL_CONSTRAIN macro to constrain the value
|
||||
*
|
||||
* \param[in] x: Input value to map
|
||||
* \param[in] in_min: Minimum value to map from (input boundary)
|
||||
* \param[in] in_max: Maximum value to map from (input boundary)
|
||||
* \param[in] out_min: Minimum value to map to (output boundary)
|
||||
* \param[in] out_max: Maximum value to map to (output boundary)
|
||||
* \return Mapped value
|
||||
*/
|
||||
#define LWUTIL_MAP(x, in_min, in_max, out_min, out_max) \
|
||||
(((x) - (in_min)) * ((out_max) - (out_min)) / ((in_max) - (in_min)) + (out_min))
|
||||
|
||||
/**
|
||||
* \brief Get absolute value of the input
|
||||
* Returns always-positive value of the input.
|
||||
*
|
||||
* \note Special care must be taken when input variable holds
|
||||
* minimum value available for given signed integer type (char, int, long, ...).
|
||||
*
|
||||
* Making absolute value of such input means longer output data type requirement.
|
||||
*
|
||||
* Consider writing result of this function to unsigned type of same integer width.
|
||||
* For example, minimum `signed char` value is `-128` while its absolute value (`128`)
|
||||
* requires variable type of either `unsigned char` or minimum signed 16-bit (or more).
|
||||
*
|
||||
* \param[in] x: Input value
|
||||
* \return Absolute value of the input value
|
||||
*/
|
||||
#define LWUTIL_ABS(x) ((x) < 0 ? -(x) : (x))
|
||||
|
||||
/**
|
||||
* \brief Unused variable to avoid compilation warning if declared but not used
|
||||
* \param[in] x: Input variable to declare unused
|
||||
*/
|
||||
#define LWUTIL_UNUSED(x) (void)(x)
|
||||
|
||||
/**
|
||||
* \brief Dereference the pointer and assign the value to it,
|
||||
* but only if ptr is not NULL.
|
||||
*
|
||||
* \note It is fully up to user to handle to correct variable and data types
|
||||
*
|
||||
* \param[in] ptr: Pointer to check and assign to
|
||||
* \param[in] value: Value to assign
|
||||
*/
|
||||
#define LWUTIL_SET_VALUE_IF_PTR_NOT_NULL(ptr, value) \
|
||||
do { \
|
||||
if ((ptr) != NULL) { \
|
||||
*(ptr) = (value); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LWUTIL_CONCAT_BASE(x, y) x##y
|
||||
#define LWUTIL_CONCAT(s0, s1) LWUTIL_CONCAT_BASE(s0, s1)
|
||||
|
||||
/**
|
||||
* \brief Compile time assert to validate specific expression.
|
||||
* Compilation will fail if expression evaluated to zero
|
||||
* \note Can only be used with the integer types
|
||||
*
|
||||
* \param[in] exp: Expression to test. It must be compile-time evaluation
|
||||
* \param[in] random_variable_name: Meaningful variable name to be used.
|
||||
* Can be whatever until it is valid variable name
|
||||
*/
|
||||
#define LWUTIL_COMPILE_TIME_ASSERT(exp, random_variable_name) \
|
||||
typedef char LWUTIL_CONCAT2(random_variable_name, __LINE__)[!(exp) ? -1 : 1];
|
||||
|
||||
/**
|
||||
* \brief Check if all bits in the `bit_mask` are set in the input value
|
||||
* \note Can only be used with the integer types
|
||||
*
|
||||
* \param[in] val: Value to check for bits in
|
||||
* \param[in] bit_mask: Bit mask to check in value
|
||||
* \return `1` if all bits are set, `0` otherwise
|
||||
*/
|
||||
#define lwutil_bits_is_set_all(val, bit_mask) (((val) & (bit_mask)) == (bit_mask))
|
||||
|
||||
/**
|
||||
* \brief Check if any of the `bit_mask` bits is set in the input value
|
||||
* \note Can only be used with the integer types
|
||||
*
|
||||
* \param[in] val: Value to check for bits in
|
||||
* \param[in] bit_mask: Bit mask to check in value
|
||||
* \return `1` if any bit is set, `0` otherwise
|
||||
*/
|
||||
#define lwutil_bits_is_set_any(val, bit_mask) (((val) & (bit_mask)) != 0)
|
||||
|
||||
/**
|
||||
* \brief Set bit mask in the input value
|
||||
* \note Can only be used with the integer types
|
||||
*
|
||||
* \param[in] val: Value to set bits in.
|
||||
* Original input is not modified. It is pass-by-value.
|
||||
* \param[in] bit_mask: Bit mask indicating which bits to set
|
||||
* \return New value with bitwise OR between input value and bit mask
|
||||
*/
|
||||
#define lwutil_bits_set(val, bit_mask) ((val) | (bit_mask))
|
||||
|
||||
/**
|
||||
* \brief Clear bit mask in the input value
|
||||
* \note Can only be used with the integer types
|
||||
*
|
||||
* \param[in] val: Value to clear bits in.
|
||||
* Original input is not modified. It is pass-by-value.
|
||||
* \param[in] bit_mask: Bit mask indicating which bits to clear
|
||||
* \return New value with bitwise AND and negated bit_mask between input value and bit mask
|
||||
* Value has bits cleared in the bit_mask set
|
||||
*/
|
||||
#define lwutil_bits_clear(val, bit_mask) ((val) & ~(bit_mask))
|
||||
|
||||
/**
|
||||
* \brief Toggle bit mask in the input value
|
||||
* \note Can only be used with the integer types
|
||||
*
|
||||
* \param[in] val: Value to toggle bits in.
|
||||
* Original input is not modified. It is pass-by-value.
|
||||
* \param[in] bit_mask: Bit mask indicating which bits to toggle
|
||||
* \return New value with bitwise AND and negated bit_mask between input value and bit mask
|
||||
* Value has bits cleared in the bit_mask set
|
||||
*/
|
||||
#define lwutil_bits_toggle(val, bit_mask) ((val) ^ (bit_mask))
|
||||
|
||||
/**
|
||||
* \brief Store `16-bit` value to bytes array in little-endian format
|
||||
* \param[in] val: Value to write to output array
|
||||
* \param[out] ptr: Minimum `2-bytes` long output array to write value to
|
||||
*/
|
||||
static inline void
|
||||
lwutil_st_u16_le(uint16_t val, void* ptr) {
|
||||
uint8_t* p = (uint8_t*)ptr;
|
||||
|
||||
p[0] = (uint8_t)((val >> 0) & 0xFF);
|
||||
p[1] = (uint8_t)((val >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Store `32-bit` value to bytes array in little-endian format
|
||||
* \param[in] val: Value to write to output array
|
||||
* \param[out] ptr: Minimum `4-bytes` long output array to write value to
|
||||
*/
|
||||
static inline void
|
||||
lwutil_st_u32_le(uint32_t val, void* ptr) {
|
||||
uint8_t* p = (uint8_t*)ptr;
|
||||
|
||||
p[0] = (uint8_t)((val >> 0) & 0xFF);
|
||||
p[1] = (uint8_t)((val >> 8) & 0xFF);
|
||||
p[2] = (uint8_t)((val >> 16) & 0xFF);
|
||||
p[3] = (uint8_t)((val >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load `16-bit` value from bytes array in little-endian format
|
||||
* \param[in] ptr: Minimum `2-bytes` long input array to extract bytes from
|
||||
* \return `16-bit` value extracted from input array
|
||||
*/
|
||||
static inline uint16_t
|
||||
lwutil_ld_u16_le(const void* ptr) {
|
||||
const uint8_t* p = (const uint8_t*)ptr;
|
||||
return p[1] << 8 | p[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load `32-bit` value from bytes array in little-endian format
|
||||
* \param[in] ptr: Minimum `2-bytes` long input array to extract bytes from
|
||||
* \return `32-bit` value extracted from input array
|
||||
*/
|
||||
static inline uint32_t
|
||||
lwutil_ld_u32_le(const void* ptr) {
|
||||
const uint8_t* p = (const uint8_t*)ptr;
|
||||
return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Store `16-bit` value to bytes array in big-endian format
|
||||
* \param[in] val: Value to write to output array
|
||||
* \param[out] ptr: Minimum `2-bytes` long output array to write value to
|
||||
*/
|
||||
static inline void
|
||||
lwutil_st_u16_be(uint16_t val, void* ptr) {
|
||||
uint8_t* p = (uint8_t*)ptr;
|
||||
|
||||
p[0] = (uint8_t)((val >> 8) & 0xFF);
|
||||
p[1] = (uint8_t)((val >> 0) & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Store `32-bit` value to bytes array in big-endian format
|
||||
* \param[in] val: Value to write to output array
|
||||
* \param[out] ptr: Minimum `4-bytes` long output array to write value to
|
||||
*/
|
||||
static inline void
|
||||
lwutil_st_u32_be(uint32_t val, void* ptr) {
|
||||
uint8_t* p = (uint8_t*)ptr;
|
||||
|
||||
p[0] = (uint8_t)((val >> 24) & 0xFF);
|
||||
p[1] = (uint8_t)((val >> 16) & 0xFF);
|
||||
p[2] = (uint8_t)((val >> 8) & 0xFF);
|
||||
p[3] = (uint8_t)((val >> 0) & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load `16-bit` value from bytes array in big-endian format
|
||||
* \param[in] ptr: Minimum `2-bytes` long input array to extract bytes from
|
||||
* \return `16-bit` value extracted from input array
|
||||
*/
|
||||
static inline uint16_t
|
||||
lwutil_ld_u16_be(const void* ptr) {
|
||||
const uint8_t* p = (const uint8_t*)ptr;
|
||||
return p[0] << 8 | p[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load `32-bit` value from bytes array in big-endian format
|
||||
* \param[in] ptr: Minimum `4-bytes` long input array to extract bytes from
|
||||
* \return `32-bit` value extracted from input array
|
||||
*/
|
||||
static inline uint32_t
|
||||
lwutil_ld_u32_be(const void* ptr) {
|
||||
const uint8_t* p = (const uint8_t*)ptr;
|
||||
return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
|
||||
}
|
||||
|
||||
void lwutil_u8_to_2asciis(uint8_t hex, char* ascii);
|
||||
void lwutil_u16_to_4asciis(uint16_t hex, char* ascii);
|
||||
void lwutil_u32_to_8asciis(uint32_t hex, char* ascii);
|
||||
uint8_t lwutil_ld_u32_varint(const void* ptr, size_t ptr_len, uint32_t* val_out);
|
||||
uint8_t lwutil_st_u32_varint(uint32_t val, void* ptr, size_t ptr_len);
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* LWUTIL_HDR_H */
|
|
@ -0,0 +1,2 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
|
@ -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
|
|
@ -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.
|
|
@ -0,0 +1,655 @@
|
|||
# letter shell 3.x
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
一个功能强大的嵌入式shell
|
||||
|
||||

|
||||
|
||||
- [letter shell 3.x](#letter-shell-3x)
|
||||
- [简介](#简介)
|
||||
- [功能](#功能)
|
||||
- [移植说明](#移植说明)
|
||||
- [使用方式](#使用方式)
|
||||
- [函数定义](#函数定义)
|
||||
- [main函数形式](#main函数形式)
|
||||
- [普通C函数形式](#普通c函数形式)
|
||||
- [变量使用](#变量使用)
|
||||
- [在函数中获取当前shell对象](#在函数中获取当前shell对象)
|
||||
- [执行未导出函数](#执行未导出函数)
|
||||
- [命令定义](#命令定义)
|
||||
- [定义方式](#定义方式)
|
||||
- [定义宏说明](#定义宏说明)
|
||||
- [命令属性字段说明](#命令属性字段说明)
|
||||
- [代理函数和代理参数解析](#代理函数和代理参数解析)
|
||||
- [函数签名](#函数签名)
|
||||
- [自定义类型解析](#自定义类型解析)
|
||||
- [权限系统说明](#权限系统说明)
|
||||
- [锁说明](#锁说明)
|
||||
- [伴生对象](#伴生对象)
|
||||
- [尾行模式](#尾行模式)
|
||||
- [建议终端软件](#建议终端软件)
|
||||
- [命令遍历工具](#命令遍历工具)
|
||||
- [x86 demo](#x86-demo)
|
||||
|
||||
## 简介
|
||||
|
||||
[letter shell](https://github.com/NevermindZZT/letter-shell)是一个C语言编写的,可以嵌入在程序中的嵌入式shell,主要面向嵌入式设备,以C语言函数为运行单位,可以通过命令行调用,运行程序中的函数
|
||||
|
||||
相对2.x版本,letter shell 3.x增加了用户管理,权限管理,以及对文件系统的初步支持
|
||||
|
||||
此外3.x版本修改了命令格式和定义,2.x版本的工程需要经过简单的修改才能完成迁移
|
||||
|
||||
若只需要使用基础功能,可以使用[letter shell 2.x](https://github.com/NevermindZZT/letter-shell/tree/shell2.x)版本
|
||||
|
||||
使用说明可参考[Letter shell 3.0 全新出发](https://nevermindzzt.github.io/2020/01/19/Letter%20shell%203.0%E5%85%A8%E6%96%B0%E5%87%BA%E5%8F%91/)
|
||||
|
||||
如果从3.0版本迁移到3.1以上版本,请注意3.1版本对读写函数原型的修改
|
||||
|
||||
## 功能
|
||||
|
||||
- 命令自动补全
|
||||
- 快捷键功能定义
|
||||
- 命令权限管理
|
||||
- 用户管理
|
||||
- 变量支持
|
||||
- 代理函数和参数代理解析
|
||||
|
||||
## 移植说明
|
||||
|
||||
1. 定义shell对象
|
||||
|
||||
```C
|
||||
Shell shell;
|
||||
```
|
||||
|
||||
2. 定义shell读,写函数
|
||||
|
||||
对于使用letter shell 3.0版本,读写函数原型如下:
|
||||
|
||||
```C
|
||||
/**
|
||||
* @brief shell读取数据函数原型
|
||||
*
|
||||
* @param char shell读取的字符
|
||||
*
|
||||
* @return char 0 读取数据成功
|
||||
* @return char -1 读取数据失败
|
||||
*/
|
||||
typedef signed char (*shellRead)(char *);
|
||||
|
||||
/**
|
||||
* @brief shell写数据函数原型
|
||||
*
|
||||
* @param const char 需写的字符
|
||||
*/
|
||||
typedef void (*shellWrite)(const char);
|
||||
```
|
||||
|
||||
对于使用letter shell 3.1版本,为了优化效率,修改了读写函数原型,如下:
|
||||
|
||||
```C
|
||||
/**
|
||||
* @brief shell读取数据函数原型
|
||||
*
|
||||
* @param data shell读取的字符
|
||||
* @param len 请求读取的字符数量
|
||||
*
|
||||
* @return unsigned short 实际读取到的字符数量
|
||||
*/
|
||||
typedef unsigned short (*shellRead)(char *data, unsigned short len);
|
||||
|
||||
/**
|
||||
* @brief shell写数据函数原型
|
||||
*
|
||||
* @param data 需写的字符数据
|
||||
* @param len 需要写入的字符数
|
||||
*
|
||||
* @return unsigned short 实际写入的字符数量
|
||||
*/
|
||||
typedef unsigned short (*shellWrite)(char *data, unsigned short len);
|
||||
```
|
||||
|
||||
3. 申请一片缓冲区
|
||||
|
||||
```C
|
||||
char shellBuffer[512];
|
||||
```
|
||||
|
||||
4. 调用shellInit进行初始化
|
||||
|
||||
```C
|
||||
shell.read = shellRead;
|
||||
shell.write = shellWrite;
|
||||
shellInit(&shell, shellBuffer, 512);
|
||||
```
|
||||
|
||||
5. 调用(建立)shell任务
|
||||
|
||||
对于运行在操作系统的情况,建立`shellTask`任务(确保sell_cfg.h中的配置无误),任务参数为shell对象
|
||||
|
||||
```C
|
||||
OsTaskCreate(shellTask, &shell, ...);
|
||||
```
|
||||
|
||||
对于裸机环境,在主循环中调用`shellTask`,或者在接收到数据时,调用`shellHandler`
|
||||
|
||||
6. 说明
|
||||
|
||||
- 对于中断方式使用shell,不用定义`shell->read`,但需要在中断中调用`shellHandler`
|
||||
- 对于使用操作系统的情况,使能`SHEHLL_TASK_WHILE`宏,然后创建shellTask任务
|
||||
|
||||
7. 其他配置
|
||||
|
||||
- 定义宏`SHELL_GET_TICK()`为获取系统tick函数,使能tab双击操作,用户长帮助补全
|
||||
|
||||
8. 配置宏
|
||||
|
||||
shell_cfg.h文件中包含了所有用于配置shell的宏,在使用前,需要根据需要进行配置
|
||||
|
||||
建议采用 overlay 的方式,新建一个头文件,例如 `shell_cfg_user.h`,然后定义编译宏 `SHELL_CFG_USER="shell_cfg_user.h"`,在这个头文件中添加需要修改的配置即可
|
||||
|
||||
| 宏 | 意义 |
|
||||
| --------------------------- | ------------------------------ |
|
||||
| SHELL_TASK_WHILE | 是否使用默认shell任务while循环 |
|
||||
| SHELL_USING_CMD_EXPORT | 是否使用命令导出方式 |
|
||||
| SHELL_USING_COMPANION | 是否使用shell伴生对象功能 |
|
||||
| SHELL_SUPPORT_END_LINE | 是否支持shell尾行模式 |
|
||||
| SHELL_HELP_LIST_USER | 是否在输入命令列表中列出用户 |
|
||||
| SHELL_HELP_LIST_VAR | 是否在输入命令列表中列出变量 |
|
||||
| SHELL_HELP_LIST_KEY | 是否在输入命令列表中列出按键 |
|
||||
| SHELL_ENTER_LF | 使用LF作为命令行回车触发 |
|
||||
| SHELL_ENTER_CR | 使用CR作为命令行回车触发 |
|
||||
| SHELL_ENTER_CRLF | 使用CRLF作为命令行回车触发 |
|
||||
| SHELL_EXEC_UNDEF_FUNC | 使用执行未导出函数的功能 |
|
||||
| SHELL_COMMAND_MAX_LENGTH | shell命令最大长度 |
|
||||
| SHELL_PARAMETER_MAX_NUMBER | shell命令参数最大数量 |
|
||||
| SHELL_HISTORY_MAX_NUMBER | 历史命令记录数量 |
|
||||
| SHELL_DOUBLE_CLICK_TIME | 双击间隔(ms) |
|
||||
| SHELL_QUICK_HELP | 快速帮助 |
|
||||
| SHELL_MAX_NUMBER | 管理的最大shell数量 |
|
||||
| SHELL_GET_TICK() | 获取系统时间(ms) |
|
||||
| SHELL_USING_LOCK | 是否使用锁 |
|
||||
| SHELL_MALLOC(size) | 内存分配函数(shell本身不需要) |
|
||||
| SHELL_FREE(obj) | 内存释放函数(shell本身不需要) |
|
||||
| SHELL_SHOW_INFO | 是否显示shell信息 |
|
||||
| SHELL_CLS_WHEN_LOGIN | 是否在登录后清除命令行 |
|
||||
| SHELL_DEFAULT_USER | shell默认用户 |
|
||||
| SHELL_DEFAULT_USER_PASSWORD | 默认用户密码 |
|
||||
| SHELL_LOCK_TIMEOUT | shell自动锁定超时 |
|
||||
| SHELL_USING_FUNC_SIGNATURE | 使用函数签名 |
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 函数定义
|
||||
|
||||
letter shell 3.x同时支持两种形式的函数定义方式,形如main函数定义的`func(int argc, char *agrv[])`以及形如普通C函数的定义`func(int i, char *str, ...)`,两种函数定义方式适用于不同的场景
|
||||
|
||||
#### main函数形式
|
||||
|
||||
使用此方式,一个函数定义的例子如下:
|
||||
|
||||
```C
|
||||
int func(int argc, char *agrv[])
|
||||
{
|
||||
printf("%dparameter(s)\r\n", argc);
|
||||
for (char i = 1; i < argc; i++)
|
||||
{
|
||||
printf("%s\r\n", argv[i]);
|
||||
}
|
||||
}
|
||||
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), func, func, test);
|
||||
```
|
||||
|
||||
终端调用
|
||||
|
||||
```sh
|
||||
letter:/$ func "hello world"
|
||||
2 parameter(s)
|
||||
hello world
|
||||
```
|
||||
|
||||
#### 普通C函数形式
|
||||
|
||||
使用此方式,shell会自动对参数进行转化处理,目前支持二进制,八进制,十进制,十六进制整形,字符,字符串的自动处理,如果需要其他类型的参数,请使用代理参数解析的方式(参考[代理函数和代理参数解析](#代理函数和代理参数解析)),或者使用字符串的方式作为参数,自行进行处理,例子如下:
|
||||
|
||||
```C
|
||||
int func(int i, char ch, char *str)
|
||||
{
|
||||
printf("input int: %d, char: %c, string: %s\r\n", i, ch, str);
|
||||
}
|
||||
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), func, func, test);
|
||||
```
|
||||
|
||||
终端调用
|
||||
|
||||
```sh
|
||||
letter:/$ func 666 'A' "hello world"
|
||||
input int: 666, char: A, string: hello world
|
||||
```
|
||||
|
||||
### 变量使用
|
||||
|
||||
letter shell 3.x支持导出变量,通过命令行查看,设置以及使用变量的值
|
||||
|
||||
- 导出变量
|
||||
|
||||
变量导出使用`SHELL_EXPORT_VAR`宏,支持整形(char, short, int),字符串,指针以及节点变量,变量导出需要使用引用的方式,如果不允许对变量进行修改,在属性中添加`SHELL_CMD_READ_ONLY`
|
||||
|
||||
```C
|
||||
int varInt = 0;
|
||||
SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_INT), varInt, &varInt, test);
|
||||
|
||||
char str[] = "test string";
|
||||
SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_STRING), varStr, str, test);
|
||||
|
||||
Log log;
|
||||
SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_POINT), log, &log, test);
|
||||
```
|
||||
|
||||
- 查看变量
|
||||
|
||||
在命令行直接输入导出的变量名即可查看变量当前的值
|
||||
|
||||
```sh
|
||||
letter:/$ varInt
|
||||
varInt = 0, 0x00000000
|
||||
|
||||
letter:/$ varStr
|
||||
varStr = "test string"
|
||||
```
|
||||
|
||||
- 修改变量
|
||||
|
||||
使用`setVar`命令修改变量的值,对于字符串型变量,请确认字符串有分配足够的空间,指针类型的变量不可修改
|
||||
|
||||
```sh
|
||||
letter:/$ setVar varInt 45678
|
||||
varInt = 45678, 0x0000b26e
|
||||
|
||||
letter:/$ setVar varStr "hello"
|
||||
varStr = "hello"
|
||||
```
|
||||
|
||||
- 使用变量
|
||||
|
||||
letter shell 3.x的变量可以在命令中作为参数传递,对于需要传递结构体引用到命令中的场景特别适用,使用`$`+变量名的方式传递
|
||||
|
||||
```sh
|
||||
letter:/$ shellPrint $shell "hello world\r\n"
|
||||
hello world
|
||||
```
|
||||
|
||||
### 在函数中获取当前shell对象
|
||||
|
||||
letter shell采取一个静态数组对定义的多个shell进行管理,shell数量可以修改宏`SHELL_MAX_NUMBER`定义(为了不使用动态内存分配,此处通过数据进行管理),从而,在shell执行的函数中,可以调用`shellGetCurrent()`获得当前活动的shell对象,从而可以实现某一个函数在不同的shell对象中发生不同的行为,也可以通过这种方式获得shell对象后,调用`shellWriteString(shell, string)`进行shell的输出
|
||||
|
||||
### 执行未导出函数
|
||||
|
||||
letter shell支持通过函数地址直接执行函数,可以方便执行那些没有导出,但是又临时需要使用的函数,使用命令`exec [addr] [args]`执行,使用此功能需要开启`SHELL_EXEC_UNDEF_FUNC`宏,注意,由于直接操作函数地址执行,如果给进的地址有误,可能引起程序崩溃
|
||||
|
||||
函数的地址可以通过编译生成的文件查找,比如说对于keil,可以在`.map`文件中查找到每个函数的地址,但是要注意有些平台可能需要要对地址做进一步处理,比如说对于 arm 平台,如果使用的是 Thumb 指令集,那么需要将地址的最低位置 1,比如说`shellClear`函数地址为`0x08028620`,则通过`exec`执行应为`exec 0x08028621`
|
||||
|
||||
其他编译器查找函数地址的方式和地址偏移的处理,请参考各编译器手册
|
||||
|
||||
## 命令定义
|
||||
|
||||
letter shell 3.x将可执行的函数命令定义,用户定义,按键定义以及变量定义统一归为命令定义,使用相同的结构储存,查找和执行
|
||||
|
||||
### 定义方式
|
||||
|
||||
letter shell 支持使用命令导出方式和命令表方式进行命令的添加,定义,通过宏```SHELL_USING_CMD_EXPORT```控制
|
||||
|
||||
命令导出方式支持keil,IAR以及GCC
|
||||
|
||||
1. 命令导出方式
|
||||
|
||||
letter shell 支持在函数体外部,采用定义常量的方式定义命令,例如`SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE (SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,help, shellHelp, show command info\r\nhelp [cmd]);`
|
||||
|
||||
对于使用keil进行编译,需要在keil的target option中增加--keep shellCommand*,防止定义的命令被优化掉
|
||||
|
||||
使用GCC编译时,需要在ld文件中的只读数据区(建议)添加:
|
||||
|
||||
```ld
|
||||
_shell_command_start = .;
|
||||
KEEP (*(shellCommand))
|
||||
_shell_command_end = .;
|
||||
```
|
||||
|
||||
2. 命令表方式
|
||||
|
||||
- 当使用其他暂时不支持使用命令导出方式的编译器时,需要在`shell_cmd_list.c`文件的命令表中添加
|
||||
|
||||
```C
|
||||
const SHELL_CommandTypeDef shellDefaultCommandList[] =
|
||||
{
|
||||
SHELL_CMD_ITEM(
|
||||
SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN,
|
||||
help, shellHelp, show command info\r\nhelp [cmd]),
|
||||
};
|
||||
```
|
||||
|
||||
### 定义宏说明
|
||||
|
||||
letter shell 3.x对可执行命令,按键,用户以及变量分别提供了一个宏,用于进行命令定义
|
||||
|
||||
1. 可执行命令定义
|
||||
|
||||
使用宏`SHELL_EXPORT_CMD`定义可执行命令,定义如下
|
||||
|
||||
```C
|
||||
/**
|
||||
* @brief shell 命令定义
|
||||
*
|
||||
* @param _attr 命令属性
|
||||
* @param _name 命令名
|
||||
* @param _func 命令函数
|
||||
* @param _desc 命令描述
|
||||
*/
|
||||
#define SHELL_EXPORT_CMD(_attr, _name, _func, _desc) \
|
||||
const char shellCmd##_name[] = #_name; \
|
||||
const char shellDesc##_name[] = #_desc; \
|
||||
SHELL_USED const ShellCommand \
|
||||
shellCommand##_name SHELL_SECTION("shellCommand") = \
|
||||
{ \
|
||||
.attr.value = _attr, \
|
||||
.data.cmd.name = shellCmd##_name, \
|
||||
.data.cmd.function = (int (*)())_func, \
|
||||
.data.cmd.desc = shellDesc##_name \
|
||||
}
|
||||
```
|
||||
|
||||
2. 变量定义
|
||||
|
||||
使用宏`SHELL_EXPORT_VAR`定义变量,定义如下
|
||||
|
||||
```C
|
||||
/**
|
||||
* @brief shell 变量定义
|
||||
*
|
||||
* @param _attr 变量属性
|
||||
* @param _name 变量名
|
||||
* @param _value 变量值
|
||||
* @param _desc 变量描述
|
||||
*/
|
||||
#define SHELL_EXPORT_VAR(_attr, _name, _value, _desc) \
|
||||
const char shellCmd##_name[] = #_name; \
|
||||
const char shellDesc##_name[] = #_desc; \
|
||||
SHELL_USED const ShellCommand \
|
||||
shellVar##_name SHELL_SECTION("shellCommand") = \
|
||||
{ \
|
||||
.attr.value = _attr, \
|
||||
.data.var.name = shellCmd##_name, \
|
||||
.data.var.value = (void *)_value, \
|
||||
.data.var.desc = shellDesc##_name \
|
||||
}
|
||||
```
|
||||
|
||||
变量定义时,`_value`应该是变量的引用,如果变量不允许修改,则需要在增加`SHELL_CMD_READ_ONLY`属性
|
||||
|
||||
3. 用户定义
|
||||
|
||||
使用宏`SHELL_EXPORT_USER`定义用户,定义如下
|
||||
|
||||
```C
|
||||
/**
|
||||
* @brief shell 用户定义
|
||||
*
|
||||
* @param _attr 用户属性
|
||||
* @param _name 用户名
|
||||
* @param _password 用户密码
|
||||
* @param _desc 用户描述
|
||||
*/
|
||||
#define SHELL_EXPORT_USER(_attr, _name, _password, _desc) \
|
||||
const char shellCmd##_name[] = #_name; \
|
||||
const char shellPassword##_name[] = #_password; \
|
||||
const char shellDesc##_name[] = #_desc; \
|
||||
SHELL_USED const ShellCommand \
|
||||
shellUser##_name SHELL_SECTION("shellCommand") = \
|
||||
{ \
|
||||
.attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_USER), \
|
||||
.data.user.name = shellCmd##_name, \
|
||||
.data.user.password = shellPassword##_name, \
|
||||
.data.user.desc = shellDesc##_name \
|
||||
}
|
||||
```
|
||||
|
||||
4. 按键定义
|
||||
|
||||
使用宏`SHELL_EXPORT_KEY`定义按键,定义如下
|
||||
|
||||
```C
|
||||
/**
|
||||
* @brief shell 按键定义
|
||||
*
|
||||
* @param _attr 按键属性
|
||||
* @param _value 按键键值
|
||||
* @param _func 按键函数
|
||||
* @param _desc 按键描述
|
||||
*/
|
||||
#define SHELL_EXPORT_KEY(_attr, _value, _func, _desc) \
|
||||
const char shellDesc##_value[] = #_desc; \
|
||||
SHELL_USED const ShellCommand \
|
||||
shellKey##_value SHELL_SECTION("shellCommand") = \
|
||||
{ \
|
||||
.attr.value = _attr|SHELL_CMD_TYPE(SHELL_TYPE_KEY), \
|
||||
.data.key.value = _value, \
|
||||
.data.key.function = (void (*)(Shell *))_func, \
|
||||
.data.key.desc = shellDesc##_value \
|
||||
}
|
||||
```
|
||||
|
||||
按键键值为在终端输入按键会发送的字符串序列,以大端模式表示,比如在SecureCRT中断,按下Tab键,会发送0x0B,则这个按键的键值为0x0B000000,如果按下方向上,会依次发送0x1B, 0x5B, 0x41, 则这个键的键值为0x1B5B4100
|
||||
|
||||
### 命令属性字段说明
|
||||
|
||||
在命令定义中,有一个`attr`字段,表示该命令的属性,具体定义为
|
||||
|
||||
```C
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned char permission : 8; /**< command权限 */
|
||||
ShellCommandType type : 4; /**< command类型 */
|
||||
unsigned char enableUnchecked : 1; /**< 在未校验密码的情况下可用 */
|
||||
unsigned char readOnly : 1; /**< 只读 */
|
||||
unsigned char reserve : 1; /**< 保留 */
|
||||
unsigned char paramNum : 4; /**< 参数数量 */
|
||||
} attrs;
|
||||
int value;
|
||||
} attr;
|
||||
```
|
||||
|
||||
在定义命令时,需要给定这些值,可以通过宏`SHELL_CMD_PERMISSION(permission)`, `SHELL_CMD_TYPE(type)`, `SHELL_CMD_ENABLE_UNCHECKED`, `SHELL_CMD_DISABLE_RETURN`, `SHELL_CMD_READ_ONLY`, `SHELL_CMD_PARAM_NUM(num)`快速声明
|
||||
|
||||
## 代理函数和代理参数解析
|
||||
|
||||
letter shell 3.x原生支持将整数,字符,字符串参数,以及在某些情况下的浮点参数直接传递给执行命令的函数,一般情况下,这几种参数类型完全可以满足调试需要,然而在某些情况下,用户确实需要传递其他类型的参数,此时,可以选择将命令定义成main函数形式,使用字符串传递参数,然后自行对参数进行解析,除此之外,letter shell还提供了代理函数的机制,可以对任意类型的参数进行自定义解析
|
||||
|
||||
关于代理函数的实现原理和具体使用示例,可以参考[letter-shell代理函数解析](https://nevermindzzt.github.io/2020/04/17/letter-shell%E4%BB%A3%E7%90%86%E5%87%BD%E6%95%B0%E8%A7%A3%E6%9E%90/)
|
||||
|
||||
使用代理函数,用户需要自定义代理参数解析器,即一个将基本参数(整数,字符,字符串参数)转换成目标类型参数的函数或者宏,letter shell默认实现了浮点类型的参数解析器`SHELL_PARAM_FLOAT(x)`
|
||||
|
||||
然后,使用代理函数命令导出宏定义命令,比如需要需要传递多个浮点参数的函数,如下
|
||||
|
||||
```C
|
||||
void test(int a, float b, int c, float d)
|
||||
{
|
||||
printf("%d, %f, %d, %f \r\n", a, b, c, d);
|
||||
}
|
||||
SHELL_EXPORT_CMD_AGENCY(SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
|
||||
test, test, test,
|
||||
p1, SHELL_PARAM_FLOAT(p2), p3, SHELL_PARAM_FLOAT(p4));
|
||||
```
|
||||
|
||||
相比常规的命令导出,代理函数命令导出前4个参数和常规形式的命令导出一致,之后的参数即传递至目标函数的参数,letter shell默认实现的代理函数定义支持最多7个参数,p1~p7,对于不需要代理参数解析的参数,只需要对应写入`px(x为1~7)`即可,比如上方示例的`p1`和`p3`,而需要代理参数解析的参数,则需要使用对应的参数解析器,比如上方示例的`p2`和`p4`
|
||||
|
||||
## 函数签名
|
||||
|
||||
letter shell 3.2.x 之后,引入了函数签名的概念,以便于参数自动解析
|
||||
|
||||
之前的版本里,如果声明的命令是 `SHELL_TYPE_CMD_FUNC`,shell 会自动进行参数的转换,但是参数转换后的类型是猜出来的,无法保证转换后的数据类型是正确的,一旦猜错了,就容易导致程序挂掉
|
||||
|
||||
由此,借鉴 Java 等语言的函数签名,新版也引入了函数签名的概念,在声明命令时,可以给定最终执行命令的函数的签名,shell 根据这个签名进行参数转换,使用此功能时,需要打开宏 `SHELL_USING_FUNC_SIGNATURE`
|
||||
|
||||
函数签名是一个字符串,通过这个字符串声明表达函数的参数类型,返回值不声明,比如一个函数`int func(int a, char *b, char c)`,它的函数签名就是 `isc`
|
||||
|
||||
基本类型的参数签名定义如下:
|
||||
|
||||
| 类型 | 签名 |
|
||||
| -------------------- | ---- |
|
||||
| char(字符) | c |
|
||||
| int/short/char(数字) | i |
|
||||
| char * (字符串) | s |
|
||||
| pointer | p |
|
||||
|
||||
声明命令时,在最后添加一个参数 `.data.cmd.signature = "isc"` 即可,比如:
|
||||
|
||||
```c
|
||||
void shellFuncSignatureTest(int a, char *b, char c)
|
||||
{
|
||||
printf("a = %d, b = %s, c = %c\r\n", a, b, c);
|
||||
}
|
||||
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
|
||||
funcSignatureTest, shellFuncSignatureTest, test function signature, .data.cmd.signature = "isc");
|
||||
```
|
||||
|
||||
### 自定义类型解析
|
||||
|
||||
由于函数签名的引用,我们就可以使用函数签名描述任何参数,对应的,在参数类型已知的情况下,也可以定义对应的参数解析器进行参数解析,自定义的参数类型签名需要以 `L` 开头,以 `;` 结尾,比如说定义一个 `TestStruct` 结构体类型为 `LTestStruct;`,那么接收这个结构体为参数的函数就可以通过这个类型签名定义函数签名,并导出命令
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
int a;
|
||||
char *b;
|
||||
} TestStruct;
|
||||
|
||||
void shellParamParserTest(int a, TestStruct *data, char *c)
|
||||
{
|
||||
printf("a = %d, data->a = %d, data->b = %s, c = %s\r\n", a, data->a, data->b, c);
|
||||
}
|
||||
SHELL_EXPORT_CMD_SIGN(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
|
||||
paramParserTest, shellParamParserTest, test function signature and param parser, iLTestStruct;s);
|
||||
```
|
||||
|
||||
同时,我们需要对自定义的类型定义解析器,使用 `SHELL_EXPORT_PARAM_PARSER` 宏
|
||||
|
||||
```c
|
||||
int testStructParser(char *string, void **param)
|
||||
{
|
||||
TestStruct *data = malloc(sizeof(TestStruct));
|
||||
data->b = malloc(16);
|
||||
if (sscanf(string, "%d %s", &(data->a), data->b) == 2)
|
||||
{
|
||||
*param = (void *)data;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int testStructClener(void *param)
|
||||
{
|
||||
TestStruct *data = (TestStruct *)param;
|
||||
free(data->b);
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
SHELL_EXPORT_PARAM_PARSER(0, LTestStruct;, testStructParser, testStructClener);
|
||||
```
|
||||
|
||||
`SHELL_EXPORT_PARAM_PARSER` 接收四个参数,第一个参数表示属性,这里一般填 0 皆可,第二个参数就是解析器对应的类型签名,第三个参数是解析器函数,第四个参数是清理函数,清理函数在参数解析失败或者命令执行完毕后会被调用,一般用于清理解析器分配的内存,如果不需要清理函数,填 `NULL` 即可
|
||||
|
||||
解析器函数接收两个参数,第一个参数是输入的字符串,也就是命令行输入的参数,第二个参数是解析后的参数,解析成功后,需要将解析后的参数赋值给第二个参数,解析成功返回 0,解析失败返回 -1
|
||||
|
||||
清理函数接收一个参数,就是解析器函数解析得到的结果
|
||||
|
||||
## 权限系统说明
|
||||
|
||||
letter shell 3.x的权限管理同用户定义紧密相关,letter shell 3.x使用8个bit位表示命令权限,当用户和命令的权限按位与为真,或者命令权限为0时,表示该用户拥有此命令的权限,可以调用该命令
|
||||
|
||||
## 锁说明
|
||||
|
||||
letter shell 3.1增加了shell锁,主要目的是为了防止shell输出和其他输入(比如说日志)对终端的竞争,导致输出混乱的现象,如果使用场景中没有出现终端输出混乱的情况,可以不使用shell锁
|
||||
|
||||
注意: 请使用支持嵌套的锁
|
||||
|
||||
1. 使能宏并实现锁
|
||||
|
||||
使能`SHELL_USING_LOCK`宏,实现shell上锁和解锁函数,函数原型如下:
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief shell上锁
|
||||
*
|
||||
* @param struct shell_def shell对象
|
||||
*
|
||||
* @return 0
|
||||
*/
|
||||
typedef int (*shellLock)(struct shell_def *);
|
||||
|
||||
/**
|
||||
* @brief shell解锁
|
||||
*
|
||||
* @param struct shell_def shell对象
|
||||
*
|
||||
* @return 0
|
||||
*/
|
||||
typedef int (*shellLock)(struct shell_def *);
|
||||
```
|
||||
|
||||
2. 使用锁
|
||||
|
||||
在可能产生终端竞争的地方,加上shell锁,比如如果调用`shellPrint`进行格式化输出
|
||||
|
||||
```C
|
||||
SHELL_LOCK(shell);
|
||||
shellPrint(shell, ...);
|
||||
SHELL_UNLOCK(shell);
|
||||
```
|
||||
|
||||
3. 注意
|
||||
|
||||
- 不要在shell命令中调用shell锁,除非实现的shell锁为可嵌套的锁
|
||||
|
||||
## 伴生对象
|
||||
|
||||
letter shell 3.0.3版本引入了伴生对象的概念,通过宏`SHELL_USING_COMPANION`开启或者关闭,若使用伴生对象的功能,需要同时将shell_companion.c文件加入到工程中,伴生对象可以用于需要将某个对象同shell关联的场景,比如说,通过快捷键控制shell终端对应的日志打印对象
|
||||
|
||||
一般情况下,使用`shellCompanionAdd`将伴生对象同shell对象进行关联,之后,可以在shell操作中,通过`shellCompanionGet`获取相应的伴生对象,以达到在不同的shell中,操作不同对象的目的
|
||||
|
||||
## 尾行模式
|
||||
|
||||
letter shell 3.0.4版本新增了尾行模式,适用于需要在shell所使用的交互终端同时输入其他信息(比如说日志)时,防止其他信息的输出,导致shell交互体验极差的情况,使用时,使能宏`SHELL_SUPPORT_END_LINE`,然后对于其他需要使用终端输入信息的地方,调用`shellWriteEndLine`接口将信息输入,此时,调用`shellWriteEndLine`进行输入的内容将会插入到命令行上方,终端会一直保持shell命令行位于最后一行
|
||||
|
||||
使用letter shell尾行模式结合[log](./extensions/log/readme.md)日志输出的效果如下:
|
||||
|
||||

|
||||
|
||||
## 建议终端软件
|
||||
|
||||
- 对于基于串口移植,letter shell建议使用secureCRT软件,letter shell中的相关按键映射都是按照secureCRT进行设计的,使用其他串口软件时,可能需要修改键值
|
||||
|
||||
## 命令遍历工具
|
||||
|
||||
letter shell 3.x提供了一个用于遍历工程中命令导出的工具,位于tools/shellTools.py,需要python3环境运行,可以列出工程中,所有使用`SHELL_EXPORT_XXX`导出的命令名,以及位置,结合VS Code可以直接进行跳转
|
||||
|
||||
```sh
|
||||
python shellTools.py project
|
||||
```
|
||||
|
||||
注意:shellTools会遍历指定目录中所有文件,所以当工程中文件较多时,速度会比较慢,建议只用于遍历用户模块的目录
|
||||
|
||||
## x86 demo
|
||||
|
||||
letter shell 3.x提供了一个x86的demo,可以直接编译运行,其中包含了一条按键键值测试命令,可以测试按键键值,用于快捷键的定义,编译运行方法如下:
|
||||
|
||||
```sh
|
||||
cd demo/x86-gcc/
|
||||
cmake .
|
||||
make
|
||||
./LetterShell
|
||||
```
|
|
@ -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 */
|
|
@ -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
|
|
@ -0,0 +1,215 @@
|
|||
# log
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
嵌入式日志打印工具
|
||||
|
||||

|
||||
|
||||
- [log](#log)
|
||||
- [简介](#简介)
|
||||
- [使用](#使用)
|
||||
- [配置](#配置)
|
||||
- [API](#api)
|
||||
- [logPrintln](#logprintln)
|
||||
- [logError](#logerror)
|
||||
- [logWarning](#logwarning)
|
||||
- [logInfo](#loginfo)
|
||||
- [logDebug](#logdebug)
|
||||
- [logVerbose](#logverbose)
|
||||
- [logAssert](#logassert)
|
||||
- [logRegister](#logregister)
|
||||
- [logSetLevel](#logsetlevel)
|
||||
- [logHexDump](#loghexdump)
|
||||
- [结合letter shell尾行模式](#结合letter-shell尾行模式)
|
||||
- [其他用法](#其他用法)
|
||||
- [单独控制某个文件日志](#单独控制某个文件日志)
|
||||
|
||||
## 简介
|
||||
|
||||
log是一个用于嵌入式系统的日志打印工具,可以为日志定义不同的级别,然后设置日志工具的打印级别,可以进行日志打印的控制
|
||||
|
||||
此外,log通过letter shell的伴生对象功能,可以和letter shell结合,实现log和shell的绑定等功能
|
||||
|
||||
## 使用
|
||||
|
||||
1. 实现log写buffer函数
|
||||
|
||||
```C
|
||||
void uartLogWrite(char *buffer, short len)
|
||||
{
|
||||
serialTransmit(&debugSerial, (uint8_t *)buffer, len, 0x100);
|
||||
}
|
||||
```
|
||||
|
||||
2. 定义log对象
|
||||
|
||||
```C
|
||||
Log uartLog = {
|
||||
.write = uartLogWrite,
|
||||
.active = true,
|
||||
.level = LOG_DEBUG
|
||||
};
|
||||
```
|
||||
|
||||
3. 注册log对象
|
||||
|
||||
```C
|
||||
logRegister(&uartLog, NULL);
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
通过修改log.h文件中的宏,可以对log工具进行配置
|
||||
|
||||
| 宏 | 意义 |
|
||||
| --------------- | ------------------------- |
|
||||
| LOG_BUFFER_SIZE | log输出缓冲大小 |
|
||||
| LOG_USING_COLOR | 是否使用颜色 |
|
||||
| LOG_MAX_NUMBER | 允许注册的最大log对象数量 |
|
||||
| LOG_AUTO_TAG | 是否自动添加TAG |
|
||||
| LOG_END | log信息结尾 |
|
||||
| LOG_TAG | 自定添加的TAG |
|
||||
| LOG_TIME_STAMP | 设置获取系统时间戳 |
|
||||
|
||||
## API
|
||||
|
||||
以下是log工具部分API的说明
|
||||
|
||||
### logPrintln
|
||||
|
||||
宏声明,用于一般的打印输出
|
||||
|
||||
```C
|
||||
#define logPrintln(format, ...)
|
||||
```
|
||||
|
||||
- 参数
|
||||
- `format` 输出格式
|
||||
- `...` 可变参数
|
||||
|
||||
### logError
|
||||
|
||||
宏声明,错误日志级别输出
|
||||
|
||||
```C
|
||||
#define logError(fmt, ...)
|
||||
```
|
||||
|
||||
- 参数
|
||||
- `fmt` 输出格式
|
||||
- `...` 可变参数
|
||||
|
||||
### logWarning
|
||||
|
||||
宏声明,警告日志级别输出,函数原型及参数说明参考`logError`
|
||||
|
||||
### logInfo
|
||||
|
||||
宏声明,信息日志级别输出,函数原型及参数说明参考`logError`
|
||||
|
||||
### logDebug
|
||||
|
||||
宏声明,调试日志级别输出,函数原型及参数说明参考`logError`
|
||||
|
||||
### logVerbose
|
||||
|
||||
宏声明,冗余日志级别输出,函数原型及参数说明参考`logError`
|
||||
|
||||
### logAssert
|
||||
|
||||
宏声明,断言
|
||||
|
||||
```C
|
||||
#define logAssert(expr, action)
|
||||
```
|
||||
|
||||
- 参数
|
||||
- `expr` 表达式
|
||||
- `action` 断言失败执行操作
|
||||
|
||||
### logRegister
|
||||
|
||||
注册log对象
|
||||
|
||||
```C
|
||||
void logRegister(Log *log, Shell *shell)
|
||||
```
|
||||
|
||||
- 参数
|
||||
- `log` log对象
|
||||
- `shell` 关联的shell对象
|
||||
|
||||
### logSetLevel
|
||||
|
||||
设置日志级别
|
||||
|
||||
```C
|
||||
void logSetLevel(Log *log, LogLevel level)
|
||||
```
|
||||
|
||||
- 参数
|
||||
- `log` log对象
|
||||
- `level` 日志级别
|
||||
|
||||
### logHexDump
|
||||
|
||||
数据16进制打印
|
||||
|
||||
```C
|
||||
void logHexDump(Log *log, LogLevel level, void *base, unsigned int length)
|
||||
```
|
||||
|
||||
- 参数
|
||||
- `log` log对象
|
||||
- `level` 日志级别
|
||||
- `base` 数据基址
|
||||
- `length` 数据长度
|
||||
|
||||
## 结合letter shell尾行模式
|
||||
|
||||
log工具可以结合letter shell的尾行模式,实现log和shell共用一个终端,但不影响shell交互体验
|
||||
|
||||
1. 使用`shellWriteEndLine`事项log写buffer函数
|
||||
|
||||
```C
|
||||
void uartLogWrite(char *buffer, short len)
|
||||
{
|
||||
if (uartLog.shell)
|
||||
{
|
||||
shellWriteEndLine(uartLog.shell, buffer, len);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. 定义log对象
|
||||
|
||||
```C
|
||||
Log uartLog = {
|
||||
.write = uartLogWrite,
|
||||
.active = true,
|
||||
.level = LOG_DEBUG
|
||||
};
|
||||
```
|
||||
|
||||
3. 注册log对象
|
||||
|
||||
```C
|
||||
logRegister(&uartLog, &shell);
|
||||
```
|
||||
|
||||
## 其他用法
|
||||
|
||||
### 单独控制某个文件日志
|
||||
|
||||
log工具可以单独对某个文件的日志进行打印控制,使用时,在对应的.c文件中加入
|
||||
|
||||
```C
|
||||
#undef LOG_ENABLE
|
||||
#define LOG_ENABLE 1
|
||||
```
|
||||
|
||||
即可单独控制某个文件的日志开关
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -0,0 +1,262 @@
|
|||
/**
|
||||
* @file shell_cfg.h
|
||||
* @author Letter (nevermindzzt@gmail.com)
|
||||
* @brief shell config
|
||||
* @version 3.0.0
|
||||
* @date 2019-12-31
|
||||
*
|
||||
* @copyright (c) 2019 Letter
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SHELL_CFG_H__
|
||||
#define __SHELL_CFG_H__
|
||||
|
||||
#include "bsp_tim.h"
|
||||
|
||||
|
||||
#ifdef SHELL_CFG_USER
|
||||
#include SHELL_CFG_USER
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SHELL_TASK_WHILE
|
||||
/**
|
||||
* @brief 是否使用默认shell任务while循环
|
||||
* 使能此宏,则`shellTask()`函数会一直循环读取输入,一般使用操作系统建立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 30
|
||||
#endif /** SHELL_MAX_NUMBER */
|
||||
|
||||
#ifndef SHELL_PRINT_BUFFER
|
||||
/**
|
||||
* @brief shell格式化输出的缓冲大小
|
||||
* 为0时不使用shell格式化输出
|
||||
*/
|
||||
#define SHELL_PRINT_BUFFER 128
|
||||
#endif /** SHELL_PRINT_BUFFER */
|
||||
|
||||
#ifndef SHELL_SCAN_BUFFER
|
||||
/**
|
||||
* @brief shell格式化输入的缓冲大小
|
||||
* 为0时不使用shell格式化输入
|
||||
* @note shell格式化输入会阻塞shellTask, 仅适用于在有操作系统的情况下使用
|
||||
*/
|
||||
#define SHELL_SCAN_BUFFER 0
|
||||
#endif /** SHELL_SCAN_BUFFER */
|
||||
|
||||
#ifndef SHELL_GET_TICK
|
||||
/**
|
||||
* @brief 获取系统时间(ms)
|
||||
* 定义此宏为获取系统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) 0
|
||||
#endif /** SHELL_MALLOC */
|
||||
|
||||
#ifndef SHELL_FREE
|
||||
/**
|
||||
* @brief shell内存释放
|
||||
* shell本身不需要此接口,若使用shell伴生对象,需要进行定义
|
||||
*/
|
||||
#define SHELL_FREE(obj)
|
||||
#endif /** SHELL_FREE */
|
||||
|
||||
#ifndef SHELL_SHOW_INFO
|
||||
/**
|
||||
* @brief 是否显示shell信息
|
||||
*/
|
||||
#define SHELL_SHOW_INFO 0
|
||||
#endif /** SHELL_SHOW_INFO */
|
||||
|
||||
#ifndef SHELL_CLS_WHEN_LOGIN
|
||||
/**
|
||||
* @brief 是否在登录后清除命令行
|
||||
*/
|
||||
#define SHELL_CLS_WHEN_LOGIN 1
|
||||
#endif /** SHELL_CLS_WHEN_LOGIN */
|
||||
|
||||
#ifndef SHELL_DEFAULT_USER
|
||||
/**
|
||||
* @brief shell默认用户
|
||||
*/
|
||||
#define SHELL_DEFAULT_USER "ch585"
|
||||
#endif /** SHELL_DEFAULT_USER */
|
||||
|
||||
#ifndef SHELL_DEFAULT_USER_PASSWORD
|
||||
/**
|
||||
* @brief shell默认用户密码
|
||||
* 若默认用户不需要密码,设为""
|
||||
*/
|
||||
#define SHELL_DEFAULT_USER_PASSWORD ""
|
||||
#endif /** SHELL_DEFAULT_USER_PASSWORD */
|
||||
|
||||
#ifndef SHELL_LOCK_TIMEOUT
|
||||
/**
|
||||
* @brief shell自动锁定超时
|
||||
* shell当前用户密码有效的时候生效,超时后会自动重新锁定shell
|
||||
* 设置为0时关闭自动锁定功能,时间单位为`SHELL_GET_TICK()`单位
|
||||
* @note 使用超时锁定必须保证`SHELL_GET_TICK()`有效
|
||||
*/
|
||||
#define SHELL_LOCK_TIMEOUT 0 * 60 * 1000
|
||||
#endif /** SHELL_LOCK_TIMEOUT */
|
||||
|
||||
#ifndef SHELL_USING_FUNC_SIGNATURE
|
||||
/**
|
||||
* @brief 使用函数签名
|
||||
* 使能后,可以在声明命令时,指定函数的签名,shell 会根据函数签名进行参数转换,
|
||||
* 而不是自动判断参数的类型,如果参数和函数签名不匹配,会停止执行命令
|
||||
*/
|
||||
#define SHELL_USING_FUNC_SIGNATURE 1
|
||||
#endif /** SHELL_USING_FUNC_SIGNATURE */
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -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 */
|
|
@ -0,0 +1,658 @@
|
|||
/**
|
||||
* @file shell_ext.c
|
||||
* @author Letter (NevermindZZT@gmail.com)
|
||||
* @brief shell extensions
|
||||
* @version 3.0.0
|
||||
* @date 2019-12-31
|
||||
*
|
||||
* @copyright (c) 2019 Letter
|
||||
*
|
||||
*/
|
||||
|
||||
#include "shell_cfg.h"
|
||||
#include "shell.h"
|
||||
#include "shell_ext.h"
|
||||
#include "string.h"
|
||||
|
||||
extern ShellCommand* shellSeekCommand(Shell *shell,
|
||||
const char *cmd,
|
||||
ShellCommand *base,
|
||||
unsigned short compareLength);
|
||||
extern int shellGetVarValue(Shell *shell, ShellCommand *command);
|
||||
|
||||
#if SHELL_USING_FUNC_SIGNATURE == 1
|
||||
/**
|
||||
* @brief 获取下一个参数类型
|
||||
*
|
||||
* @param signature 函数签名
|
||||
* @param index 参数遍历在签名中的起始索引
|
||||
* @param type 获取到的参数类型
|
||||
*
|
||||
* @return int 下一个参数在签名中的索引
|
||||
*/
|
||||
static int shellGetNextParamType(const char *signature, int index, char *type)
|
||||
{
|
||||
const char *p = signature + index;
|
||||
if (*p == 'L')
|
||||
{
|
||||
while (*p != ';' && *p != 0)
|
||||
{
|
||||
*type++ = *p++;
|
||||
index++;
|
||||
}
|
||||
*type++ = *p++;
|
||||
index++;
|
||||
}
|
||||
else if (*p != 0)
|
||||
{
|
||||
*type++ = *p;
|
||||
index++;
|
||||
}
|
||||
*type = '\0';
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
static int shellGetParamNumExcept(const char *signature)
|
||||
{
|
||||
int num = 0;
|
||||
const char *p = signature;
|
||||
|
||||
while (*p)
|
||||
{
|
||||
if (*p == 'L')
|
||||
{
|
||||
while (*p != ';' && *p != 0)
|
||||
{
|
||||
p++;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
else
|
||||
{
|
||||
p++;
|
||||
}
|
||||
num++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 判断数字进制
|
||||
*
|
||||
* @param string 参数字符串
|
||||
* @return ShellNumType 进制
|
||||
*/
|
||||
static ShellNumType shellExtNumType(char *string)
|
||||
{
|
||||
char *p = string;
|
||||
ShellNumType type = NUM_TYPE_DEC;
|
||||
|
||||
if ((*p == '0') && ((*(p + 1) == 'x') || (*(p + 1) == 'X')))
|
||||
{
|
||||
type = NUM_TYPE_HEX;
|
||||
}
|
||||
else if ((*p == '0') && ((*(p + 1) == 'b') || (*(p + 1) == 'B')))
|
||||
{
|
||||
type = NUM_TYPE_BIN;
|
||||
}
|
||||
else if (*p == '0')
|
||||
{
|
||||
type = NUM_TYPE_OCT;
|
||||
}
|
||||
|
||||
while (*p++)
|
||||
{
|
||||
if (*p == '.' && *(p + 1) != 0)
|
||||
{
|
||||
type = NUM_TYPE_FLOAT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 字符转数字
|
||||
*
|
||||
* @param code 字符
|
||||
* @return char 数字
|
||||
*/
|
||||
static char shellExtToNum(char code)
|
||||
{
|
||||
if ((code >= '0') && (code <= '9'))
|
||||
{
|
||||
return code -'0';
|
||||
}
|
||||
else if ((code >= 'a') && (code <= 'f'))
|
||||
{
|
||||
return code - 'a' + 10;
|
||||
}
|
||||
else if ((code >= 'A') && (code <= 'F'))
|
||||
{
|
||||
return code - 'A' + 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 解析字符参数
|
||||
*
|
||||
* @param string 字符串参数
|
||||
* @return char 解析出的字符
|
||||
*/
|
||||
static char shellExtParseChar(char *string)
|
||||
{
|
||||
char *p = (*string == '\'') ? (string + 1) : string;
|
||||
char value = 0;
|
||||
|
||||
if (*p == '\\')
|
||||
{
|
||||
switch (*(p + 1))
|
||||
{
|
||||
case 'b':
|
||||
value = '\b';
|
||||
break;
|
||||
case 'r':
|
||||
value = '\r';
|
||||
break;
|
||||
case 'n':
|
||||
value = '\n';
|
||||
break;
|
||||
case 't':
|
||||
value = '\t';
|
||||
break;
|
||||
case '0':
|
||||
value = 0;
|
||||
break;
|
||||
default:
|
||||
value = *(p + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
value = *p;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 解析字符串参数
|
||||
*
|
||||
* @param string 字符串参数
|
||||
* @return char* 解析出的字符串
|
||||
*/
|
||||
static char* shellExtParseString(char *string)
|
||||
{
|
||||
char *p = string;
|
||||
unsigned short index = 0;
|
||||
|
||||
if (*string == '\"')
|
||||
{
|
||||
p = ++string;
|
||||
}
|
||||
|
||||
while (*p)
|
||||
{
|
||||
if (*p == '\\')
|
||||
{
|
||||
*(string + index) = shellExtParseChar(p - 1);
|
||||
p++;
|
||||
}
|
||||
else if (*p == '\"')
|
||||
{
|
||||
*(string + index) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*(string + index) = *p;
|
||||
}
|
||||
p++;
|
||||
index ++;
|
||||
}
|
||||
*(string + index) = 0;
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 解析数字参数
|
||||
*
|
||||
* @param string 字符串参数
|
||||
* @return unsigned int 解析出的数字
|
||||
*/
|
||||
static unsigned int shellExtParseNumber(char *string)
|
||||
{
|
||||
ShellNumType type = NUM_TYPE_DEC;
|
||||
char radix = 10;
|
||||
char *p = string;
|
||||
char offset = 0;
|
||||
signed char sign = 1;
|
||||
unsigned int valueInt = 0;
|
||||
float valueFloat = 0.0;
|
||||
unsigned int devide = 0;
|
||||
|
||||
if (*string == '-')
|
||||
{
|
||||
sign = -1;
|
||||
}
|
||||
|
||||
type = shellExtNumType(string + ((sign == -1) ? 1 : 0));
|
||||
|
||||
switch ((char)type)
|
||||
{
|
||||
case NUM_TYPE_HEX:
|
||||
radix = 16;
|
||||
offset = 2;
|
||||
break;
|
||||
|
||||
case NUM_TYPE_OCT:
|
||||
radix = 8;
|
||||
offset = 1;
|
||||
break;
|
||||
|
||||
case NUM_TYPE_BIN:
|
||||
radix = 2;
|
||||
offset = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
p = string + offset + ((sign == -1) ? 1 : 0);
|
||||
|
||||
while (*p)
|
||||
{
|
||||
if (*p == '.')
|
||||
{
|
||||
devide = 1;
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
valueInt = valueInt * radix + shellExtToNum(*p);
|
||||
devide *= 10;
|
||||
p++;
|
||||
}
|
||||
if (type == NUM_TYPE_FLOAT && devide != 0)
|
||||
{
|
||||
valueFloat = (float)valueInt / devide * sign;
|
||||
return *(unsigned int *)(&valueFloat);
|
||||
}
|
||||
else
|
||||
{
|
||||
return valueInt * sign;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 解析变量参数
|
||||
*
|
||||
* @param shell shell对象
|
||||
* @param var 变量
|
||||
* @return unsigned int 变量值
|
||||
*/
|
||||
static unsigned int shellExtParseVar(Shell *shell, char *var)
|
||||
{
|
||||
ShellCommand *command = shellSeekCommand(shell,
|
||||
var + 1,
|
||||
shell->commandList.base,
|
||||
0);
|
||||
if (command)
|
||||
{
|
||||
return shellGetVarValue(shell, command);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 解析参数
|
||||
*
|
||||
* @param shell shell对象
|
||||
* @param string 参数
|
||||
* @param type 参数类型
|
||||
* @param result 解析结果
|
||||
*
|
||||
* @return int 0 解析成功 --1 解析失败
|
||||
*/
|
||||
int shellExtParsePara(Shell *shell, char *string, char *type, unsigned int *result)
|
||||
{
|
||||
if (type == NULL || (*string == '$' && *(string + 1)))
|
||||
{
|
||||
if (*string == '\'' && *(string + 1))
|
||||
{
|
||||
*result = (unsigned int)shellExtParseChar(string);
|
||||
return 0;
|
||||
}
|
||||
else if (*string == '-' || (*string >= '0' && *string <= '9'))
|
||||
{
|
||||
*result = (unsigned int)shellExtParseNumber(string);
|
||||
return 0;
|
||||
}
|
||||
else if (*string == '$' && *(string + 1))
|
||||
{
|
||||
*result = shellExtParseVar(shell, string);
|
||||
return 0;
|
||||
}
|
||||
else if (*string)
|
||||
{
|
||||
*result = (unsigned int)shellExtParseString(string);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#if SHELL_USING_FUNC_SIGNATURE == 1
|
||||
else
|
||||
{
|
||||
if (strcmp("c", type) == 0)
|
||||
{
|
||||
*result = (unsigned int)shellExtParseChar(string);
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp("i", type) == 0
|
||||
|| strcmp("f", type) == 0
|
||||
|| strcmp("p", type) == 0)
|
||||
{
|
||||
*result = (unsigned int)shellExtParseNumber(string);
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp("s", type) == 0)
|
||||
{
|
||||
*result = (unsigned int)shellExtParseString(string);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ShellCommand *command = shellSeekCommand(shell,
|
||||
type,
|
||||
shell->commandList.base,
|
||||
0);
|
||||
if (command != NULL)
|
||||
{
|
||||
void *param;
|
||||
if (command->data.paramParser.parser(shellExtParseString(string), ¶m) == 0)
|
||||
{
|
||||
*result = (unsigned int)param;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
shellWriteString(shell, "Parse param for type: ");
|
||||
shellWriteString(shell, type);
|
||||
shellWriteString(shell, " failed\r\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
shellWriteString(shell, "Can't find the param parser for type: ");
|
||||
shellWriteString(shell, type);
|
||||
shellWriteString(shell, "\r\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#if SHELL_USING_FUNC_SIGNATURE == 1
|
||||
/**
|
||||
* @brief 清理参数
|
||||
*
|
||||
* @param shell shell
|
||||
* @param type 参数类型
|
||||
* @param param 参数
|
||||
*
|
||||
* @return int 0 清理成功 -1 清理失败
|
||||
*/
|
||||
int shellExtCleanerPara(Shell *shell, char *type, unsigned int param)
|
||||
{
|
||||
if (type == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp("c", type) == 0
|
||||
|| strcmp("i", type) == 0
|
||||
|| strcmp("f", type) == 0
|
||||
|| strcmp("p", type) == 0
|
||||
|| strcmp("s", type) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ShellCommand *command = shellSeekCommand(shell,
|
||||
type,
|
||||
shell->commandList.base,
|
||||
0);
|
||||
if (command != NULL && command->data.paramParser.cleaner != NULL)
|
||||
{
|
||||
return command->data.paramParser.cleaner((void *)param);
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
|
||||
|
||||
|
||||
/**
|
||||
* @brief 执行命令
|
||||
*
|
||||
* @param shell shell对象
|
||||
* @param command 命令
|
||||
* @param argc 参数个数
|
||||
* @param argv 参数
|
||||
* @return int 返回值
|
||||
*/
|
||||
int shellExtRun(Shell *shell, ShellCommand *command, int argc, char *argv[])
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int params[SHELL_PARAMETER_MAX_NUMBER] = {0};
|
||||
int paramNum = command->attr.attrs.paramNum > (argc - 1) ?
|
||||
command->attr.attrs.paramNum : (argc - 1);
|
||||
#if SHELL_USING_FUNC_SIGNATURE == 1
|
||||
char type[16];
|
||||
int index = 0;
|
||||
|
||||
if (command->data.cmd.signature != NULL)
|
||||
{
|
||||
int except = shellGetParamNumExcept(command->data.cmd.signature);
|
||||
if (except != argc - 1)
|
||||
{
|
||||
shellWriteString(shell, "Parameters number incorrect\r\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; i < argc - 1; i++)
|
||||
{
|
||||
#if SHELL_USING_FUNC_SIGNATURE == 1
|
||||
if (command->data.cmd.signature != NULL) {
|
||||
index = shellGetNextParamType(command->data.cmd.signature, index, type);
|
||||
if (shellExtParsePara(shell, argv[i + 1], type, ¶ms[i]) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
|
||||
{
|
||||
if (shellExtParsePara(shell, argv[i + 1], NULL, ¶ms[i]) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (paramNum)
|
||||
{
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 1
|
||||
case 0:
|
||||
ret = command->data.cmd.function();
|
||||
break;
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 1 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 2
|
||||
case 1:
|
||||
{
|
||||
int (*func)(int) = command->data.cmd.function;
|
||||
ret = func(params[0]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 2 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 3
|
||||
case 2:
|
||||
{
|
||||
int (*func)(int, int) = command->data.cmd.function;
|
||||
ret = func(params[0], params[1]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 3 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 4
|
||||
case 3:
|
||||
{
|
||||
int (*func)(int, int, int) = command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 4 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 5
|
||||
case 4:
|
||||
{
|
||||
int (*func)(int, int, int, int) = command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2], params[3]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 5 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 6
|
||||
case 5:
|
||||
{
|
||||
int (*func)(int, int, int, int, int) = command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2], params[3], params[4]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 6 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 7
|
||||
case 6:
|
||||
{
|
||||
int (*func)(int, int, int, int, int, int) = command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2], params[3], params[4], params[5]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 7 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 8
|
||||
case 7:
|
||||
{
|
||||
int (*func)(int, int, int, int, int, int, int) = command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 8 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 9
|
||||
case 8:
|
||||
{
|
||||
int (*func)(int, int, int, int, int, int, int, int) = command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 9 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 10
|
||||
case 9:
|
||||
{
|
||||
int (*func)(int, int, int, int, int, int, int, int, int) = command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
|
||||
params[8]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 10 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 11
|
||||
case 10:
|
||||
{
|
||||
int (*func)(int, int, int, int, int, int, int, int, int, int) = command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
|
||||
params[8], params[9]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 11 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 12
|
||||
case 11:
|
||||
{
|
||||
int (*func)(int, int, int, int, int, int, int, int, int, int, int) = command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
|
||||
params[8], params[9], params[10]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 12 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 13
|
||||
case 12:
|
||||
{
|
||||
int (*func)(int, int, int, int, int, int, int, int, int, int, int, int) = command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
|
||||
params[8], params[9], params[10], params[11]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 13 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 14
|
||||
case 13:
|
||||
{
|
||||
int (*func)(int, int, int, int, int, int, int, int, int, int, int, int, int) = command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
|
||||
params[8], params[9], params[10], params[11], params[12]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 14 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 15
|
||||
case 14:
|
||||
{
|
||||
int (*func)(int, int, int, int, int, int, int, int, int, int, int, int, int, int)
|
||||
= command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
|
||||
params[8], params[9], params[10], params[11], params[12], params[13]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 15 */
|
||||
#if SHELL_PARAMETER_MAX_NUMBER >= 16
|
||||
case 15:
|
||||
{
|
||||
int (*func)(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int)
|
||||
= command->data.cmd.function;
|
||||
ret = func(params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
|
||||
params[8], params[9], params[10], params[11], params[12], params[13], params[14]);
|
||||
break;
|
||||
}
|
||||
#endif /** SHELL_PARAMETER_MAX_NUMBER >= 16 */
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
#if SHELL_USING_FUNC_SIGNATURE == 1
|
||||
if (command->data.cmd.signature != NULL) {
|
||||
index = 0;
|
||||
for (int i = 0; i < argc - 1; i++)
|
||||
{
|
||||
index = shellGetNextParamType(command->data.cmd.signature, index, type);
|
||||
shellExtCleanerPara(shell, type, params[i]);
|
||||
}
|
||||
}
|
||||
#endif /** SHELL_USING_FUNC_SIGNATURE == 1 */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
Loading…
Reference in New Issue