Merge 82df51d4f5 into b5d18369bb
This commit is contained in:
commit
df5cec6831
|
|
@ -7,6 +7,7 @@ add_definitions(-DVERSION=\"v${PROJECT_VERSION}\")
|
||||||
|
|
||||||
option(BUILD_STATIC "Whether or not to build with static libraries" OFF)
|
option(BUILD_STATIC "Whether or not to build with static libraries" OFF)
|
||||||
option(ENABLE_UDEV "use udev to search JTAG adapter from /dev/xx" ON)
|
option(ENABLE_UDEV "use udev to search JTAG adapter from /dev/xx" ON)
|
||||||
|
option(ENABLE_FPGALINK "enable fx2 cable support through libfpgalink" OFF)
|
||||||
option(USE_PKGCONFIG "Use pkgconfig to find libraries" ON)
|
option(USE_PKGCONFIG "Use pkgconfig to find libraries" ON)
|
||||||
option(LINK_CMAKE_THREADS "Use CMake find_package to link the threading library" OFF)
|
option(LINK_CMAKE_THREADS "Use CMake find_package to link the threading library" OFF)
|
||||||
|
|
||||||
|
|
@ -139,8 +140,26 @@ else()
|
||||||
target_link_libraries(openFPGALoader
|
target_link_libraries(openFPGALoader
|
||||||
${LIBUSB_LIBRARIES}
|
${LIBUSB_LIBRARIES}
|
||||||
${LIBFTDI_LIBRARIES}
|
${LIBFTDI_LIBRARIES}
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (ENABLE_FPGALINK)
|
||||||
|
find_library(LIBFPGALINK libfpgalink)
|
||||||
|
if (${LIBFPGALINK} STREQUAL "LIBFPGALINK-NOTFOUND")
|
||||||
|
message("libfpgalink not found, disabling fpgalink support")
|
||||||
|
set(ENABLE_FPGALINK OFF)
|
||||||
|
else()
|
||||||
|
message("libfpgalink found: ${LIBFPGALINK}")
|
||||||
|
file(REAL_PATH "../../include" INC_DIR BASE_DIRECTORY ${LIBFPGALINK})
|
||||||
|
add_definitions(-DUSE_LIBFPGALINK)
|
||||||
|
target_include_directories(openFPGALoader PRIVATE ${INC_DIR})
|
||||||
|
target_link_libraries(openFPGALoader ${LIBFPGALINK})
|
||||||
|
target_sources(openFPGALoader PRIVATE
|
||||||
|
src/fpgalink.cpp
|
||||||
|
src/fpgalink.hpp)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||||
# winsock provides ntohs
|
# winsock provides ntohs
|
||||||
target_link_libraries(openFPGALoader ws2_32)
|
target_link_libraries(openFPGALoader ws2_32)
|
||||||
|
|
|
||||||
13
INSTALL.md
13
INSTALL.md
|
|
@ -5,6 +5,16 @@
|
||||||
```
|
```
|
||||||
$ git clone https://github.com/trabucayre/openFPGALoader.git
|
$ git clone https://github.com/trabucayre/openFPGALoader.git
|
||||||
```
|
```
|
||||||
|
## For libfpgalink support (fx2 cable and more) add the following steps
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git clone https://github.com/makestuff/fpgalink.git
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
$ build.sh
|
||||||
|
```
|
||||||
|
Add the install/bin directory to the path (for building and running OpenFPGALoader)
|
||||||
|
|
||||||
## Compile from source
|
## Compile from source
|
||||||
|
|
||||||
|
|
@ -13,11 +23,14 @@ $ mkdir build
|
||||||
$ cd build
|
$ cd build
|
||||||
$ cmake ../ # add -DBUILD_STATIC=ON to build a static version
|
$ cmake ../ # add -DBUILD_STATIC=ON to build a static version
|
||||||
# add -DENABLE_UDEV=OFF to disable udev support and -d /dev/xxx
|
# add -DENABLE_UDEV=OFF to disable udev support and -d /dev/xxx
|
||||||
|
# add -DENABLE_LIBFPGALINK=ON to enable libfpgalink support
|
||||||
$ cmake --build .
|
$ cmake --build .
|
||||||
or
|
or
|
||||||
$ make -j$(nproc)
|
$ make -j$(nproc)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
As root:
|
As root:
|
||||||
|
|
|
||||||
|
|
@ -260,6 +260,15 @@ allowed values are:
|
||||||
| DCD | 6 |
|
| DCD | 6 |
|
||||||
| RI | 7 |
|
| RI | 7 |
|
||||||
|
|
||||||
|
#### FX2 cables and pins configuration
|
||||||
|
|
||||||
|
FX2 cables need to have their pin mapping provided
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openFPGALoader [options] -cfx2 --pins=TDI:TDO:TCK:TMS /path/to/bitstream.ext
|
||||||
|
```
|
||||||
|
where:
|
||||||
|
* TDI:TDO:TCK:TMS may be the pin ID (A0 <= id <= D7) for pins PA0...PD7
|
||||||
|
|
||||||
### CYC1000 and de0nano
|
### CYC1000 and de0nano
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ enum communication_type {
|
||||||
MODE_FTDI_BITBANG = 1, /*! used with ft232RL/ft231x */
|
MODE_FTDI_BITBANG = 1, /*! used with ft232RL/ft231x */
|
||||||
MODE_FTDI_SERIAL = 2, /*! ft2232, ft232H */
|
MODE_FTDI_SERIAL = 2, /*! ft2232, ft232H */
|
||||||
MODE_DIRTYJTAG = 3, /*! JTAG probe firmware for STM32F1 */
|
MODE_DIRTYJTAG = 3, /*! JTAG probe firmware for STM32F1 */
|
||||||
MODE_USBBLASTER = 4, /*! JTAG probe firmware for USBBLASTER */
|
MODE_USBBLASTER = 4, /*! JTAG probe firmware for USBBLASTER */
|
||||||
|
MODE_FX2 = 5, /* FX2 support through libFPGAlink */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -41,6 +42,9 @@ static std::map <std::string, cable_t> cable_list = {
|
||||||
{"ft4232", {MODE_FTDI_SERIAL, {0x0403, 0x6011, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}},
|
{"ft4232", {MODE_FTDI_SERIAL, {0x0403, 0x6011, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}},
|
||||||
{"ecpix5-debug", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0xF8, 0xFB, 0xFF, 0xFF}}},
|
{"ecpix5-debug", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0xF8, 0xFB, 0xFF, 0xFF}}},
|
||||||
{"usb-blaster", {MODE_USBBLASTER, {}}},
|
{"usb-blaster", {MODE_USBBLASTER, {}}},
|
||||||
|
#ifdef USE_LIBFPGALINK
|
||||||
|
{"fx2", {MODE_FX2, {}}},
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#include <libusb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "display.hpp"
|
||||||
|
#include "fpgalink.hpp"
|
||||||
|
#include "makestuff/libfpgalink.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#undef CHECK_STATUS
|
||||||
|
|
||||||
|
#define CHECK_STATUS(fStatus)\
|
||||||
|
if (fStatus) \
|
||||||
|
{ \
|
||||||
|
printError("Error: ", false);\
|
||||||
|
printError(error);\
|
||||||
|
flFreeError(error);\
|
||||||
|
fStatus = progClose(handle, &error);\
|
||||||
|
if (error)\
|
||||||
|
flFreeError(error);\
|
||||||
|
flClose(handle);\
|
||||||
|
handle = NULL;\
|
||||||
|
throw std::exception();\
|
||||||
|
}
|
||||||
|
|
||||||
|
FpgaLink::FpgaLink(bool verbose):
|
||||||
|
_verbose(verbose)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int FpgaLink::writeTMS(uint8_t *tms, int len, bool flush_buffer)
|
||||||
|
{
|
||||||
|
(void)flush_buffer;
|
||||||
|
FLStatus fStatus;
|
||||||
|
const char *error = NULL;
|
||||||
|
uint32_t bitPattern = 0;
|
||||||
|
int ulen = len;
|
||||||
|
int i = 0;
|
||||||
|
while (ulen > 0)
|
||||||
|
{
|
||||||
|
{bitPattern = tms[i++];}
|
||||||
|
ulen-=8;
|
||||||
|
if (ulen > 0) {bitPattern |= tms[i++] << 8;}
|
||||||
|
ulen-=8;
|
||||||
|
if (ulen > 0) {bitPattern |= tms[i++] << 16;}
|
||||||
|
ulen-=8;
|
||||||
|
if (ulen > 0) {bitPattern |= tms[i++] << 24;}
|
||||||
|
ulen-=8;
|
||||||
|
fStatus = jtagClockFSM(handle, bitPattern, (ulen > 0) ? 32 : 32 + ulen, &error);
|
||||||
|
CHECK_STATUS(fStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FpgaLink::toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len)
|
||||||
|
{
|
||||||
|
(void)tms;
|
||||||
|
(void)tdi;
|
||||||
|
FLStatus fStatus;
|
||||||
|
const char *error = NULL;
|
||||||
|
fStatus = jtagClocks(handle, clk_len, &error);
|
||||||
|
CHECK_STATUS(fStatus);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FpgaLink::flush()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FpgaLink::writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end)
|
||||||
|
{
|
||||||
|
FLStatus fStatus;
|
||||||
|
const char *error = NULL;
|
||||||
|
if (rx)
|
||||||
|
fStatus = jtagShiftInOut(handle, len, tx, rx, end, &error);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fStatus = jtagShiftInOnly(handle, len, tx, end, &error);
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_STATUS(fStatus);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FX2Cable::FX2Cable(bool verbose, const jtag_pins_conf_t *pin_conf, const char *ivp, const char *vp):
|
||||||
|
FpgaLink::FpgaLink(verbose)
|
||||||
|
{
|
||||||
|
FLStatus fStatus;
|
||||||
|
const char *error = NULL;
|
||||||
|
fStatus = flInitialise(0, &error);
|
||||||
|
CHECK_STATUS(fStatus);
|
||||||
|
char portConfig[10];
|
||||||
|
|
||||||
|
vp = "1D50:602B";
|
||||||
|
std::string svp(vp);
|
||||||
|
|
||||||
|
printInfo("Attempting to open connection to FPGALink device " + svp +"...", false);
|
||||||
|
fStatus = flOpen(vp, &handle, NULL);
|
||||||
|
if ( fStatus ) {
|
||||||
|
{
|
||||||
|
int count = 60;
|
||||||
|
uint8 flag;
|
||||||
|
ivp = "04b4:8613";
|
||||||
|
std::string sivp(ivp);
|
||||||
|
printInfo("Loading firmware into " + sivp, true);
|
||||||
|
fStatus = flLoadStandardFirmware(ivp, vp, &error);
|
||||||
|
CHECK_STATUS(fStatus);
|
||||||
|
|
||||||
|
printInfo("Awaiting renumeration");
|
||||||
|
flSleep(1000);
|
||||||
|
do {
|
||||||
|
printInfo(".", false);
|
||||||
|
fflush(stdout);
|
||||||
|
fStatus = flIsDeviceAvailable(vp, &flag, &error);
|
||||||
|
CHECK_STATUS(fStatus);
|
||||||
|
flSleep(250);
|
||||||
|
count--;
|
||||||
|
} while ( !flag && count );
|
||||||
|
printf("\n");
|
||||||
|
if ( !flag ) {
|
||||||
|
printError("Error: FPGALink device did not renumerate properly as " + svp, true);
|
||||||
|
flClose(handle);
|
||||||
|
handle = NULL;
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
printInfo("Attempting to open connection to FPGLink device " + svp + " again...", false);
|
||||||
|
fStatus = flOpen(vp, &handle, &error);
|
||||||
|
CHECK_STATUS(fStatus);
|
||||||
|
printInfo("success", true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printInfo("success", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
transcode_pin_config(pin_conf, portConfig);
|
||||||
|
fStatus = progOpen(handle, portConfig, &error);
|
||||||
|
CHECK_STATUS(fStatus);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FX2Cable::transcode_pin_config(const jtag_pins_conf_t *pin_conf, char* buffer)
|
||||||
|
{
|
||||||
|
sprintf(buffer, "%X%X%X%X",pin_conf->tdo_pin, pin_conf->tdi_pin, pin_conf->tms_pin, pin_conf->tck_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
FX2Cable::~FX2Cable()
|
||||||
|
{
|
||||||
|
const char *error = NULL;
|
||||||
|
FLStatus fStatus;
|
||||||
|
(void)fStatus;
|
||||||
|
if (handle)
|
||||||
|
{
|
||||||
|
fStatus = progClose(handle, &error);
|
||||||
|
flClose(handle);
|
||||||
|
handle = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRC_FX2CABLE_HPP_
|
||||||
|
#define SRC_FX2CABLE_HPP_
|
||||||
|
|
||||||
|
#include <libusb.h>
|
||||||
|
|
||||||
|
#include "jtagInterface.hpp"
|
||||||
|
#include "board.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \file FX2Cable.hpp
|
||||||
|
* \class FX2Cable
|
||||||
|
* \brief concrete class between jtag implementation and FX2 cable
|
||||||
|
*/
|
||||||
|
|
||||||
|
class FpgaLink : public JtagInterface{
|
||||||
|
|
||||||
|
public:
|
||||||
|
FpgaLink(bool verbose);
|
||||||
|
int setClkFreq(uint32_t clkHZ) override{(void)clkHZ; return 0;}
|
||||||
|
int setClkFreq(uint32_t clkHZ, char use_divide_by_5) override {(void)clkHZ; (void)use_divide_by_5;return 0;}
|
||||||
|
|
||||||
|
/* TMS */
|
||||||
|
int writeTMS(uint8_t *tms, int len, bool flush_buffer) override;
|
||||||
|
/* TDI */
|
||||||
|
int writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end) override;
|
||||||
|
/* clk */
|
||||||
|
int toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len) override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief return internal buffer size (in byte).
|
||||||
|
* \return _buffer_size divided by 2 (two byte for clk) and divided by 8 (one
|
||||||
|
* state == one byte)
|
||||||
|
*/
|
||||||
|
int get_buffer_size() override { return 0;}
|
||||||
|
|
||||||
|
bool isFull() override { return false;}
|
||||||
|
|
||||||
|
int flush() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool _verbose;
|
||||||
|
struct FLContext *handle = NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class FX2Cable : public FpgaLink {
|
||||||
|
public:
|
||||||
|
FX2Cable(bool verbose, const jtag_pins_conf_t *pin_conf, const char *ivp = NULL, const char *vp = NULL);
|
||||||
|
virtual ~FX2Cable();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void transcode_pin_config(const jtag_pins_conf_t *pin_conf, char* buffer);
|
||||||
|
};
|
||||||
|
#endif // SRC_FX2CABLE_HPP_
|
||||||
|
|
@ -33,6 +33,10 @@
|
||||||
#include "dirtyJtag.hpp"
|
#include "dirtyJtag.hpp"
|
||||||
#include "usbBlaster.hpp"
|
#include "usbBlaster.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_LIBFPGALINK
|
||||||
|
#include "fpgalink.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#define DEBUG 0
|
#define DEBUG 0
|
||||||
|
|
@ -103,6 +107,11 @@ void Jtag::init_internal(cable_t &cable, const string &dev, const string &serial
|
||||||
case MODE_USBBLASTER:
|
case MODE_USBBLASTER:
|
||||||
_jtag = new UsbBlaster(_verbose);
|
_jtag = new UsbBlaster(_verbose);
|
||||||
break;
|
break;
|
||||||
|
#ifdef USE_LIBFPGALINK
|
||||||
|
case MODE_FX2:
|
||||||
|
_jtag = new FX2Cable(_verbose, pin_conf);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
std::cerr << "Jtag: unknown cable type" << std::endl;
|
std::cerr << "Jtag: unknown cable type" << std::endl;
|
||||||
throw std::exception();
|
throw std::exception();
|
||||||
|
|
|
||||||
14
src/main.cpp
14
src/main.cpp
|
|
@ -383,7 +383,7 @@ int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *p
|
||||||
"write bitstream in SRAM (default: true, only for Gowin and ECP5 devices)")
|
"write bitstream in SRAM (default: true, only for Gowin and ECP5 devices)")
|
||||||
("o,offset", "start offset in EEPROM",
|
("o,offset", "start offset in EEPROM",
|
||||||
cxxopts::value<unsigned int>(args->offset))
|
cxxopts::value<unsigned int>(args->offset))
|
||||||
("pins", "pin config (only for ft232R) TDI:TDO:TCK:TMS",
|
("pins", "pin config (only for ft232R and fx2) TDI:TDO:TCK:TMS",
|
||||||
cxxopts::value<vector<string>>(pins))
|
cxxopts::value<vector<string>>(pins))
|
||||||
("quiet", "Produce quiet output (no progress bar)",
|
("quiet", "Produce quiet output (no progress bar)",
|
||||||
cxxopts::value<bool>(quiet))
|
cxxopts::value<bool>(quiet))
|
||||||
|
|
@ -467,7 +467,7 @@ int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *p
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
int pin_num;
|
int pin_num;
|
||||||
try {
|
try {
|
||||||
pin_num = std::stoi(pins[i], nullptr, 0);
|
pin_num = std::stoi(pins[i], nullptr, 16);
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
if (pins_list.find(pins[i]) == pins_list.end()) {
|
if (pins_list.find(pins[i]) == pins_list.end()) {
|
||||||
printError("Invalid pin name");
|
printError("Invalid pin name");
|
||||||
|
|
@ -475,8 +475,16 @@ int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *p
|
||||||
}
|
}
|
||||||
pin_num = pins_list[pins[i]];
|
pin_num = pins_list[pins[i]];
|
||||||
}
|
}
|
||||||
|
#ifdef USE_LIBFPGALINK
|
||||||
|
if ((pin_num > 7 || pin_num < 0) &&
|
||||||
|
(pin_num > 0xD7 || pin_num < 0xD0) &&
|
||||||
|
(pin_num > 0xC7 || pin_num < 0xC0) &&
|
||||||
|
(pin_num > 0xB7 || pin_num < 0xB0) &&
|
||||||
|
(pin_num > 0xA7 || pin_num < 0xA0)) {
|
||||||
|
#else
|
||||||
if (pin_num > 7 || pin_num < 0) {
|
if (pin_num > 7 || pin_num < 0) {
|
||||||
|
#endif
|
||||||
|
|
||||||
printError("Invalid pin ID");
|
printError("Invalid pin ID");
|
||||||
throw std::exception();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue