Browse Source

implemented various things for cli

added 'reset to defaults' to cli
got rid of 'servo' feature since that's not really a feature a user can set
added couple more configurable tricopter things to config struct.

git-svn-id: https://afrodevices.googlecode.com/svn/trunk/baseflight@118 7c89a4a9-59b9-e629-4cfe-3a2d53b20e61
master
timecop 13 years ago
parent
commit
2fc24b338e
  1. 84
      baseflight.uvopt
  2. 5215
      obj/baseflight.hex
  3. 4
      src/board.h
  4. 244
      src/cli.c
  5. 15
      src/config.c
  6. 6
      src/main.c
  7. 13
      src/mixer.c
  8. 5
      src/mw.h

84
baseflight.uvopt

@ -161,9 +161,24 @@
<Bp>
<Number>0</Number>
<Type>0</Type>
<LineNumber>309</LineNumber>
<EnabledFlag>1</EnabledFlag>
<Address>134218890</Address>
<ByteObject>0</ByteObject>
<ManyObjects>0</ManyObjects>
<SizeOfObject>0</SizeOfObject>
<BreakByAccess>0</BreakByAccess>
<BreakIfRCount>1</BreakIfRCount>
<Filename></Filename>
<ExecCommand></ExecCommand>
<Expression>\\baseflight\src/cli.c\309</Expression>
</Bp>
<Bp>
<Number>1</Number>
<Type>0</Type>
<LineNumber>58</LineNumber>
<EnabledFlag>1</EnabledFlag>
<Address>134240618</Address>
<Address>134242932</Address>
<ByteObject>0</ByteObject>
<ManyObjects>0</ManyObjects>
<SizeOfObject>0</SizeOfObject>
@ -173,6 +188,21 @@
<ExecCommand></ExecCommand>
<Expression>\\baseflight\src/drv_mpu6050.c\58</Expression>
</Bp>
<Bp>
<Number>2</Number>
<Type>0</Type>
<LineNumber>316</LineNumber>
<EnabledFlag>1</EnabledFlag>
<Address>134218956</Address>
<ByteObject>0</ByteObject>
<ManyObjects>0</ManyObjects>
<SizeOfObject>0</SizeOfObject>
<BreakByAccess>0</BreakByAccess>
<BreakIfRCount>1</BreakIfRCount>
<Filename></Filename>
<ExecCommand></ExecCommand>
<Expression>\\baseflight\src/cli.c\316</Expression>
</Bp>
</Breakpoint>
<WatchWindow1>
<Ww>
@ -202,7 +232,7 @@
<Mm>
<WinNumber>1</WinNumber>
<SubType>0</SubType>
<ItemText>buf</ItemText>
<ItemText>eqptr</ItemText>
</Mm>
</MemoryWindow1>
<MemoryWindow2>
@ -507,8 +537,8 @@
<Focus>0</Focus>
<ColumnNumber>0</ColumnNumber>
<tvExpOptDlg>0</tvExpOptDlg>
<TopLine>120</TopLine>
<CurrentLine>131</CurrentLine>
<TopLine>296</TopLine>
<CurrentLine>314</CurrentLine>
<bDave2>0</bDave2>
<PathWithFileName>.\src\cli.c</PathWithFileName>
<FilenameWithoutPath>cli.c</FilenameWithoutPath>
@ -519,10 +549,10 @@
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<ColumnNumber>14</ColumnNumber>
<ColumnNumber>0</ColumnNumber>
<tvExpOptDlg>0</tvExpOptDlg>
<TopLine>91</TopLine>
<CurrentLine>123</CurrentLine>
<CurrentLine>120</CurrentLine>
<bDave2>0</bDave2>
<PathWithFileName>.\src\config.c</PathWithFileName>
<FilenameWithoutPath>config.c</FilenameWithoutPath>
@ -547,10 +577,10 @@
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<ColumnNumber>13</ColumnNumber>
<ColumnNumber>0</ColumnNumber>
<tvExpOptDlg>0</tvExpOptDlg>
<TopLine>0</TopLine>
<CurrentLine>0</CurrentLine>
<TopLine>1</TopLine>
<CurrentLine>28</CurrentLine>
<bDave2>0</bDave2>
<PathWithFileName>.\src\main.c</PathWithFileName>
<FilenameWithoutPath>main.c</FilenameWithoutPath>
@ -561,10 +591,10 @@
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<ColumnNumber>0</ColumnNumber>
<ColumnNumber>19</ColumnNumber>
<tvExpOptDlg>0</tvExpOptDlg>
<TopLine>0</TopLine>
<CurrentLine>0</CurrentLine>
<TopLine>61</TopLine>
<CurrentLine>79</CurrentLine>
<bDave2>0</bDave2>
<PathWithFileName>.\src\mixer.c</PathWithFileName>
<FilenameWithoutPath>mixer.c</FilenameWithoutPath>
@ -605,8 +635,8 @@
<Focus>0</Focus>
<ColumnNumber>0</ColumnNumber>
<tvExpOptDlg>0</tvExpOptDlg>
<TopLine>1</TopLine>
<CurrentLine>4</CurrentLine>
<TopLine>43</TopLine>
<CurrentLine>43</CurrentLine>
<bDave2>0</bDave2>
<PathWithFileName>.\src\serial.c</PathWithFileName>
<FilenameWithoutPath>serial.c</FilenameWithoutPath>
@ -617,10 +647,10 @@
<FileType>5</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<ColumnNumber>20</ColumnNumber>
<ColumnNumber>0</ColumnNumber>
<tvExpOptDlg>0</tvExpOptDlg>
<TopLine>0</TopLine>
<CurrentLine>0</CurrentLine>
<TopLine>1</TopLine>
<CurrentLine>9</CurrentLine>
<bDave2>0</bDave2>
<PathWithFileName>.\src\board.h</PathWithFileName>
<FilenameWithoutPath>board.h</FilenameWithoutPath>
@ -633,8 +663,8 @@
<Focus>0</Focus>
<ColumnNumber>0</ColumnNumber>
<tvExpOptDlg>0</tvExpOptDlg>
<TopLine>70</TopLine>
<CurrentLine>101</CurrentLine>
<TopLine>159</TopLine>
<CurrentLine>180</CurrentLine>
<bDave2>0</bDave2>
<PathWithFileName>.\src\mw.h</PathWithFileName>
<FilenameWithoutPath>mw.h</FilenameWithoutPath>
@ -708,10 +738,10 @@
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<ColumnNumber>0</ColumnNumber>
<ColumnNumber>19</ColumnNumber>
<tvExpOptDlg>0</tvExpOptDlg>
<TopLine>0</TopLine>
<CurrentLine>0</CurrentLine>
<TopLine>134</TopLine>
<CurrentLine>154</CurrentLine>
<bDave2>0</bDave2>
<PathWithFileName>.\src\drv_i2c.c</PathWithFileName>
<FilenameWithoutPath>drv_i2c.c</FilenameWithoutPath>
@ -750,10 +780,10 @@
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<ColumnNumber>51</ColumnNumber>
<ColumnNumber>22</ColumnNumber>
<tvExpOptDlg>0</tvExpOptDlg>
<TopLine>0</TopLine>
<CurrentLine>0</CurrentLine>
<TopLine>85</TopLine>
<CurrentLine>111</CurrentLine>
<bDave2>0</bDave2>
<PathWithFileName>.\src\drv_system.c</PathWithFileName>
<FilenameWithoutPath>drv_system.c</FilenameWithoutPath>
@ -997,8 +1027,8 @@
<Focus>0</Focus>
<ColumnNumber>0</ColumnNumber>
<tvExpOptDlg>0</tvExpOptDlg>
<TopLine>0</TopLine>
<CurrentLine>0</CurrentLine>
<TopLine>133</TopLine>
<CurrentLine>133</CurrentLine>
<bDave2>0</bDave2>
<PathWithFileName>.\src\baseflight_startups\startup_stm32f10x_md.s</PathWithFileName>
<FilenameWithoutPath>startup_stm32f10x_md.s</FilenameWithoutPath>

5215
obj/baseflight.hex
File diff suppressed because it is too large
View File

4
src/board.h

@ -5,6 +5,7 @@
#include <stdint.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include "stm32f10x_conf.h"
#include "core_cm3.h"
@ -20,14 +21,13 @@ typedef enum {
typedef enum {
FEATURE_PPM = 1 << 0,
FEATURE_VBAT = 1 << 1,
FEATURE_SERVO = 1 << 2,
FEATURE_INFLIGHT_ACC_CAL = 1 << 2,
FEATURE_DIGITAL_SERVO = 1 << 3,
FEATURE_MOTOR_STOP = 1 << 4,
FEATURE_SERVO_TILT = 1 << 5,
FEATURE_CAMTRIG = 1 << 6,
FEATURE_GYRO_SMOOTHING = 1 << 7,
FEATURE_LED_RING = 1 << 8,
FEATURE_INFLIGHT_ACC_CAL = 1 << 9,
} AvailableFeatures;
typedef void (* sensorInitFuncPtr)(void);

244
src/cli.c

@ -3,23 +3,32 @@
// we unset this on 'exit'
extern uint8_t cliMode;
static void cliDefaults(char *cmdline);
static void cliExit(char *cmdline);
static void cliFeature(char *cmdline);
static void cliHelp(char *cmdline);
static void cliMixer(char *cmdline);
static void cliRMode(char *cmdline);
static void cliSave(char *cmdline);
static void cliSet(char *cmdline);
static void cliVersion(char *cmdline);
// buffer
static char cliBuffer[32];
static uint8_t bufferIndex = 0;
static bool unsaved = false;
// sync this with MultiType enum from mw.h
const char *mixerNames[] = {
"TRI", "QUADP", "QUADX", "BI",
"GIMBAL", "Y6", "HEX6",
"GIMBAL", "Y6", "HEX6",
"FLYING_WING", "Y4", "HEX6X", "OCTOX8", "OCTOFLATP", "OCTOFLATX",
"AIRPLANE", "HELI_120_CCPM", "HELI_90_DEG", "VTAIL4"
"AIRPLANE", "HELI_120_CCPM", "HELI_90_DEG", "VTAIL4", NULL
};
// sync this with AvailableFeatures enum from board.h
const char *featureNames[] = {
"PPM", "VBAT", "INFLIGHT_ACC_CAL", "DIGITAL_SERVO", "MOTOR_STOP",
"SERVO_TILT", "CAMTRIG", "GYRO_SMOOTHING", "LED_RING",
NULL
};
typedef struct {
@ -30,17 +39,18 @@ typedef struct {
// should be sorted a..z for bsearch()
const clicmd_t cmdTable[] = {
{ "defaults", "reset to defaults and reboot", cliDefaults },
{ "exit", "", cliExit },
{ "feature", "list or -val or val", cliFeature },
{ "help", "", cliHelp },
{ "mixer", "mixer name", cliMixer },
{ "rmode", "pwm / ppm", cliRMode },
{ "set", "name=value", cliSet },
{ "mixer", "mixer name or list", cliMixer },
{ "save", "save and reboot", cliSave },
{ "set", "name=value or blank for list", cliSet },
{ "version", "", cliVersion },
};
#define CMD_COUNT (sizeof(cmdTable) / sizeof(cmdTable[0]))
typedef enum {
VAR_BOOL, // yes/no true/false 1/0 type stuff
VAR_UINT8,
VAR_INT8,
VAR_UINT16,
@ -65,12 +75,17 @@ const clivalue_t valueTable[] = {
{ "wing_left_mid", VAR_UINT16, &cfg.wing_left_mid, 0, 2000 },
{ "wing_right_mid", VAR_UINT16, &cfg.wing_right_mid, 0, 2000 },
{ "tri_yaw_middle", VAR_UINT16, &cfg.tri_yaw_middle, 0, 2000 },
{ "tri_yaw_min", VAR_UINT16, &cfg.tri_yaw_min, 0, 2000 },
{ "tri_yaw_max", VAR_UINT16, &cfg.tri_yaw_max, 0, 2000 },
{ "tilt_pitch_prop", VAR_INT8, &cfg.tilt_pitch_prop, -100, 100 },
{ "tilt_roll_prop", VAR_INT8, &cfg.tilt_roll_prop, -100, 100 },
};
#define VALUE_COUNT (sizeof(valueTable) / sizeof(valueTable[0]))
static void cliSetVar(const clivalue_t *var, const int32_t value);
static void cliPrintVar(const clivalue_t *var);
static void cliPrompt(void)
{
uartPrint("\r\n# ");
@ -82,12 +97,82 @@ static int cliCompare(const void *a, const void *b)
return strncasecmp(ca->name, cb->name, strlen(cb->name));
}
static void cliDefaults(char *cmdline)
{
uartPrint("Resetting to defaults...\r\n");
checkFirstTime(true);
uartPrint("Rebooting...");
delay(10);
systemReset(false);
}
static void cliExit(char *cmdline)
{
uartPrint("\r\nLeaving CLI mode...\r\n");
memset(cliBuffer, 0, sizeof(cliBuffer));
bufferIndex = 0;
cliMode = 0;
// save and reboot... I think this makes the most sense
cliSave(cmdline);
}
static void cliFeature(char *cmdline)
{
uint8_t i;
uint8_t len;
uint32_t mask;
len = strlen(cmdline);
mask = featureMask();
if (len == 0) {
uartPrint("Enabled features: ");
for (i = 0; ; i++) {
if (featureNames[i] == NULL)
break;
if (mask & (1 << i))
uartPrint((char *)featureNames[i]);
uartWrite(' ');
}
uartPrint("\r\n");
} else if (strncasecmp(cmdline, "list", len) == 0) {
uartPrint("Available features: ");
for (i = 0; ; i++) {
if (featureNames[i] == NULL)
break;
uartPrint((char *)featureNames[i]);
uartWrite(' ');
}
uartPrint("\r\n");
return;
} else {
bool remove = false;
if (cmdline[0] == '-') {
// remove feature
remove = true;
cmdline++; // skip over -
len--;
}
for (i = 0; ; i++) {
if (featureNames[i] == NULL) {
uartPrint("Invalid feature name...\r\n");
break;
}
if (strncasecmp(cmdline, featureNames[i], len) == 0) {
if (remove) {
featureClear(1 << i);
uartPrint("Disabled ");
} else {
featureSet(1 << i);
uartPrint("Enabled ");
}
uartPrint((char *)featureNames[i]);
uartPrint("\r\n");
break;
}
}
}
}
static void cliHelp(char *cmdline)
@ -106,29 +191,141 @@ static void cliHelp(char *cmdline)
static void cliMixer(char *cmdline)
{
uint8_t i;
uint8_t len;
len = strlen(cmdline);
if (len == 0) {
uartPrint("Current mixer: ");
uartPrint((char *)mixerNames[cfg.mixerConfiguration - 1]);
uartPrint("\r\n");
return;
} else if (strncasecmp(cmdline, "list", len) == 0) {
uartPrint("Available mixers: ");
for (i = 0; ; i++) {
if (mixerNames[i] == NULL)
break;
uartPrint((char *)mixerNames[i]);
uartWrite(' ');
}
uartPrint("\r\n");
return;
}
for (i = 0; ; i++) {
if (mixerNames[i] == NULL) {
uartPrint("Invalid mixer type...\r\n");
break;
}
if (strncasecmp(cmdline, mixerNames[i], len) == 0) {
cfg.mixerConfiguration = i + 1;
uartPrint("Mixer set to ");
uartPrint((char *)mixerNames[i]);
uartPrint("\r\n");
break;
}
}
}
static void cliSave(char *cmdline)
{
uartPrint("Saving...");
writeParams();
uartPrint("\r\nRebooting...");
delay(10);
systemReset(false);
}
static void cliRMode(char *cmdline)
static void cliPrintVar(const clivalue_t *var)
{
if (strncasecmp(cmdline, "pwm", 3) == 0) {
uartPrint("PWM Mode");
featureClear(FEATURE_PPM);
} else {
uartPrint("PPM Mode");
featureSet(FEATURE_PPM);
int32_t value;
char buf[16];
switch (var->type) {
case VAR_UINT8:
value = *(uint8_t *)var->ptr;
break;
case VAR_INT8:
value = *(int8_t *)var->ptr;
break;
case VAR_UINT16:
value = *(uint16_t *)var->ptr;
break;
case VAR_INT16:
value = *(int16_t *)var->ptr;
break;
}
snprintf(buf, 16, "%d", value);
uartPrint(buf);
}
cliExit(cmdline);
writeParams();
systemReset(false);
static void cliSetVar(const clivalue_t *var, const int32_t value)
{
switch (var->type) {
case VAR_UINT8:
*(uint8_t *)var->ptr = (uint8_t)value;
break;
case VAR_INT8:
*(int8_t *)var->ptr = (int8_t)value;
break;
case VAR_UINT16:
*(uint16_t *)var->ptr = (uint16_t)value;
break;
case VAR_INT16:
*(int16_t *)var->ptr = (int16_t)value;
break;
}
}
static void cliSet(char *cmdline)
{
uint8_t i;
uint8_t len;
const clivalue_t *val;
char *eqptr = NULL;
int32_t value = 0;
len = strlen(cmdline);
if (len == 0) {
uartPrint("Current settings: \r\n");
for (i = 0; i < VALUE_COUNT; i++) {
val = &valueTable[i];
uartPrint((char *)valueTable[i].name);
uartPrint(" = ");
cliPrintVar(val);
uartPrint("\r\n");
delay(10);
}
} else if (eqptr = strstr(cmdline, "=")) {
// has equal, set var
eqptr++;
len--;
value = atoi(eqptr);
for (i = 0; i < VALUE_COUNT; i++) {
val = &valueTable[i];
if (strncasecmp(cmdline, valueTable[i].name, strlen(valueTable[i].name)) == 0) {
// found
if (value >= valueTable[i].min && value <= valueTable[i].max) {
cliSetVar(val, value);
uartPrint((char *)valueTable[i].name);
uartPrint(" set to ");
cliPrintVar(val);
} else {
uartPrint("ERR: Value assignment out of range\r\n");
}
return;
}
}
uartPrint("ERR: Unknown variable name\r\n");
}
}
static void cliVersion(char *cmdline)
@ -150,7 +347,7 @@ void cliProcess(void)
if (c == '\t' || c == '?') {
const clicmd_t *cmd, *pstart = NULL, *pend = NULL;
int i = bufferIndex;
for (cmd = cmdTable;cmd < cmdTable + CMD_COUNT;cmd++) {
for (cmd = cmdTable; cmd < cmdTable + CMD_COUNT; cmd++) {
if (bufferIndex && (strncasecmp(cliBuffer, cmd->name, bufferIndex) != 0))
continue;
if (!pstart)
@ -158,7 +355,7 @@ void cliProcess(void)
pend = cmd;
}
if (pstart) { /* Buffer matches one or more commands */
for (;;bufferIndex++) {
for (; ; bufferIndex++) {
if (pstart->name[bufferIndex] != pend->name[bufferIndex])
break;
if (!pstart->name[bufferIndex]) {
@ -179,12 +376,13 @@ void cliProcess(void)
cliPrompt();
i = 0; /* Redraw prompt */
}
for (;i < bufferIndex;i++)
for (; i < bufferIndex; i++)
uartWrite(cliBuffer[i]);
} else if (!bufferIndex && c == 4) {
cliExit(cliBuffer);
return;
} else if (c == 12) {
// clear screen
uartPrint("\033[2J\033[1;1H");
cliPrompt();
} else if (bufferIndex && (c == '\n' || c == '\r')) {
@ -201,7 +399,7 @@ void cliProcess(void)
if (cmd)
cmd->func(cliBuffer + strlen(cmd->name) + 1);
else
uartPrint("ERR: Unknown command, try 'HELP'");
uartPrint("ERR: Unknown command, try 'help'");
memset(cliBuffer, 0, sizeof(cliBuffer));
bufferIndex = 0;

15
src/config.c

@ -8,7 +8,7 @@
config_t cfg;
static uint32_t enabledSensors = 0;
static uint8_t checkNewConf = 3;
static uint8_t checkNewConf = 4;
void readEEPROM(void)
{
@ -52,13 +52,13 @@ void writeParams(void)
blinkLED(15, 20, 1);
}
void checkFirstTime(void)
void checkFirstTime(bool reset)
{
uint8_t test_val, i;
test_val = *(uint8_t *)FLASH_WRITE_ADDR;
if (test_val == checkNewConf)
if (!reset && test_val == checkNewConf)
return;
// Default settings
@ -115,7 +115,9 @@ void checkFirstTime(void)
cfg.wing_left_mid = 1500;
cfg.wing_right_mid = 1500;
cfg.tri_yaw_middle = 1500;
cfg.tri_yaw_min = 1020;
cfg.tri_yaw_max = 2000;
// gimbal
cfg.tilt_pitch_prop = 10;
cfg.tilt_roll_prop = 10;
@ -157,3 +159,8 @@ void featureClearAll()
{
cfg.enabledFeatures = 0;
}
uint32_t featureMask(void)
{
return cfg.enabledFeatures;
}

6
src/main.c

@ -1,6 +1,8 @@
#include "board.h"
#include "mw.h"
extern uint8_t useServo;
int main(void)
{
uint8_t i;
@ -17,13 +19,13 @@ int main(void)
systemInit();
readEEPROM();
checkFirstTime();
checkFirstTime(false);
// We have these sensors
sensorsSet(SENSOR_ACC | SENSOR_BARO | SENSOR_MAG);
mixerInit(); // this will configure FEATURE_SERVO depending on mixer type
pwmInit(feature(FEATURE_PPM), feature(FEATURE_SERVO), feature(FEATURE_DIGITAL_SERVO));
pwmInit(feature(FEATURE_PPM), useServo, feature(FEATURE_DIGITAL_SERVO));
LED1_ON;
LED0_OFF;

13
src/mixer.c

@ -2,6 +2,7 @@
#include "mw.h"
static uint8_t numberMotor = 4;
uint8_t useServo = 0;
int16_t motor[8];
int16_t servo[8] = { 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500 };
@ -9,10 +10,10 @@ void mixerInit(void)
{
// enable servos for mixes that require them. note, this shifts motor counts.
if (cfg.mixerConfiguration == MULTITYPE_BI || cfg.mixerConfiguration == MULTITYPE_TRI || cfg.mixerConfiguration == MULTITYPE_GIMBAL || cfg.mixerConfiguration == MULTITYPE_FLYING_WING)
featureSet(FEATURE_SERVO);
useServo = 1;
// if we want camstab/trig, that also enabled servos. this is kinda lame. maybe rework feature bits later.
if (feature(FEATURE_SERVO_TILT) || feature(FEATURE_CAMTRIG))
featureSet(FEATURE_SERVO);
useServo = 1;
switch (cfg.mixerConfiguration) {
case MULTITYPE_GIMBAL:
@ -51,7 +52,7 @@ void mixerInit(void)
void writeServos(void)
{
if (!feature(FEATURE_SERVO))
if (!useServo)
return;
if (cfg.mixerConfiguration == MULTITYPE_TRI || cfg.mixerConfiguration == MULTITYPE_BI) {
@ -66,13 +67,15 @@ void writeServos(void)
}
}
extern uint8_t cliMode;
void writeMotors(void)
{
uint8_t i;
uint8_t offset = 0;
// when servos are enabled, motor outputs 1..2 are for servos only
if (feature(FEATURE_SERVO))
if (useServo)
offset = 2;
for (i = 0; i < numberMotor; i++)

5
src/mw.h

@ -175,6 +175,8 @@ typedef struct config_t {
uint16_t wing_left_mid; // left servo center pos. - use this for initial trim
uint16_t wing_right_mid; // right servo center pos. - use this for initial trim
uint16_t tri_yaw_middle; // tail servo center pos. - use this for initial trim
uint16_t tri_yaw_min; // tail servo min
uint16_t tri_yaw_max; // tail servo max
// gimbal-related configuration
int8_t tilt_pitch_prop; // servo proportional (tied to angle) ; can be negative to invert movement
@ -270,7 +272,7 @@ void serialCom(void);
// Config
void readEEPROM(void);
void writeParams(void);
void checkFirstTime(void);
void checkFirstTime(bool reset);
bool sensors(uint32_t mask);
void sensorsSet(uint32_t mask);
void sensorsClear(uint32_t mask);
@ -278,6 +280,7 @@ bool feature(uint32_t mask);
void featureSet(uint32_t mask);
void featureClear(uint32_t mask);
void featureClearAll(void);
uint32_t featureMask(void);
// cli
void cliProcess(void);
Loading…
Cancel
Save