diff --git a/Src/boards/rl/mini_v3/board/board.cmake b/Src/boards/rl/mini_v3/board/board.cmake
index 2511d4da..8b168543 100644
--- a/Src/boards/rl/mini_v3/board/board.cmake
+++ b/Src/boards/rl/mini_v3/board/board.cmake
@@ -18,6 +18,7 @@ set(BOARD_FLASH_ORIGIN_HEX 0x08000000)
set(BOARD_FLASH_SIZE_KB 512)
set(BOOTLOADER_RESERVED_KB 64)
list(APPEND APPLICATION_SOURCES
+ ${CMAKE_CURRENT_LIST_DIR}/gpio.cpp
${CMAKE_CURRENT_LIST_DIR}/pwm.cpp
${CMAKE_CURRENT_LIST_DIR}/rcpwm_channels.cpp
)
diff --git a/Src/boards/rl/mini_v3/board/gpio.cpp b/Src/boards/rl/mini_v3/board/gpio.cpp
new file mode 100644
index 00000000..19bb2c5d
--- /dev/null
+++ b/Src/boards/rl/mini_v3/board/gpio.cpp
@@ -0,0 +1,25 @@
+/**
+ * This program is free software under the GNU General Public License v3.
+ * See for details.
+ */
+
+#include
+#include
+#include "gpio_mapping.hpp"
+#include "peripheral/gpio/gpio_stm32.hpp"
+
+namespace HAL {
+namespace {
+const std::array kGpios = {{
+ {.port = CAN1_TERMINATOR_GPIO_Port, .pin = CAN1_TERMINATOR_Pin},
+ {.port = CAN2_TERMINATOR_GPIO_Port, .pin = CAN2_TERMINATOR_Pin},
+ }
+};
+static_assert(BoardGpio::CAN1_TERMINATOR == 0);
+static_assert(BoardGpio::CAN2_TERMINATOR == 1);
+static_assert(BoardGpio::CAN2_TERMINATOR + 1 == kGpios.size());
+}
+
+const std::span gpios{kGpios};
+
+} // namespace HAL
diff --git a/Src/boards/rl/mini_v3/board/gpio_mapping.hpp b/Src/boards/rl/mini_v3/board/gpio_mapping.hpp
new file mode 100644
index 00000000..e1a41892
--- /dev/null
+++ b/Src/boards/rl/mini_v3/board/gpio_mapping.hpp
@@ -0,0 +1,13 @@
+/**
+ * This program is free software under the GNU General Public License v3.
+ * See for details.
+ */
+
+#pragma once
+#include "peripheral/gpio/gpio.hpp"
+
+namespace BoardGpio {
+static constexpr HAL::GpioPin INVALID = HAL::GPIO_INVALID_PIN;
+static constexpr HAL::GpioPin CAN1_TERMINATOR = 0;
+static constexpr HAL::GpioPin CAN2_TERMINATOR = 1;
+} // namespace BoardGpio
diff --git a/Src/boards/rl/node_v4/board/gpio_mapping.hpp b/Src/boards/rl/node_v4/board/gpio_mapping.hpp
deleted file mode 100644
index 1da62104..00000000
--- a/Src/boards/rl/node_v4/board/gpio_mapping.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * This program is free software under the GNU General Public License v3.
- * See for details.
- */
-
-#ifndef SRC_BOARDS_RL_NODE_V4_GPIO_MAPPING_HPP_
-#define SRC_BOARDS_RL_NODE_V4_GPIO_MAPPING_HPP_
-
-#include "peripheral/gpio/gpio.hpp"
-
-namespace BoardGpio {
-
-static constexpr HAL::GPIO::Pin INVALID = HAL::GPIO::Pin::GPIO_AMOUNT;
-
-static constexpr HAL::GPIO::Pin INTERNAL_LED_RED = HAL::GPIO::Pin::INTERNAL_LED_RED;
-static constexpr HAL::GPIO::Pin INTERNAL_LED_GREEN = HAL::GPIO::Pin::INTERNAL_LED_GREEN;
-static constexpr HAL::GPIO::Pin INTERNAL_LED_BLUE = HAL::GPIO::Pin::INTERNAL_LED_BLUE;
-
-static constexpr HAL::GPIO::Pin EXT_RGB_LED_RED = INVALID;
-static constexpr HAL::GPIO::Pin EXT_RGB_LED_GREEN = INVALID;
-static constexpr HAL::GPIO::Pin EXT_RGB_LED_BLUE = INVALID;
-
-static constexpr HAL::GPIO::Pin CAN1_TERMINATOR = INVALID;
-static constexpr HAL::GPIO::Pin CAN2_TERMINATOR = INVALID;
-
-} // namespace BoardGpio
-
-#endif // SRC_BOARDS_RL_NODE_V4_GPIO_MAPPING_HPP_
diff --git a/Src/modules/application/application.cpp b/Src/modules/application/application.cpp
index 6edf69af..6b6d43ea 100644
--- a/Src/modules/application/application.cpp
+++ b/Src/modules/application/application.cpp
@@ -19,6 +19,10 @@
#include "module.hpp"
#include "main.h"
+#if defined(CAN1_TERMINATOR_Pin) && defined(CAN2_TERMINATOR_Pin)
+#include "gpio_mapping.hpp"
+#endif
+
#ifndef LIBPARAMS_HAS_REDUNDANT_STORAGE
#define LIBPARAMS_HAS_REDUNDANT_STORAGE 1
#endif
@@ -85,8 +89,8 @@ static int8_t init_board_periphery() {
auto teminator_param = paramsGetIntegerValue(IntParamsIndexes::PARAM_SYSTEM_CAN_TEMINATOR);
std::bitset<2> terminator_mask(teminator_param);
- HAL::GPIO::set(HAL::GPIO::Pin::CAN1_TERMINATOR, terminator_mask[0]);
- HAL::GPIO::set(HAL::GPIO::Pin::CAN2_TERMINATOR, terminator_mask[1]);
+ HAL::GPIO::set(BoardGpio::CAN1_TERMINATOR, terminator_mask[0]);
+ HAL::GPIO::set(BoardGpio::CAN2_TERMINATOR, terminator_mask[1]);
#endif
return 0;
}
diff --git a/Src/peripheral/gpio/CMakeLists.txt b/Src/peripheral/gpio/CMakeLists.txt
index fe474f40..abbeb49a 100644
--- a/Src/peripheral/gpio/CMakeLists.txt
+++ b/Src/peripheral/gpio/CMakeLists.txt
@@ -11,11 +11,10 @@ if(NOT APP_PLATFORM)
message(SEND_ERROR "APP_PLATFORM is not specified or unsupported! Options: stm32f103, stm32g0b1, stm32h753xx, ubuntu.")
endif()
-if(APP_PLATFORM STREQUAL "stm32f103")
-elseif(APP_PLATFORM STREQUAL "stm32g0b1")
- list(APPEND PERIPHERAL_SOURCES ${CMAKE_CURRENT_LIST_DIR}/gpio_stm32g0.cpp)
-elseif(APP_PLATFORM STREQUAL "stm32h753xx")
- list(APPEND PERIPHERAL_SOURCES ${CMAKE_CURRENT_LIST_DIR}/gpio_stm32h7.cpp)
+if(APP_PLATFORM STREQUAL "stm32f103" OR
+ APP_PLATFORM STREQUAL "stm32g0b1" OR
+ APP_PLATFORM STREQUAL "stm32h753xx")
+ list(APPEND PERIPHERAL_SOURCES ${CMAKE_CURRENT_LIST_DIR}/gpio_stm32.cpp)
elseif(APP_PLATFORM STREQUAL "ubuntu")
list(APPEND PERIPHERAL_SOURCES ${CMAKE_CURRENT_LIST_DIR}/gpio_ubuntu.cpp)
else()
diff --git a/Src/peripheral/gpio/gpio.hpp b/Src/peripheral/gpio/gpio.hpp
index c37d29eb..9f1375f1 100644
--- a/Src/peripheral/gpio/gpio.hpp
+++ b/Src/peripheral/gpio/gpio.hpp
@@ -12,42 +12,17 @@
namespace HAL {
+using GpioPin = uint8_t;
+inline constexpr GpioPin GPIO_INVALID_PIN = UINT8_MAX;
+
class GPIO {
public:
- enum class Pin {
- /**
- * @brief Any node must have an internal RGB LED
- */
- INTERNAL_LED_RED,
- INTERNAL_LED_GREEN,
- INTERNAL_LED_BLUE,
-
- /**
- * @brief A node may have an auxilliary external RGB LED
- */
- EXT_RGB_LED_RED,
- EXT_RGB_LED_GREEN,
- EXT_RGB_LED_BLUE,
-
- /**
- * @brief CAN terminator 120 ohm resistor
- * v2: doesn't exist yet
- * v3: PA15
- */
- CAN1_TERMINATOR,
-
- /**
- * @brief CAN terminator 120 ohm resistor
- * v2: doesn't exist yet
- * v3: PB15
- */
- CAN2_TERMINATOR,
-
- GPIO_AMOUNT,
- };
-
- static void set(const Pin gpio_pin, bool state);
- static bool get(const Pin gpio_pin);
+ static uint8_t pin_count();
+ static void set(GpioPin gpio_pin, bool state);
+ static bool get(GpioPin gpio_pin);
+
+private:
+ static bool is_valid_pin(GpioPin gpio_pin);
};
} // namespace HAL
diff --git a/Src/peripheral/gpio/gpio_stm32.cpp b/Src/peripheral/gpio/gpio_stm32.cpp
new file mode 100644
index 00000000..68f9daa6
--- /dev/null
+++ b/Src/peripheral/gpio/gpio_stm32.cpp
@@ -0,0 +1,42 @@
+/**
+ * This program is free software under the GNU General Public License v3.
+ * See for details.
+ */
+
+#include "peripheral/gpio/gpio_stm32.hpp"
+#include
+
+namespace HAL {
+
+namespace {
+const std::array kNoGpios{};
+}
+
+extern const std::span gpios __attribute__((weak)) = kNoGpios;
+
+bool GPIO::is_valid_pin(GpioPin gpio_pin) {
+ return gpio_pin < pin_count();
+}
+
+uint8_t GPIO::pin_count() {
+ return static_cast(gpios.size());
+}
+
+void GPIO::set(GpioPin gpio_pin, bool state) {
+ if (!is_valid_pin(gpio_pin)) {
+ return;
+ }
+
+ const auto pin_state = state ? GPIO_PIN_SET : GPIO_PIN_RESET;
+ HAL_GPIO_WritePin(gpios[gpio_pin].port, gpios[gpio_pin].pin, pin_state);
+}
+
+bool GPIO::get(GpioPin gpio_pin) {
+ if (!is_valid_pin(gpio_pin)) {
+ return false;
+ }
+
+ return HAL_GPIO_ReadPin(gpios[gpio_pin].port, gpios[gpio_pin].pin) == GPIO_PIN_SET;
+}
+
+} // namespace HAL
diff --git a/Src/peripheral/gpio/gpio_stm32.hpp b/Src/peripheral/gpio/gpio_stm32.hpp
new file mode 100644
index 00000000..cfc7551b
--- /dev/null
+++ b/Src/peripheral/gpio/gpio_stm32.hpp
@@ -0,0 +1,24 @@
+/**
+ * This program is free software under the GNU General Public License v3.
+ * See for details.
+ */
+
+#ifndef SRC_PERIPHERAL_GPIO_STM32_HPP_
+#define SRC_PERIPHERAL_GPIO_STM32_HPP_
+
+#include
+#include "main.h"
+#include "peripheral/gpio/gpio.hpp"
+
+namespace HAL {
+
+struct GpioPinInfo {
+ GPIO_TypeDef* port;
+ uint16_t pin;
+};
+
+extern const std::span gpios;
+
+} // namespace HAL
+
+#endif // SRC_PERIPHERAL_GPIO_STM32_HPP_
diff --git a/Src/peripheral/gpio/gpio_stm32g0.cpp b/Src/peripheral/gpio/gpio_stm32g0.cpp
deleted file mode 100644
index f0a69dcc..00000000
--- a/Src/peripheral/gpio/gpio_stm32g0.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * This program is free software under the GNU General Public License v3.
- * See for details.
- * Author: Anastasiia Stepanova
- * Author: Dmitry Ponomarev
- */
-
-#include "peripheral/gpio/gpio.hpp"
-#include "main.h"
-
-namespace HAL {
-
-void GPIO::set(Pin gpio_pin, bool state) {
-#ifdef CAN1_TERMINATOR_GPIO_Port
- auto pin_state = state ? GPIO_PIN_SET : GPIO_PIN_RESET;
- switch (gpio_pin) {
- case Pin::CAN1_TERMINATOR:
- HAL_GPIO_WritePin(CAN1_TERMINATOR_GPIO_Port, CAN1_TERMINATOR_Pin, pin_state);
- break;
-
- case Pin::CAN2_TERMINATOR:
- HAL_GPIO_WritePin(CAN2_TERMINATOR_GPIO_Port, CAN2_TERMINATOR_Pin, pin_state);
- break;
- default:
- break;
- }
-#else
- (void)gpio_pin;
- (void)state;
-#endif
-}
-
-bool GPIO::get(Pin gpio_pin) {
-#ifdef CAN1_TERMINATOR_GPIO_Port
- switch (gpio_pin) {
- case Pin::CAN1_TERMINATOR:
- return HAL_GPIO_ReadPin(CAN1_TERMINATOR_GPIO_Port, CAN1_TERMINATOR_Pin);
-
- case Pin::CAN2_TERMINATOR:
- return HAL_GPIO_ReadPin(CAN2_TERMINATOR_GPIO_Port, CAN2_TERMINATOR_Pin);
-
- default:
- return GPIO_PIN_SET;
- }
-#else
- (void)gpio_pin;
- return GPIO_PIN_RESET;
-#endif
-}
-
-} // namespace HAL
diff --git a/Src/peripheral/gpio/gpio_stm32h7.cpp b/Src/peripheral/gpio/gpio_stm32h7.cpp
deleted file mode 100644
index da54103b..00000000
--- a/Src/peripheral/gpio/gpio_stm32h7.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * This program is free software under the GNU General Public License v3.
- * See for details.
- */
-
-#include "peripheral/gpio/gpio.hpp"
-#include "main.h"
-
-namespace HAL {
-
-void GPIO::set(Pin gpio_pin, bool state) {
-#if defined(CAN1_TERMINATOR_GPIO_Port) || defined(CAN2_TERMINATOR_GPIO_Port)
- const auto pin_state = state ? GPIO_PIN_SET : GPIO_PIN_RESET;
- switch (gpio_pin) {
- #ifdef CAN1_TERMINATOR_GPIO_Port
- case Pin::CAN1_TERMINATOR:
- HAL_GPIO_WritePin(CAN1_TERMINATOR_GPIO_Port, CAN1_TERMINATOR_Pin, pin_state);
- break;
- #endif
-
- #ifdef CAN2_TERMINATOR_GPIO_Port
- case Pin::CAN2_TERMINATOR:
- HAL_GPIO_WritePin(CAN2_TERMINATOR_GPIO_Port, CAN2_TERMINATOR_Pin, pin_state);
- break;
- #endif
-
- default:
- break;
- }
-#else
- (void)gpio_pin;
- (void)state;
-#endif
-}
-
-bool GPIO::get(Pin gpio_pin) {
-#if defined(CAN1_TERMINATOR_GPIO_Port) || defined(CAN2_TERMINATOR_GPIO_Port)
- switch (gpio_pin) {
- #ifdef CAN1_TERMINATOR_GPIO_Port
- case Pin::CAN1_TERMINATOR:
- return HAL_GPIO_ReadPin(CAN1_TERMINATOR_GPIO_Port, CAN1_TERMINATOR_Pin);
- #endif
-
- #ifdef CAN2_TERMINATOR_GPIO_Port
- case Pin::CAN2_TERMINATOR:
- return HAL_GPIO_ReadPin(CAN2_TERMINATOR_GPIO_Port, CAN2_TERMINATOR_Pin);
- #endif
-
- default:
- return GPIO_PIN_RESET;
- }
-#else
- (void)gpio_pin;
- return GPIO_PIN_RESET;
-#endif
-}
-
-} // namespace HAL
diff --git a/Src/peripheral/gpio/gpio_ubuntu.cpp b/Src/peripheral/gpio/gpio_ubuntu.cpp
index da6f940e..71df34e5 100644
--- a/Src/peripheral/gpio/gpio_ubuntu.cpp
+++ b/Src/peripheral/gpio/gpio_ubuntu.cpp
@@ -5,20 +5,35 @@
* Author: Dmitry Ponomarev
*/
-#include "peripheral/gpio/gpio.hpp"
-#include
-#include "main.h"
+#include "peripheral/gpio/gpio_ubuntu.hpp"
+#include
namespace HAL {
-static std::vector gpio((int)GPIO::Pin::GPIO_AMOUNT, false);
+namespace {
+std::array kNoGpios{};
+}
+
+[[gnu::weak]] std::span gpios{kNoGpios};
+
+bool GPIO::is_valid_pin(GpioPin gpio_pin) {
+ return gpio_pin < pin_count();
+}
+
+uint8_t GPIO::pin_count() {
+ return static_cast(gpios.size());
+}
+
+void GPIO::set(GpioPin gpio_pin, bool state) {
+ if (!is_valid_pin(gpio_pin)) {
+ return;
+ }
-void GPIO::set(Pin gpio_pin, bool state) {
- gpio[(int)gpio_pin] = state;
+ gpios[gpio_pin] = state;
}
-bool GPIO::get(Pin gpio_pin) {
- return gpio[(int)gpio_pin];
+bool GPIO::get(GpioPin gpio_pin) {
+ return is_valid_pin(gpio_pin) ? gpios[gpio_pin] : false;
}
} // namespace HAL
diff --git a/Src/peripheral/gpio/gpio_ubuntu.hpp b/Src/peripheral/gpio/gpio_ubuntu.hpp
new file mode 100644
index 00000000..e7facba3
--- /dev/null
+++ b/Src/peripheral/gpio/gpio_ubuntu.hpp
@@ -0,0 +1,18 @@
+/**
+ * This program is free software under the GNU General Public License v3.
+ * See for details.
+ */
+
+#ifndef SRC_PERIPHERAL_GPIO_UBUNTU_HPP_
+#define SRC_PERIPHERAL_GPIO_UBUNTU_HPP_
+
+#include
+#include "peripheral/gpio/gpio.hpp"
+
+namespace HAL {
+
+extern std::span gpios;
+
+} // namespace HAL
+
+#endif // SRC_PERIPHERAL_GPIO_UBUNTU_HPP_
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index a45550eb..e461fcd8 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -40,3 +40,11 @@ endfunction()
gen_test(fft ${UNIT_TESTS_DIR}/common/test_fft.cpp)
gen_test(algorithms ${UNIT_TESTS_DIR}/common/test_algorithms.cpp)
+
+add_executable(gpio
+ ${UNIT_TESTS_DIR}/peripheral/test_gpio.cpp
+ ${ROOT_DIR}/Src/peripheral/gpio/gpio_ubuntu.cpp
+)
+target_include_directories(gpio PUBLIC ${ROOT_DIR}/Src)
+target_link_libraries(gpio GTest::gtest GTest::gtest_main)
+add_test(NAME gpio COMMAND gpio)
diff --git a/Tests/peripheral/test_gpio.cpp b/Tests/peripheral/test_gpio.cpp
new file mode 100644
index 00000000..00435831
--- /dev/null
+++ b/Tests/peripheral/test_gpio.cpp
@@ -0,0 +1,51 @@
+/**
+ * This program is free software under the GNU General Public License v3.
+ * See for details.
+ */
+
+#include
+#include
+#include
+#include "peripheral/gpio/gpio_ubuntu.hpp"
+
+namespace HAL {
+namespace {
+std::array kGpios{};
+}
+std::span gpios{kGpios};
+} // namespace HAL
+
+class GpioTest : public testing::Test {
+protected:
+ void SetUp() override {
+ HAL::GPIO::set(0, false);
+ HAL::GPIO::set(1, false);
+ }
+};
+
+TEST_F(GpioTest, StoresIndependentPinStates) {
+ HAL::GPIO::set(0, true);
+
+ EXPECT_TRUE(HAL::GPIO::get(0));
+ EXPECT_FALSE(HAL::GPIO::get(1));
+
+ HAL::GPIO::set(1, true);
+
+ EXPECT_TRUE(HAL::GPIO::get(0));
+ EXPECT_TRUE(HAL::GPIO::get(1));
+}
+
+TEST_F(GpioTest, InvalidReadReturnsFalse) {
+ EXPECT_FALSE(HAL::GPIO::get(HAL::GPIO_INVALID_PIN));
+ EXPECT_FALSE(HAL::GPIO::get(HAL::GPIO::pin_count()));
+}
+
+TEST_F(GpioTest, InvalidWriteDoesNotChangeValidPins) {
+ HAL::GPIO::set(0, true);
+
+ HAL::GPIO::set(HAL::GPIO_INVALID_PIN, false);
+ HAL::GPIO::set(HAL::GPIO::pin_count(), false);
+
+ EXPECT_TRUE(HAL::GPIO::get(0));
+ EXPECT_FALSE(HAL::GPIO::get(1));
+}