chore(cpn): Allow configuring AUX serial port as "External module" (#2854)

* Add external module to hardware settings

* Use serial port aux1 rather than radio profile externalaccessmod
Move module conversion function from OpenTxFirmare to ModuleData

* Remove externalaccessmod from radio profile options

* Unnecessary CMake option HARDWARE_EXTERNAL_ACCESS_MOD removed.

---------

Co-authored-by: rotorman <risto.koiva@web.de>
This commit is contained in:
Neil Horne
2023-03-05 19:12:32 +10:00
committed by GitHub
co-authored by rotorman
parent b4e13474df
commit c84abbf739
11 changed files with 186 additions and 157 deletions
@@ -84,6 +84,7 @@ const YamlLookupTable uartModeLut = {
{ GeneralSettings::AUX_SERIAL_GPS, "GPS" },
{ GeneralSettings::AUX_SERIAL_DEBUG, "DEBUG" },
{ GeneralSettings::AUX_SERIAL_SPACEMOUSE, "SPACEMOUSE" },
{ GeneralSettings::AUX_SERIAL_EXT_MODULE, "EXT_MODULE" },
};
const YamlLookupTable antennaModeLut = {
+18 -10
View File
@@ -511,6 +511,8 @@ QString GeneralSettings::serialModeToString(int value)
return tr("Debug");
case AUX_SERIAL_SPACEMOUSE:
return tr("SpaceMouse");
case AUX_SERIAL_EXT_MODULE:
return tr("External module");
default:
return CPN_STR_UNKNOWN_ITEM;
}
@@ -577,21 +579,27 @@ AbstractStaticItemModel * GeneralSettings::bluetoothModeItemModel()
}
// static
AbstractStaticItemModel * GeneralSettings::serialModeItemModel(int port_nr)
AbstractStaticItemModel * GeneralSettings::serialModeItemModel()
{
AbstractStaticItemModel * mdl = new AbstractStaticItemModel();
mdl->setName(QString(AIM_GS_SERIALMODE).arg(port_nr));
mdl->setName(AIM_GS_SERIALMODE);
for (int i = 0; i < AUX_SERIAL_COUNT; i++) {
if (port_nr == SP_VCP &&
(i == AUX_SERIAL_TELE_IN ||
i == AUX_SERIAL_SBUS_TRAINER ||
i == AUX_SERIAL_GPS ||
i == AUX_SERIAL_SPACEMOUSE)) {
// These are disabled on VCP
continue;
int contexts = AllAuxSerialModeContexts;
if (i == AUX_SERIAL_EXT_MODULE) {
contexts &= ~(AUX2Context | VCPContext);
}
mdl->appendToItemList(serialModeToString(i), i);
else if (i == AUX_SERIAL_TELE_IN ||
i == AUX_SERIAL_SBUS_TRAINER ||
i == AUX_SERIAL_GPS ||
i == AUX_SERIAL_SPACEMOUSE ||
i == AUX_SERIAL_EXT_MODULE) {
contexts &= ~VCPContext;
}
mdl->appendToItemList(serialModeToString(i), i, true, 0, contexts);
}
mdl->loadItemList();
+11 -2
View File
@@ -38,7 +38,7 @@ class AbstractStaticItemModel;
// identiying names of static abstract item models
constexpr char AIM_GS_ANTENNAMODE[] {"gs.antennamode"};
constexpr char AIM_GS_BLUETOOTHMODE[] {"gs.bluetoothmode"};
constexpr char AIM_GS_SERIALMODE[] {"gs.serialmode%1"};
constexpr char AIM_GS_SERIALMODE[] {"gs.serialmode"};
constexpr char AIM_GS_INTMODULEBAUDRATE[] {"gs.intmodulebaudrate"};
constexpr char AIM_GS_STICKDEADZONE[] {"gs.stickdeadzone"};
constexpr char AIM_GS_UARTSAMPLEMODE[] {"gs.uartsamplemode"};
@@ -142,9 +142,18 @@ class GeneralSettings {
AUX_SERIAL_GPS,
AUX_SERIAL_DEBUG,
AUX_SERIAL_SPACEMOUSE,
AUX_SERIAL_EXT_MODULE,
AUX_SERIAL_COUNT
};
enum AuxSerialModeContext
{
AUX1Context = 0x01,
AUX2Context = 0x02,
VCPContext = 0x04,
AllAuxSerialModeContexts = AUX1Context | AUX2Context | VCPContext,
};
enum TelemetryBaudrate {
};
@@ -295,7 +304,7 @@ class GeneralSettings {
static AbstractStaticItemModel * antennaModeItemModel(bool model_setup = false);
static AbstractStaticItemModel * bluetoothModeItemModel();
static AbstractStaticItemModel * serialModeItemModel(int port_nr);
static AbstractStaticItemModel * serialModeItemModel();
static AbstractStaticItemModel * internalModuleBaudrateItemModel();
static AbstractStaticItemModel * stickDeadZoneItemModel();
static AbstractStaticItemModel * uartSampleModeItemModel();
+137 -5
View File
@@ -43,7 +43,8 @@ void ModuleData::convert(RadioDataConversionState & cstate)
if ((PulsesProtocol) protocol != PULSES_OFF)
evt = RadioDataConversionState::EVT_INV;
}
else if (!fw->isAvailable((PulsesProtocol) protocol, cstate.subCompIdx)) {
else if (!isAvailable((PulsesProtocol) protocol, cstate.subCompIdx)) { // TODO: replace with call to ModuleData::isProtocolAvailable
//else if (!isProtocolAvailable(cstate.subCompIdx, (PulsesProtocol) protocol, cstate.toGS)) {
evt = RadioDataConversionState::EVT_INV;
}
}
@@ -58,6 +59,135 @@ void ModuleData::convert(RadioDataConversionState & cstate)
}
}
// moved from OpenTxFirmware EdgeTX v2.9
// only called by ModuleData::convert
// TODO: merge with ModuleData::isProtocolAvailable as share much of the same logic
// however they differ but why? Suspect have diverged as existence of both functions not known to devs
// static
bool ModuleData::isAvailable(PulsesProtocol proto, int port)
{
Firmware *fw = getCurrentFirmware();
Board::Type board = fw->getBoard();
QString id = fw->getId();
if (IS_HORUS_OR_TARANIS(board)) {
switch (port) {
case 0:
switch (proto) {
case PULSES_OFF:
return true;
case PULSES_PXX_XJT_X16:
case PULSES_PXX_XJT_LR12:
return !IS_ACCESS_RADIO(board, id) && !IS_FAMILY_T16(board) && !IS_FAMILY_T12(board) && !IS_FLYSKY_NV14(board);
case PULSES_PXX_XJT_D8:
return !(IS_ACCESS_RADIO(board, id) || id.contains("eu")) && !IS_FAMILY_T16(board) && !IS_FAMILY_T12(board) && !IS_FLYSKY_NV14(board);
case PULSES_ACCESS_ISRM:
case PULSES_ACCST_ISRM_D16:
return IS_ACCESS_RADIO(board, id);
case PULSES_MULTIMODULE:
return fw->getCapability(HasIntModuleMulti);
case PULSES_CROSSFIRE:
return fw->getCapability(HasIntModuleCRSF) || fw->getCapability(HasIntModuleELRS);
case PULSES_AFHDS3:
return fw->getCapability(HasIntModuleFlySky);
default:
return false;
}
case 1:
switch (proto) {
case PULSES_OFF:
case PULSES_PPM:
return true;
case PULSES_PXX_XJT_X16:
case PULSES_PXX_XJT_D8:
case PULSES_PXX_XJT_LR12:
return !(IS_TARANIS_XLITES(board) || IS_TARANIS_X9LITE(board));
case PULSES_PXX_R9M:
case PULSES_LP45:
case PULSES_DSM2:
case PULSES_DSMX:
case PULSES_SBUS:
case PULSES_MULTIMODULE:
case PULSES_CROSSFIRE:
case PULSES_AFHDS3:
case PULSES_GHOST:
return true;
case PULSES_ACCESS_R9M:
return IS_ACCESS_RADIO(board, id) || (IS_FAMILY_HORUS_OR_T16(board) && id.contains("externalaccessmod"));
case PULSES_PXX_R9M_LITE:
case PULSES_ACCESS_R9M_LITE:
case PULSES_ACCESS_R9M_LITE_PRO:
case PULSES_XJT_LITE_X16:
case PULSES_XJT_LITE_D8:
case PULSES_XJT_LITE_LR12:
return (IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board) || IS_RADIOMASTER_ZORRO(board));
default:
return false;
}
case -1:
switch (proto) {
case PULSES_PPM:
return true;
default:
return false;
}
default:
return false;
}
}
else if (IS_SKY9X(board)) {
switch (port) {
case 0:
switch (proto) {
case PULSES_PPM:
case PULSES_PXX_XJT_X16:
case PULSES_PXX_XJT_D8:
case PULSES_PXX_XJT_LR12:
case PULSES_PXX_R9M:
case PULSES_LP45:
case PULSES_DSM2:
case PULSES_DSMX:
case PULSES_SBUS:
case PULSES_MULTIMODULE:
return true;
default:
return false;
}
break;
case 1:
switch (proto) {
case PULSES_PPM:
return true;
default:
return false;
}
break;
default:
return false;
}
}
else {
switch (proto) {
case PULSES_PPM:
case PULSES_DSMX:
case PULSES_LP45:
case PULSES_DSM2:
// case PULSES_PXX_DJT: // Unavailable for now
case PULSES_PPM16:
case PULSES_PPMSIM:
return true;
default:
return false;
}
}
return false; // to avoid compiler warning
}
bool ModuleData::isPxx2Module() const
{
switch(protocol){
@@ -378,8 +508,9 @@ AbstractStaticItemModel * ModuleData::internalModuleItemModel(int board)
return mdl;
}
// TODO: merge with ModuleData::isAvailable noting the functions have diverged!!!
// static
bool ModuleData::isProtocolAvailable(int moduleidx, unsigned int protocol, GeneralSettings & settings)
bool ModuleData::isProtocolAvailable(int moduleidx, unsigned int protocol, GeneralSettings & generalSettings)
{
if (protocol == PULSES_OFF)
return true;
@@ -388,7 +519,7 @@ bool ModuleData::isProtocolAvailable(int moduleidx, unsigned int protocol, Gener
Board::Type board = fw->getBoard();
if (moduleidx == 0)
return (int)settings.internalModule == getTypeFromProtocol(protocol);
return (int)generalSettings.internalModule == getTypeFromProtocol(protocol);
QString id = fw->getId();
@@ -415,7 +546,8 @@ bool ModuleData::isProtocolAvailable(int moduleidx, unsigned int protocol, Gener
case PULSES_LEMON_DSMP:
return true;
case PULSES_ACCESS_R9M:
return IS_ACCESS_RADIO(board, id) || (IS_FAMILY_HORUS_OR_T16(board) && id.contains("externalaccessmod"));
return (IS_ACCESS_RADIO(board, id) ||
generalSettings.serialPort[GeneralSettings::SP_AUX1] == GeneralSettings::AUX_SERIAL_EXT_MODULE);
case PULSES_PXX_R9M_LITE:
case PULSES_ACCESS_R9M_LITE:
case PULSES_ACCESS_R9M_LITE_PRO:
@@ -515,5 +647,5 @@ AbstractStaticItemModel * ModuleData::telemetryBaudrateItemModel(unsigned int p
}
mdl->loadItemList();
return mdl;
return mdl;
}
+2 -1
View File
@@ -277,7 +277,8 @@ class ModuleData {
static int getSubTypeFromProtocol(unsigned int protocol);
static QString typeToString(int type);
static AbstractStaticItemModel * internalModuleItemModel(int board = -1);
static bool isProtocolAvailable(int moduleidx, unsigned int protocol, GeneralSettings & settings);
static bool isProtocolAvailable(int moduleidx, unsigned int protocol, GeneralSettings & generalSettings);
static AbstractStaticItemModel * protocolItemModel(GeneralSettings & settings);
static AbstractStaticItemModel * telemetryBaudrateItemModel(unsigned int protocol);
static bool isAvailable(PulsesProtocol proto, int port = 0); // moved from OpenTxFirmware EdgeTX v2.9 - TODO remove and use isProtocolAvailable
};
@@ -828,123 +828,6 @@ QTime OpenTxFirmware::getMaxTimerStart()
return QTime(8, 59, 59);
}
bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port)
{
if (IS_HORUS_OR_TARANIS(board)) {
switch (port) {
case 0:
switch (proto) {
case PULSES_OFF:
return true;
case PULSES_PXX_XJT_X16:
case PULSES_PXX_XJT_LR12:
return !IS_ACCESS_RADIO(board, id) && !IS_FAMILY_T16(board) && !IS_FAMILY_T12(board) && !IS_FLYSKY_NV14(board);
case PULSES_PXX_XJT_D8:
return !(IS_ACCESS_RADIO(board, id) || id.contains("eu")) && !IS_FAMILY_T16(board) && !IS_FAMILY_T12(board) && !IS_FLYSKY_NV14(board);
case PULSES_ACCESS_ISRM:
case PULSES_ACCST_ISRM_D16:
return IS_ACCESS_RADIO(board, id);
case PULSES_MULTIMODULE:
return getCapability(HasIntModuleMulti);
case PULSES_CROSSFIRE:
return getCapability(HasIntModuleCRSF) || getCapability(HasIntModuleELRS);
case PULSES_AFHDS3:
return getCapability(HasIntModuleFlySky);
default:
return false;
}
case 1:
switch (proto) {
case PULSES_OFF:
case PULSES_PPM:
return true;
case PULSES_PXX_XJT_X16:
case PULSES_PXX_XJT_D8:
case PULSES_PXX_XJT_LR12:
return !(IS_TARANIS_XLITES(board) || IS_TARANIS_X9LITE(board));
case PULSES_PXX_R9M:
case PULSES_LP45:
case PULSES_DSM2:
case PULSES_DSMX:
case PULSES_SBUS:
case PULSES_MULTIMODULE:
case PULSES_CROSSFIRE:
case PULSES_AFHDS3:
case PULSES_GHOST:
return true;
case PULSES_ACCESS_R9M:
return IS_ACCESS_RADIO(board, id) || (IS_FAMILY_HORUS_OR_T16(board) && id.contains("externalaccessmod"));
case PULSES_PXX_R9M_LITE:
case PULSES_ACCESS_R9M_LITE:
case PULSES_ACCESS_R9M_LITE_PRO:
case PULSES_XJT_LITE_X16:
case PULSES_XJT_LITE_D8:
case PULSES_XJT_LITE_LR12:
return (IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board) || IS_RADIOMASTER_ZORRO(board));
default:
return false;
}
case -1:
switch (proto) {
case PULSES_PPM:
return true;
default:
return false;
}
default:
return false;
}
}
else if (IS_SKY9X(board)) {
switch (port) {
case 0:
switch (proto) {
case PULSES_PPM:
case PULSES_PXX_XJT_X16:
case PULSES_PXX_XJT_D8:
case PULSES_PXX_XJT_LR12:
case PULSES_PXX_R9M:
case PULSES_LP45:
case PULSES_DSM2:
case PULSES_DSMX:
case PULSES_SBUS:
case PULSES_MULTIMODULE:
return true;
default:
return false;
}
break;
case 1:
switch (proto) {
case PULSES_PPM:
return true;
default:
return false;
}
break;
default:
return false;
}
}
else {
switch (proto) {
case PULSES_PPM:
case PULSES_DSMX:
case PULSES_LP45:
case PULSES_DSM2:
// case PULSES_PXX_DJT: // Unavailable for now
case PULSES_PPM16:
case PULSES_PPMSIM:
return true;
default:
return false;
}
}
}
template<typename T, size_t SIZE>
size_t getSizeA(T (&)[SIZE])
{
@@ -1385,7 +1268,6 @@ void registerOpenTxFirmwares()
firmware = new OpenTxFirmware(FIRMWAREID("x10"), Firmware::tr("FrSky Horus X10 / X10S"), BOARD_X10);
addOpenTxFrskyOptions(firmware);
firmware->addOption("internalaccess", Firmware::tr("Support for ACCESS internal module replacement"));
firmware->addOption("externalaccessmod", Firmware::tr("Support hardware mod: R9M ACCESS"));
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, EU + FLEX);
@@ -1399,7 +1281,6 @@ void registerOpenTxFirmwares()
firmware = new OpenTxFirmware(FIRMWAREID("x12s"), Firmware::tr("FrSky Horus X12S"), BOARD_HORUS_X12S);
addOpenTxFrskyOptions(firmware);
firmware->addOption("internalaccess", Firmware::tr("Support for ACCESS internal module replacement"));
firmware->addOption("externalaccessmod", Firmware::tr("Support hardware mod: R9M ACCESS"));
firmware->addOption("pcbdev", Firmware::tr("Use ONLY with first DEV pcb version"));
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, EU + FLEX);
@@ -1440,7 +1321,6 @@ void registerOpenTxFirmwares()
addOpenTxFrskyOptions(firmware);
firmware->addOption("internalmulti", Firmware::tr("Support for MULTI internal module"));
firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module"));
firmware->addOption("externalaccessmod", Firmware::tr("Support hardware mod: R9M ACCESS"));
addOpenTxRfOptions(firmware, FLEX);
registerOpenTxFirmware(firmware);
@@ -1503,7 +1383,6 @@ void registerOpenTxFirmwares()
static const Firmware::Option opt_bt("bluetooth", Firmware::tr("Support for bluetooth module"));
static const Firmware::Option opt_internal_gps("internalgps", Firmware::tr("Support internal GPS"));
firmware->addOptionsGroup({opt_bt, opt_internal_gps});
firmware->addOption("externalaccessmod", Firmware::tr("Support hardware mod: R9M ACCESS"));
firmware->addOption("flyskygimbals", Firmware::tr("Support hardware mod: FlySky Paladin EV Gimbals"));
registerOpenTxFirmware(firmware);
@@ -1511,7 +1390,6 @@ void registerOpenTxFirmwares()
firmware = new OpenTxFirmware(FIRMWAREID("t18"), Firmware::tr("Jumper T18"), BOARD_JUMPER_T18);
addOpenTxFrskyOptions(firmware);
firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module"));
firmware->addOption("externalaccessmod", Firmware::tr("Support hardware mod: R9M ACCESS"));
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, FLEX);
@@ -130,7 +130,12 @@ class OpenTxFirmware: public Firmware
virtual QTime getMaxTimerStart();
virtual bool isAvailable(PulsesProtocol proto, int port=0);
// moved to ModuleData EdgeTX v2.9
virtual bool isAvailable(PulsesProtocol proto, int port=0)
{
qDebug() << "WARNING: Depreciate function called. Always returns false!";
return false;
}
protected:
+10 -5
View File
@@ -34,6 +34,9 @@
constexpr char FIM_SWITCHTYPE2POS[] {"Switch Type 2 Pos"};
constexpr char FIM_SWITCHTYPE3POS[] {"Switch Type 3 Pos"};
constexpr char FIM_INTERNALMODULES[] {"Internal Modules"};
constexpr char FIM_AUX1SERIALMODES[] {"AUX1 Modes"};
constexpr char FIM_AUX2SERIALMODES[] {"AUX2 Modes"};
constexpr char FIM_VCPSERIALMODES[] {"VCP Modes"};
class ExclusiveComboGroup: public QObject
{
@@ -97,8 +100,10 @@ HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings
int antmodelid = editorItemModels->registerItemModel(GeneralSettings::antennaModeItemModel());
int btmodelid = editorItemModels->registerItemModel(GeneralSettings::bluetoothModeItemModel());
int auxmodelid = editorItemModels->registerItemModel(GeneralSettings::serialModeItemModel(GeneralSettings::SP_AUX1));
int vcpmodelid = editorItemModels->registerItemModel(GeneralSettings::serialModeItemModel(GeneralSettings::SP_VCP));
id = editorItemModels->registerItemModel(GeneralSettings::serialModeItemModel());
tabFilteredModels->registerItemModel(new FilteredItemModel(editorItemModels->getItemModel(id), GeneralSettings::AUX1Context), FIM_AUX1SERIALMODES);
tabFilteredModels->registerItemModel(new FilteredItemModel(editorItemModels->getItemModel(id), GeneralSettings::AUX2Context), FIM_AUX2SERIALMODES);
tabFilteredModels->registerItemModel(new FilteredItemModel(editorItemModels->getItemModel(id), GeneralSettings::VCPContext), FIM_VCPSERIALMODES);
int baudmodelid = editorItemModels->registerItemModel(GeneralSettings::internalModuleBaudrateItemModel());
int uartmodelid = editorItemModels->registerItemModel(GeneralSettings::uartSampleModeItemModel());
@@ -250,7 +255,7 @@ HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings
if (firmware->getCapability(HasAuxSerialMode)) {
addLabel(tr("AUX1"));
AutoComboBox *serialPortMode = new AutoComboBox(this);
serialPortMode->setModel(editorItemModels->getItemModel(auxmodelid));
serialPortMode->setModel(tabFilteredModels->getItemModel(FIM_AUX1SERIALMODES));
serialPortMode->setField(generalSettings.serialPort[GeneralSettings::SP_AUX1], this);
exclGroup->addCombo(serialPortMode);
params->append(serialPortMode);
@@ -269,7 +274,7 @@ HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings
if (firmware->getCapability(HasAux2SerialMode)) {
addLabel(tr("AUX2"));
AutoComboBox *serialPortMode = new AutoComboBox(this);
serialPortMode->setModel(editorItemModels->getItemModel(auxmodelid));
serialPortMode->setModel(tabFilteredModels->getItemModel(FIM_AUX2SERIALMODES));
serialPortMode->setField(generalSettings.serialPort[GeneralSettings::SP_AUX2], this);
exclGroup->addCombo(serialPortMode);
params->append(serialPortMode);
@@ -288,7 +293,7 @@ HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings
if (firmware->getCapability(HasVCPSerialMode)) {
addLabel(tr("USB-VCP"));
serialPortUSBVCP = new AutoComboBox(this);
serialPortUSBVCP->setModel(editorItemModels->getItemModel(vcpmodelid));
serialPortUSBVCP->setModel(tabFilteredModels->getItemModel(FIM_VCPSERIALMODES));
serialPortUSBVCP->setField(generalSettings.serialPort[GeneralSettings::SP_VCP], this);
exclGroup->addCombo(serialPortUSBVCP);
params->append(serialPortUSBVCP);
-3
View File
@@ -95,9 +95,6 @@ static const char * const options[] = {
#if defined(SHUTDOWN_CONFIRMATION)
"shutdownconfirm",
#endif
#if defined(HARDWARE_EXTERNAL_ACCESS_MOD)
"externalaccessmod",
#endif
#if defined(IMU_LSM6DS33)
"lsm6ds33",
#endif
-7
View File
@@ -6,7 +6,6 @@ option(PXX2 "PXX2 protocol support" OFF)
option(AFHDS3 "AFHDS3 TX Module" ON)
option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON)
option(GHOST "Ghost TX Module" ON)
option(HARDWARE_EXTERNAL_ACCESS_MOD "Support for R9M 2019 hardware mod" OFF)
option(SPACEMOUSE "Support for SpaceMouse" OFF)
set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)")
@@ -189,12 +188,6 @@ if(NOT UNEXPECTED_SHUTDOWN)
add_definitions(-DNO_UNEXPECTED_SHUTDOWN)
endif()
if(HARDWARE_EXTERNAL_ACCESS_MOD)
set(PXX2 ON)
set(AUX_SERIAL OFF)
add_definitions(-DHARDWARE_EXTERNAL_ACCESS_MOD)
endif()
if(IMU_LSM6DS33)
add_definitions(-DIMU_LSM6DS33)
set(AUX_SERIAL OFF)
+1 -1
View File
@@ -849,7 +849,7 @@
#define EXTMODULE_PWR_GPIO GPIOB
#define EXTMODULE_PWR_GPIO_PIN GPIO_Pin_3 // PB.03
#define EXTERNAL_MODULE_PWR_OFF() GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN)
#if (defined(PCBX10) && defined(PCBREV_EXPRESS)) || defined(HARDWARE_EXTERNAL_ACCESS_MOD)
#if defined(PCBX10) && defined(PCBREV_EXPRESS)
#define EXTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_DMA1)
#define EXTMODULE_TX_GPIO GPIOB
#define EXTMODULE_USART_GPIO EXTMODULE_TX_GPIO