mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
[cxx]: GPIO CXX wrappers, experiemental CI rule
* Wrapper class for simple GPIO interaction like read/write without ISRs. * Added rule to provoke builds after changes in the experimental C++ component.
This commit is contained in:
parent
b5f9149399
commit
06956d46c1
@ -362,3 +362,10 @@ test_rom_on_linux_works:
|
||||
- cd ${IDF_PATH}/components/esp_rom/host_test/rom_test
|
||||
- idf.py build
|
||||
- build/test_rom_host.elf
|
||||
|
||||
test_cxx_gpio:
|
||||
extends: .host_test_template
|
||||
script:
|
||||
- cd ${IDF_PATH}/examples/cxx/experimental/experimental_cpp_component/host_test/gpio
|
||||
- idf.py build
|
||||
- build/test_gpio_cxx_host.elf
|
||||
|
@ -37,6 +37,7 @@
|
||||
|
||||
.patterns-build_components: &patterns-build_components
|
||||
- "components/**/*"
|
||||
- "examples/cxx/experimental/experimental_cpp_component/*"
|
||||
|
||||
.patterns-build_system: &patterns-build_system
|
||||
- "tools/cmake/**/*"
|
||||
|
10
examples/cxx/experimental/blink_cxx/CMakeLists.txt
Normal file
10
examples/cxx/experimental/blink_cxx/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# For more information about build system see
|
||||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(blink_cxx)
|
57
examples/cxx/experimental/blink_cxx/README.md
Normal file
57
examples/cxx/experimental/blink_cxx/README.md
Normal file
@ -0,0 +1,57 @@
|
||||
# Example: Blink C++ example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates usage of the `GPIO_Output` C++ class in ESP-IDF.
|
||||
|
||||
In this example, the `sdkconfig.defaults` file sets the `CONFIG_COMPILER_CXX_EXCEPTIONS` option.
|
||||
This enables both compile time support (`-fexceptions` compiler flag) and run-time support for C++ exception handling.
|
||||
This is necessary for the C++ APIs.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
Any ESP32 family development board.
|
||||
|
||||
Connect an LED to the corresponding pin (default is pin 4). If the board has a normal LED already, you can use the pin number to which that one is connected.
|
||||
|
||||
Development boards with an RGB LED that only has one data line like the ESP32-C3-DevKitC-02 and ESP32-C3-DevKitM-1 will not work. In this case, please connect an external normal LED to the chosen pin.
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
...
|
||||
I (339) cpu_start: Starting scheduler.
|
||||
I (343) gpio: GPIO[4]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
LED ON
|
||||
LED OFF
|
||||
LED ON
|
||||
LED OFF
|
||||
LED ON
|
||||
LED OFF
|
||||
LED ON
|
||||
LED OFF
|
||||
LED ON
|
||||
LED OFF
|
||||
|
||||
```
|
||||
|
2
examples/cxx/experimental/blink_cxx/main/CMakeLists.txt
Normal file
2
examples/cxx/experimental/blink_cxx/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "main.cpp"
|
||||
INCLUDE_DIRS ".")
|
39
examples/cxx/experimental/blink_cxx/main/main.cpp
Normal file
39
examples/cxx/experimental/blink_cxx/main/main.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
/* Blink C++ Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <thread>
|
||||
#include "esp_log.h"
|
||||
#include "gpio_cxx.hpp"
|
||||
|
||||
using namespace idf;
|
||||
using namespace std;
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
/* The functions of GPIO_Output throws exceptions in case of parameter errors or if there are underlying driver
|
||||
errors. */
|
||||
try {
|
||||
/* This line may throw an exception if the pin number is invalid.
|
||||
* Alternatively to 4, choose another output-capable pin. */
|
||||
GPIO_Output gpio(GPIONum(4));
|
||||
|
||||
while (true) {
|
||||
printf("LED ON\n");
|
||||
gpio.set_high();
|
||||
this_thread::sleep_for(std::chrono::seconds(1));
|
||||
printf("LED OFF\n");
|
||||
gpio.set_low();
|
||||
this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
} catch (GPIOException &e) {
|
||||
printf("GPIO exception occurred: %s\n", esp_err_to_name(e.error));
|
||||
printf("stopping.\n");
|
||||
}
|
||||
}
|
3
examples/cxx/experimental/blink_cxx/sdkconfig.defaults
Normal file
3
examples/cxx/experimental/blink_cxx/sdkconfig.defaults
Normal file
@ -0,0 +1,3 @@
|
||||
# Enable C++ exceptions and set emergency pool size for exception objects
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024
|
@ -1,6 +1,7 @@
|
||||
idf_component_register(SRCS
|
||||
"esp_exception.cpp"
|
||||
"i2c_cxx.cpp"
|
||||
"gpio_cxx.cpp"
|
||||
"esp_event_api.cpp"
|
||||
"esp_event_cxx.cpp"
|
||||
"esp_timer_cxx.cpp"
|
||||
|
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#if __cpp_exceptions
|
||||
|
||||
#include <array>
|
||||
#include "driver/gpio.h"
|
||||
#include "gpio_cxx.hpp"
|
||||
|
||||
namespace idf {
|
||||
|
||||
#define GPIO_CHECK_THROW(err) CHECK_THROW_SPECIFIC((err), GPIOException)
|
||||
|
||||
namespace {
|
||||
#if CONFIG_IDF_TARGET_LINUX
|
||||
constexpr std::array<uint32_t, 1> INVALID_GPIOS = {24};
|
||||
#elif CONFIG_IDF_TARGET_ESP32
|
||||
constexpr std::array<uint32_t, 1> INVALID_GPIOS = {24};
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
constexpr std::array<uint32_t, 4> INVALID_GPIOS = {22, 23, 24, 25};
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
constexpr std::array<uint32_t, 4> INVALID_GPIOS = {22, 23, 24, 25};
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
constexpr std::array<uint32_t, 0> INVALID_GPIOS = {};
|
||||
#else
|
||||
#error "No GPIOs defined for the current target"
|
||||
#endif
|
||||
|
||||
gpio_num_t gpio_to_driver_type(const GPIONum &gpio_num)
|
||||
{
|
||||
return static_cast<gpio_num_t>(gpio_num.get_num());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GPIOException::GPIOException(esp_err_t error) : ESPException(error) { }
|
||||
|
||||
esp_err_t check_gpio_pin_num(uint32_t pin_num) noexcept
|
||||
{
|
||||
if (pin_num >= GPIO_NUM_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
for (auto num: INVALID_GPIOS)
|
||||
{
|
||||
if (pin_num == num) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t check_gpio_drive_strength(uint32_t strength) noexcept
|
||||
{
|
||||
if (strength >= GPIO_DRIVE_CAP_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
GPIOPullMode GPIOPullMode::FLOATING()
|
||||
{
|
||||
return GPIOPullMode(GPIO_FLOATING);
|
||||
}
|
||||
|
||||
GPIOPullMode GPIOPullMode::PULLUP()
|
||||
{
|
||||
return GPIOPullMode(GPIO_PULLUP_ONLY);
|
||||
}
|
||||
|
||||
GPIOPullMode GPIOPullMode::PULLDOWN()
|
||||
{
|
||||
return GPIOPullMode(GPIO_PULLDOWN_ONLY);
|
||||
}
|
||||
|
||||
GPIOWakeupIntrType GPIOWakeupIntrType::LOW_LEVEL()
|
||||
{
|
||||
return GPIOWakeupIntrType(GPIO_INTR_LOW_LEVEL);
|
||||
}
|
||||
|
||||
GPIOWakeupIntrType GPIOWakeupIntrType::HIGH_LEVEL()
|
||||
{
|
||||
return GPIOWakeupIntrType(GPIO_INTR_HIGH_LEVEL);
|
||||
}
|
||||
|
||||
GPIODriveStrength GPIODriveStrength::DEFAULT()
|
||||
{
|
||||
return MEDIUM();
|
||||
}
|
||||
|
||||
GPIODriveStrength GPIODriveStrength::WEAK()
|
||||
{
|
||||
return GPIODriveStrength(GPIO_DRIVE_CAP_0);
|
||||
}
|
||||
|
||||
GPIODriveStrength GPIODriveStrength::LESS_WEAK()
|
||||
{
|
||||
return GPIODriveStrength(GPIO_DRIVE_CAP_1);
|
||||
}
|
||||
|
||||
GPIODriveStrength GPIODriveStrength::MEDIUM()
|
||||
{
|
||||
return GPIODriveStrength(GPIO_DRIVE_CAP_2);
|
||||
}
|
||||
|
||||
GPIODriveStrength GPIODriveStrength::STRONGEST()
|
||||
{
|
||||
return GPIODriveStrength(GPIO_DRIVE_CAP_3);
|
||||
}
|
||||
|
||||
GPIOBase::GPIOBase(GPIONum num) : gpio_num(num)
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_reset_pin(gpio_to_driver_type(gpio_num)));
|
||||
}
|
||||
|
||||
void GPIOBase::hold_en()
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_hold_en(gpio_to_driver_type(gpio_num)));
|
||||
}
|
||||
|
||||
void GPIOBase::hold_dis()
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_hold_dis(gpio_to_driver_type(gpio_num)));
|
||||
}
|
||||
|
||||
void GPIOBase::set_drive_strength(GPIODriveStrength strength)
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_set_drive_capability(gpio_to_driver_type(gpio_num),
|
||||
static_cast<gpio_drive_cap_t>(strength.get_strength())));
|
||||
}
|
||||
|
||||
GPIO_Output::GPIO_Output(GPIONum num) : GPIOBase(num)
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_set_direction(gpio_to_driver_type(gpio_num), GPIO_MODE_OUTPUT));
|
||||
}
|
||||
|
||||
void GPIO_Output::set_high()
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_set_level(gpio_to_driver_type(gpio_num), 1));
|
||||
}
|
||||
|
||||
void GPIO_Output::set_low()
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_set_level(gpio_to_driver_type(gpio_num), 0));
|
||||
}
|
||||
|
||||
GPIODriveStrength GPIOBase::get_drive_strength()
|
||||
{
|
||||
gpio_drive_cap_t strength;
|
||||
GPIO_CHECK_THROW(gpio_get_drive_capability(gpio_to_driver_type(gpio_num), &strength));
|
||||
return GPIODriveStrength(static_cast<uint32_t>(strength));
|
||||
}
|
||||
|
||||
GPIOInput::GPIOInput(GPIONum num) : GPIOBase(num)
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_set_direction(gpio_to_driver_type(gpio_num), GPIO_MODE_INPUT));
|
||||
}
|
||||
|
||||
GPIOLevel GPIOInput::get_level() const noexcept
|
||||
{
|
||||
int level = gpio_get_level(gpio_to_driver_type(gpio_num));
|
||||
if (level) {
|
||||
return GPIOLevel::HIGH;
|
||||
} else {
|
||||
return GPIOLevel::LOW;
|
||||
}
|
||||
}
|
||||
|
||||
void GPIOInput::set_pull_mode(GPIOPullMode mode)
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_set_pull_mode(gpio_to_driver_type(gpio_num),
|
||||
static_cast<gpio_pull_mode_t>(mode.get_pull_mode())));
|
||||
}
|
||||
|
||||
void GPIOInput::wakeup_enable(GPIOWakeupIntrType interrupt_type)
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_wakeup_enable(gpio_to_driver_type(gpio_num),
|
||||
static_cast<gpio_int_type_t>(interrupt_type.get_level())));
|
||||
}
|
||||
|
||||
void GPIOInput::wakeup_disable()
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_wakeup_disable(gpio_to_driver_type(gpio_num)));
|
||||
}
|
||||
|
||||
GPIO_OpenDrain::GPIO_OpenDrain(GPIONum num) : GPIOInput(num)
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_set_direction(gpio_to_driver_type(gpio_num), GPIO_MODE_INPUT_OUTPUT_OD));
|
||||
}
|
||||
|
||||
void GPIO_OpenDrain::set_floating()
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_set_level(gpio_to_driver_type(gpio_num), 1));
|
||||
}
|
||||
|
||||
void GPIO_OpenDrain::set_low()
|
||||
{
|
||||
GPIO_CHECK_THROW(gpio_set_level(gpio_to_driver_type(gpio_num), 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,78 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "gpio_cxx.hpp"
|
||||
extern "C" {
|
||||
#include "Mockgpio.h"
|
||||
}
|
||||
|
||||
static const idf::GPIONum VALID_GPIO(18);
|
||||
|
||||
/**
|
||||
* Exception which is thrown if there is some internal cmock error which results in a
|
||||
* longjump to the location of a TEST_PROTECT() call.
|
||||
*
|
||||
* @note This is a temporary solution until there is a better integration of CATCH into CMock.
|
||||
* Note also that usually there will be a segfault when cmock fails a second time.
|
||||
* This means paying attention to the first error message is crucial for removing errors.
|
||||
*/
|
||||
class CMockException : public std::exception {
|
||||
public:
|
||||
virtual ~CMockException() { }
|
||||
|
||||
/**
|
||||
* @return A reminder to look at the actual cmock log.
|
||||
*/
|
||||
virtual const char *what() const noexcept
|
||||
{
|
||||
return "CMock encountered an error. Look at the CMock log";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper macro for setting up a test protect call for CMock.
|
||||
*
|
||||
* This macro should be used at the beginning of any test cases
|
||||
* which use generated CMock mock functions.
|
||||
* This is necessary because CMock uses longjmp which screws up C++ stacks and
|
||||
* also the CATCH mechanisms.
|
||||
*
|
||||
* @note This is a temporary solution until there is a better integration of CATCH into CMock.
|
||||
* Note also that usually there will be a segfault when cmock fails a second time.
|
||||
* This means paying attention to the first error message is crucial for removing errors.
|
||||
*/
|
||||
#define CMOCK_SETUP() \
|
||||
do { \
|
||||
if (!TEST_PROTECT()) { \
|
||||
throw CMockException(); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
struct GPIOFixture {
|
||||
GPIOFixture(idf::GPIONum gpio_num = idf::GPIONum(18), gpio_mode_t mode = GPIO_MODE_OUTPUT) : num(gpio_num)
|
||||
{
|
||||
CMOCK_SETUP();
|
||||
gpio_reset_pin_ExpectAndReturn(static_cast<gpio_num_t>(num.get_num()), ESP_OK); gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(num.get_num()), mode, ESP_OK);
|
||||
}
|
||||
|
||||
~GPIOFixture()
|
||||
{
|
||||
// Verify that all expected methods have been called.
|
||||
Mockgpio_Verify();
|
||||
}
|
||||
|
||||
idf::GPIONum num;
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(COMPONENTS main)
|
||||
|
||||
idf_build_set_property(COMPILE_DEFINITIONS "-DNO_DEBUG_STORAGE" APPEND)
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/mocks/driver/")
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component/")
|
||||
project(test_gpio_cxx_host)
|
@ -0,0 +1,8 @@
|
||||
| Supported Targets | Linux |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# Build
|
||||
`idf.py build` (sdkconfig.defaults sets the linux target by default)
|
||||
|
||||
# Run
|
||||
`build/test_gpio_cxx_host.elf`
|
@ -0,0 +1,13 @@
|
||||
idf_component_get_property(spi_flash_dir spi_flash COMPONENT_DIR)
|
||||
idf_component_get_property(cpp_component experimental_cpp_component COMPONENT_DIR)
|
||||
|
||||
idf_component_register(SRCS "gpio_cxx_test.cpp"
|
||||
"${cpp_component}/esp_exception.cpp"
|
||||
"${cpp_component}/gpio_cxx.cpp"
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
"${cpp_component}/host_test/fixtures"
|
||||
"${cpp_component}/include"
|
||||
"${cpp_component}/test" # FIXME for unity_cxx.hpp, make it generally available instead
|
||||
$ENV{IDF_PATH}/tools/catch
|
||||
REQUIRES driver cmock esp_common)
|
@ -0,0 +1,397 @@
|
||||
/* GPIO C++ unit tests
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include <stdio.h>
|
||||
#include "esp_err.h"
|
||||
#include "freertos/portmacro.h"
|
||||
#include "gpio_cxx.hpp"
|
||||
#include "test_fixtures.hpp"
|
||||
|
||||
#include "catch.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "Mockgpio.h"
|
||||
}
|
||||
|
||||
// TODO: IDF-2693, function definition just to satisfy linker, mock esp_common instead
|
||||
const char *esp_err_to_name(esp_err_t code) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
using namespace idf;
|
||||
|
||||
TEST_CASE("gpio num out of range")
|
||||
{
|
||||
CHECK_THROWS_AS(GPIONum(-1), GPIOException&);
|
||||
CHECK_THROWS_AS(GPIONum(static_cast<uint32_t>(GPIO_NUM_MAX)), GPIOException&);
|
||||
CHECK_THROWS_AS(GPIONum(24), GPIOException&); // On ESP32, 24 isn't a valid GPIO number
|
||||
}
|
||||
|
||||
TEST_CASE("gpio num operator")
|
||||
{
|
||||
GPIONum gpio_num_0(18u);
|
||||
GPIONum gpio_num_1(18u);
|
||||
GPIONum gpio_num_2(19u);
|
||||
|
||||
CHECK(gpio_num_0 == gpio_num_1);
|
||||
CHECK(gpio_num_2 != gpio_num_1);
|
||||
}
|
||||
|
||||
TEST_CASE("drive strength out of range")
|
||||
{
|
||||
CHECK_THROWS_AS(GPIODriveStrength(-1), GPIOException&);
|
||||
CHECK_THROWS_AS(GPIODriveStrength(static_cast<uint32_t>(GPIO_DRIVE_CAP_MAX)), GPIOException&);
|
||||
}
|
||||
|
||||
TEST_CASE("drive strength as expected")
|
||||
{
|
||||
CHECK(GPIODriveStrength::DEFAULT().get_strength() == GPIO_DRIVE_CAP_2);
|
||||
CHECK(GPIODriveStrength::WEAK().get_strength() == GPIO_DRIVE_CAP_0);
|
||||
CHECK(GPIODriveStrength::LESS_WEAK().get_strength() == GPIO_DRIVE_CAP_1);
|
||||
CHECK(GPIODriveStrength::MEDIUM().get_strength() == GPIO_DRIVE_CAP_2);
|
||||
CHECK(GPIODriveStrength::STRONGEST().get_strength() == GPIO_DRIVE_CAP_3);
|
||||
}
|
||||
|
||||
TEST_CASE("pull mode create functions work as expected")
|
||||
{
|
||||
CHECK(GPIOPullMode::FLOATING().get_pull_mode() == 3);
|
||||
CHECK(GPIOPullMode::PULLUP().get_pull_mode() == 0);
|
||||
CHECK(GPIOPullMode::PULLDOWN().get_pull_mode() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("GPIOIntrType create functions work as expected")
|
||||
{
|
||||
CHECK(GPIOWakeupIntrType::LOW_LEVEL().get_level() == GPIO_INTR_LOW_LEVEL);
|
||||
CHECK(GPIOWakeupIntrType::HIGH_LEVEL().get_level() == GPIO_INTR_HIGH_LEVEL);
|
||||
}
|
||||
|
||||
TEST_CASE("output resetting pin fails")
|
||||
{
|
||||
CMOCK_SETUP();
|
||||
gpio_reset_pin_ExpectAnyArgsAndReturn(ESP_FAIL);
|
||||
|
||||
CHECK_THROWS_AS(GPIO_Output gpio(VALID_GPIO), GPIOException&);
|
||||
|
||||
Mockgpio_Verify();
|
||||
}
|
||||
|
||||
TEST_CASE("output setting direction fails")
|
||||
{
|
||||
CMOCK_SETUP();
|
||||
gpio_reset_pin_ExpectAnyArgsAndReturn(ESP_OK);
|
||||
gpio_set_direction_ExpectAnyArgsAndReturn(ESP_FAIL);
|
||||
|
||||
CHECK_THROWS_AS(GPIO_Output gpio(VALID_GPIO), GPIOException&);
|
||||
|
||||
Mockgpio_Verify();
|
||||
}
|
||||
|
||||
TEST_CASE("output constructor sets correct arguments")
|
||||
{
|
||||
CMOCK_SETUP();
|
||||
gpio_reset_pin_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()), ESP_OK);
|
||||
gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()), GPIO_MODE_OUTPUT, ESP_OK);
|
||||
|
||||
GPIO_Output gpio(VALID_GPIO);
|
||||
|
||||
Mockgpio_Verify();
|
||||
}
|
||||
|
||||
TEST_CASE("output set high fails")
|
||||
{
|
||||
GPIOFixture fix;
|
||||
gpio_set_level_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), 1, ESP_FAIL);
|
||||
|
||||
GPIO_Output gpio(fix.num);
|
||||
|
||||
CHECK_THROWS_AS(gpio.set_high(), GPIOException&);
|
||||
}
|
||||
|
||||
TEST_CASE("output set high success")
|
||||
{
|
||||
GPIOFixture fix;
|
||||
gpio_set_level_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), 1, ESP_OK);
|
||||
|
||||
GPIO_Output gpio(fix.num);
|
||||
|
||||
gpio.set_high();
|
||||
}
|
||||
|
||||
TEST_CASE("output set low fails")
|
||||
{
|
||||
GPIOFixture fix;
|
||||
gpio_set_level_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), 0, ESP_FAIL);
|
||||
|
||||
GPIO_Output gpio(fix.num);
|
||||
|
||||
CHECK_THROWS_AS(gpio.set_low(), GPIOException&);
|
||||
}
|
||||
|
||||
TEST_CASE("output set low success")
|
||||
{
|
||||
GPIOFixture fix;
|
||||
gpio_set_level_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), 0, ESP_OK);
|
||||
|
||||
GPIO_Output gpio(fix.num);
|
||||
|
||||
gpio.set_low();
|
||||
}
|
||||
|
||||
TEST_CASE("output set drive strength")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO);
|
||||
gpio_set_drive_capability_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), GPIO_DRIVE_CAP_0, ESP_OK);
|
||||
|
||||
GPIO_Output gpio(fix.num);
|
||||
|
||||
gpio.set_drive_strength(GPIODriveStrength::WEAK());
|
||||
}
|
||||
|
||||
TEST_CASE("output get drive strength")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO);
|
||||
gpio_drive_cap_t drive_strength = GPIO_DRIVE_CAP_3;
|
||||
gpio_get_drive_capability_ExpectAnyArgsAndReturn(ESP_OK);
|
||||
gpio_get_drive_capability_ReturnThruPtr_strength(&drive_strength);
|
||||
|
||||
GPIO_Output gpio(fix.num);
|
||||
|
||||
CHECK(gpio.get_drive_strength() == GPIODriveStrength::STRONGEST());
|
||||
}
|
||||
|
||||
TEST_CASE("GPIOInput setting direction fails")
|
||||
{
|
||||
CMOCK_SETUP();
|
||||
gpio_reset_pin_ExpectAnyArgsAndReturn(ESP_OK);
|
||||
gpio_set_direction_ExpectAnyArgsAndReturn(ESP_FAIL);
|
||||
|
||||
CHECK_THROWS_AS(GPIOInput gpio(VALID_GPIO), GPIOException&);
|
||||
|
||||
Mockgpio_Verify();
|
||||
}
|
||||
|
||||
TEST_CASE("constructor sets correct arguments")
|
||||
{
|
||||
CMOCK_SETUP();
|
||||
gpio_reset_pin_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()), ESP_OK);
|
||||
gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()), GPIO_MODE_INPUT, ESP_OK);
|
||||
|
||||
GPIOInput gpio(VALID_GPIO);
|
||||
|
||||
Mockgpio_Verify();
|
||||
}
|
||||
|
||||
TEST_CASE("get level low")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_get_level_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), 0);
|
||||
|
||||
GPIOInput gpio(fix.num);
|
||||
|
||||
CHECK(gpio.get_level() == GPIOLevel::LOW);
|
||||
}
|
||||
|
||||
TEST_CASE("get level high")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_get_level_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), 1);
|
||||
|
||||
GPIOInput gpio(fix.num);
|
||||
|
||||
CHECK(gpio.get_level() == GPIOLevel::HIGH);
|
||||
}
|
||||
|
||||
TEST_CASE("set pull mode fails")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), GPIO_FLOATING, ESP_FAIL);
|
||||
|
||||
GPIOInput gpio(fix.num);
|
||||
|
||||
CHECK_THROWS_AS(gpio.set_pull_mode(GPIOPullMode::FLOATING()), GPIOException&);
|
||||
}
|
||||
|
||||
TEST_CASE("GPIOInput set pull mode floating")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), GPIO_FLOATING, ESP_OK);
|
||||
|
||||
GPIOInput gpio(fix.num);
|
||||
|
||||
gpio.set_pull_mode(GPIOPullMode::FLOATING());
|
||||
}
|
||||
|
||||
TEST_CASE("GPIOInput set pull mode pullup")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), GPIO_PULLUP_ONLY, ESP_OK);
|
||||
|
||||
GPIOInput gpio(fix.num);
|
||||
|
||||
gpio.set_pull_mode(GPIOPullMode::PULLUP());
|
||||
}
|
||||
|
||||
TEST_CASE("GPIOInput set pull mode pulldown")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), GPIO_PULLDOWN_ONLY, ESP_OK);
|
||||
|
||||
GPIOInput gpio(fix.num);
|
||||
|
||||
gpio.set_pull_mode(GPIOPullMode::PULLDOWN());
|
||||
}
|
||||
|
||||
TEST_CASE("GPIOInput wake up enable fails")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_wakeup_enable_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), GPIO_INTR_LOW_LEVEL, ESP_FAIL);
|
||||
|
||||
GPIOInput gpio(fix.num);
|
||||
|
||||
CHECK_THROWS_AS(gpio.wakeup_enable(GPIOWakeupIntrType::LOW_LEVEL()), GPIOException&);
|
||||
}
|
||||
|
||||
TEST_CASE("GPIOInput wake up enable high int")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_wakeup_enable_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), GPIO_INTR_HIGH_LEVEL, ESP_OK);
|
||||
|
||||
GPIOInput gpio(fix.num);
|
||||
|
||||
gpio.wakeup_enable(GPIOWakeupIntrType::HIGH_LEVEL());
|
||||
}
|
||||
|
||||
TEST_CASE("GPIOInput wake up disable fails")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_wakeup_disable_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), ESP_FAIL);
|
||||
|
||||
GPIOInput gpio(fix.num);
|
||||
|
||||
CHECK_THROWS_AS(gpio.wakeup_disable(), GPIOException&);
|
||||
}
|
||||
|
||||
TEST_CASE("GPIOInput wake up disable high int")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_wakeup_disable_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), ESP_OK);
|
||||
|
||||
GPIOInput gpio(fix.num);
|
||||
|
||||
gpio.wakeup_disable();
|
||||
}
|
||||
|
||||
TEST_CASE("GPIO_OpenDrain setting direction fails")
|
||||
{
|
||||
CMOCK_SETUP();
|
||||
gpio_reset_pin_ExpectAnyArgsAndReturn(ESP_OK);
|
||||
gpio_set_direction_ExpectAnyArgsAndReturn(ESP_FAIL);
|
||||
|
||||
CHECK_THROWS_AS(GPIO_OpenDrain gpio(VALID_GPIO), GPIOException&);
|
||||
|
||||
Mockgpio_Verify();
|
||||
}
|
||||
|
||||
TEST_CASE("GPIO_OpenDrain constructor sets correct arguments")
|
||||
{
|
||||
CMOCK_SETUP();
|
||||
gpio_reset_pin_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()), ESP_OK);
|
||||
gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()),
|
||||
GPIO_MODE_INPUT,
|
||||
ESP_OK);
|
||||
gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()),
|
||||
GPIO_MODE_INPUT_OUTPUT_OD,
|
||||
ESP_OK);
|
||||
|
||||
GPIO_OpenDrain gpio(VALID_GPIO);
|
||||
|
||||
Mockgpio_Verify();
|
||||
}
|
||||
|
||||
TEST_CASE("GPIO_OpenDrain set floating fails")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()),
|
||||
GPIO_MODE_INPUT_OUTPUT_OD,
|
||||
ESP_OK);
|
||||
gpio_set_level_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), 1, ESP_FAIL);
|
||||
|
||||
GPIO_OpenDrain gpio(fix.num);
|
||||
|
||||
CHECK_THROWS_AS(gpio.set_floating(), GPIOException&);
|
||||
}
|
||||
|
||||
TEST_CASE("GPIO_OpenDrain set floating success")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()),
|
||||
GPIO_MODE_INPUT_OUTPUT_OD,
|
||||
ESP_OK);
|
||||
gpio_set_level_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), 1, ESP_OK);
|
||||
|
||||
GPIO_OpenDrain gpio(fix.num);
|
||||
|
||||
gpio.set_floating();
|
||||
}
|
||||
|
||||
TEST_CASE("GPIO_OpenDrain set low fails")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()),
|
||||
GPIO_MODE_INPUT_OUTPUT_OD,
|
||||
ESP_OK);
|
||||
gpio_set_level_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), 0, ESP_FAIL);
|
||||
|
||||
GPIO_OpenDrain gpio(fix.num);
|
||||
|
||||
CHECK_THROWS_AS(gpio.set_low(), GPIOException&);
|
||||
}
|
||||
|
||||
TEST_CASE("GPIO_OpenDrain set low success")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()),
|
||||
GPIO_MODE_INPUT_OUTPUT_OD,
|
||||
ESP_OK);
|
||||
gpio_set_level_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), 0, ESP_OK);
|
||||
|
||||
GPIO_OpenDrain gpio(fix.num);
|
||||
|
||||
gpio.set_low();
|
||||
}
|
||||
|
||||
TEST_CASE("GPIO_OpenDrain set drive strength")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()),
|
||||
GPIO_MODE_INPUT_OUTPUT_OD,
|
||||
ESP_OK);
|
||||
|
||||
gpio_set_drive_capability_ExpectAndReturn(static_cast<gpio_num_t>(fix.num.get_num()), GPIO_DRIVE_CAP_0, ESP_OK);
|
||||
GPIO_OpenDrain gpio(fix.num);
|
||||
|
||||
gpio.set_drive_strength(GPIODriveStrength::WEAK());
|
||||
}
|
||||
|
||||
TEST_CASE("GPIO_OpenDrain get drive strength")
|
||||
{
|
||||
GPIOFixture fix(VALID_GPIO, GPIO_MODE_INPUT);
|
||||
gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(VALID_GPIO.get_num()),
|
||||
GPIO_MODE_INPUT_OUTPUT_OD,
|
||||
ESP_OK);
|
||||
gpio_drive_cap_t drive_strength = GPIO_DRIVE_CAP_3;
|
||||
gpio_get_drive_capability_ExpectAnyArgsAndReturn(ESP_OK);
|
||||
gpio_get_drive_capability_ReturnThruPtr_strength(&drive_strength);
|
||||
|
||||
GPIO_OpenDrain gpio(fix.num);
|
||||
|
||||
CHECK(gpio.get_drive_strength() == GPIODriveStrength::STRONGEST());
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_CXX_EXCEPTIONS=y
|
@ -0,0 +1,402 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if __cpp_exceptions
|
||||
|
||||
#include "esp_exception.hpp"
|
||||
#include "system_cxx.hpp"
|
||||
|
||||
namespace idf {
|
||||
|
||||
/**
|
||||
* @brief Exception thrown for errors in the GPIO C++ API.
|
||||
*/
|
||||
struct GPIOException : public ESPException {
|
||||
/**
|
||||
* @param error The IDF error representing the error class of the error to throw.
|
||||
*/
|
||||
GPIOException(esp_err_t error);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the numeric pin number is valid on the current hardware.
|
||||
*/
|
||||
esp_err_t check_gpio_pin_num(uint32_t pin_num) noexcept;
|
||||
|
||||
/**
|
||||
* Check if the numeric value of a drive strength is valid on the current hardware.
|
||||
*/
|
||||
esp_err_t check_gpio_drive_strength(uint32_t strength) noexcept;
|
||||
|
||||
/**
|
||||
* This is a "Strong Value Type" class for GPIO. The GPIO pin number is checked during construction according to
|
||||
* the hardware capabilities. This means that any GPIONumBase object is guaranteed to contain a valid GPIO number.
|
||||
* See also the template class \c StrongValue.
|
||||
*/
|
||||
template<typename GPIONumFinalType>
|
||||
class GPIONumBase final : public StrongValueComparable<uint32_t> {
|
||||
public:
|
||||
/**
|
||||
* @brief Create a numerical pin number representation and make sure it's correct.
|
||||
*
|
||||
* @throw GPIOException if the number does not reflect a valid GPIO number on the current hardware.
|
||||
*/
|
||||
GPIONumBase(uint32_t pin) : StrongValueComparable<uint32_t>(pin)
|
||||
{
|
||||
esp_err_t pin_check_result = check_gpio_pin_num(pin);
|
||||
if (pin_check_result != ESP_OK) {
|
||||
throw GPIOException(pin_check_result);
|
||||
}
|
||||
}
|
||||
|
||||
using StrongValueComparable<uint32_t>::operator==;
|
||||
using StrongValueComparable<uint32_t>::operator!=;
|
||||
|
||||
/**
|
||||
* Retrieves the valid numerical representation of the GPIO number.
|
||||
*/
|
||||
uint32_t get_num() const { return get_value(); };
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a TAG type whose sole purpose is to create a distinct type from GPIONumBase.
|
||||
*/
|
||||
class GPIONumType;
|
||||
|
||||
/**
|
||||
* A GPIO number type used for general GPIOs, in contrast to specific GPIO pins like e.g. SPI_SCLK.
|
||||
*/
|
||||
using GPIONum = GPIONumBase<class GPIONumType>;
|
||||
|
||||
/**
|
||||
* Level of an input GPIO.
|
||||
*/
|
||||
enum class GPIOLevel {
|
||||
HIGH,
|
||||
LOW
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a valid pull up configuration for GPIOs.
|
||||
* It is supposed to resemble an enum type, hence it has static creation methods and a private constructor.
|
||||
* This class is a "Strong Value Type", see also the template class \c StrongValue for more properties.
|
||||
*/
|
||||
class GPIOPullMode final : public StrongValueComparable<uint32_t> {
|
||||
private:
|
||||
/**
|
||||
* Constructor is private since it should only be accessed by the static creation methods.
|
||||
*
|
||||
* @param pull_mode A valid numerical respresentation of the pull up configuration. Must be valid!
|
||||
*/
|
||||
GPIOPullMode(uint32_t pull_mode) : StrongValueComparable<uint32_t>(pull_mode) { }
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a representation of a floating pin configuration.
|
||||
* For more information, check the driver and HAL files.
|
||||
*/
|
||||
static GPIOPullMode FLOATING();
|
||||
|
||||
/**
|
||||
* Create a representation of a pullup configuration.
|
||||
* For more information, check the driver and HAL files.
|
||||
*/
|
||||
static GPIOPullMode PULLUP();
|
||||
|
||||
/**
|
||||
* Create a representation of a pulldown configuration.
|
||||
* For more information, check the driver and HAL files.
|
||||
*/
|
||||
static GPIOPullMode PULLDOWN();
|
||||
|
||||
using StrongValueComparable<uint32_t>::operator==;
|
||||
using StrongValueComparable<uint32_t>::operator!=;
|
||||
|
||||
/**
|
||||
* Retrieves the valid numerical representation of the pull mode.
|
||||
*/
|
||||
uint32_t get_pull_mode() const { return get_value(); };
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a valid wakup interrupt type for GPIO inputs.
|
||||
*
|
||||
* This class is a "Strong Value Type", see also the template class \c StrongValue for more properties.
|
||||
* It is supposed to resemble an enum type, hence it has static creation methods and a private constructor.
|
||||
* For a detailed mapping of interrupt types to numeric values, please refer to the driver types and implementation.
|
||||
*/
|
||||
class GPIOWakeupIntrType final: public StrongValueComparable<uint32_t> {
|
||||
private:
|
||||
/**
|
||||
* Constructor is private since it should only be accessed by the static creation methods.
|
||||
*
|
||||
* @param pull_mode A valid numerical respresentation of a possible interrupt level to wake up. Must be valid!
|
||||
*/
|
||||
GPIOWakeupIntrType(uint32_t interrupt_level) : StrongValueComparable<uint32_t>(interrupt_level) { }
|
||||
|
||||
public:
|
||||
static GPIOWakeupIntrType LOW_LEVEL();
|
||||
static GPIOWakeupIntrType HIGH_LEVEL();
|
||||
|
||||
/**
|
||||
* Retrieves the valid numerical representation of the pull mode.
|
||||
*/
|
||||
uint32_t get_level() const noexcept { return get_value(); };
|
||||
};
|
||||
|
||||
/**
|
||||
* Class representing a valid drive strength for GPIO outputs.
|
||||
* This class is a "Strong Value Type", see also the template class \c StrongValue for more properties.
|
||||
* For a detailed mapping for values to drive strengths, please refer to the datasheet of the chip you are using.
|
||||
* E.g. for ESP32, the values in general are the following:
|
||||
* - WEAK: 5mA
|
||||
* - STRONGER: 10mA
|
||||
* - DEFAULT/MEDIUM: 20mA
|
||||
* - STRONGEST: 40mA
|
||||
*/
|
||||
class GPIODriveStrength final : public StrongValueComparable<uint32_t> {
|
||||
public:
|
||||
/**
|
||||
* @brief Create a drive strength representation and checks its validity.
|
||||
*
|
||||
* After construction, this class should have a guaranteed valid strength representation.
|
||||
*
|
||||
* @param strength the numeric value mapping for a particular strength. For possible ranges, look at the
|
||||
* static creation functions below.
|
||||
* @throws GPIOException if the supplied number is out of the hardware capable range.
|
||||
*/
|
||||
GPIODriveStrength(uint32_t strength) : StrongValueComparable<uint32_t>(strength)
|
||||
{
|
||||
esp_err_t strength_check_result = check_gpio_drive_strength(strength);
|
||||
if (strength_check_result != ESP_OK) {
|
||||
throw GPIOException(strength_check_result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a representation of the default drive strength.
|
||||
* For more information, check the datasheet and driver and HAL files.
|
||||
*/
|
||||
static GPIODriveStrength DEFAULT();
|
||||
|
||||
/**
|
||||
* Create a representation of the weak drive strength.
|
||||
* For more information, check the datasheet and driver and HAL files.
|
||||
*/
|
||||
static GPIODriveStrength WEAK();
|
||||
|
||||
/**
|
||||
* Create a representation of the less weak drive strength.
|
||||
* For more information, check the datasheet and driver and HAL files.
|
||||
*/
|
||||
static GPIODriveStrength LESS_WEAK();
|
||||
|
||||
/**
|
||||
* Create a representation of the medium drive strength.
|
||||
* For more information, check the datasheet and driver and HAL files.
|
||||
*/
|
||||
static GPIODriveStrength MEDIUM();
|
||||
|
||||
/**
|
||||
* Create a representation of the strong drive strength.
|
||||
*/
|
||||
static GPIODriveStrength STRONGEST();
|
||||
|
||||
using StrongValueComparable<uint32_t>::operator==;
|
||||
using StrongValueComparable<uint32_t>::operator!=;
|
||||
|
||||
/**
|
||||
* Retrieves the valid numerical representation of the drive strength.
|
||||
*/
|
||||
uint32_t get_strength() const { return get_value(); };
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implementations commonly used functionality for all GPIO configurations.
|
||||
*
|
||||
* Some functionality is only for specific configurations (set and get drive strength) but is necessary here
|
||||
* to avoid complicating the inheritance hierarchy of the GPIO classes.
|
||||
* Child classes implementing any GPIO configuration (output, input, etc.) are meant to intherit from this class
|
||||
* and possibly make some of the functionality publicly available.
|
||||
*/
|
||||
class GPIOBase {
|
||||
protected:
|
||||
/**
|
||||
* @brief Construct a GPIO.
|
||||
*
|
||||
* This constructor will only reset the GPIO but leaves the actual configuration (input, output, etc.) to
|
||||
* the sub class.
|
||||
*
|
||||
* @param num GPIO pin number of the GPIO to be configured.
|
||||
*
|
||||
* @throws GPIOException
|
||||
* - if the underlying driver function fails
|
||||
*/
|
||||
GPIOBase(GPIONum num);
|
||||
|
||||
/**
|
||||
* @brief Enable gpio pad hold function.
|
||||
*
|
||||
* The gpio pad hold function works in both input and output modes, but must be output-capable gpios.
|
||||
* If pad hold enabled:
|
||||
* in output mode: the output level of the pad will be force locked and can not be changed.
|
||||
* in input mode: the input value read will not change, regardless the changes of input signal.
|
||||
*
|
||||
* @throws GPIOException if the underlying driver function fails.
|
||||
*/
|
||||
void hold_en();
|
||||
|
||||
/**
|
||||
* @brief Disable gpio pad hold function.
|
||||
*
|
||||
* @throws GPIOException if the underlying driver function fails.
|
||||
*/
|
||||
void hold_dis();
|
||||
|
||||
/**
|
||||
* @brief Configure the drive strength of the GPIO.
|
||||
*
|
||||
* @param strength The drive strength. Refer to \c GPIODriveStrength for more details.
|
||||
*
|
||||
* @throws GPIOException if the underlying driver function fails.
|
||||
*/
|
||||
void set_drive_strength(GPIODriveStrength strength);
|
||||
|
||||
/**
|
||||
* @brief Return the current drive strength of the GPIO.
|
||||
*
|
||||
* @return The currently configured drive strength. Refer to \c GPIODriveStrength for more details.
|
||||
*
|
||||
* @throws GPIOException if the underlying driver function fails.
|
||||
*/
|
||||
GPIODriveStrength get_drive_strength();
|
||||
|
||||
/**
|
||||
* @brief The number of the configured GPIO pin.
|
||||
*/
|
||||
GPIONum gpio_num;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This class represents a GPIO which is configured as output.
|
||||
*/
|
||||
class GPIO_Output : public GPIOBase {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct and configure a GPIO as output.
|
||||
*
|
||||
* @param num GPIO pin number of the GPIO to be configured.
|
||||
*
|
||||
* @throws GPIOException
|
||||
* - if the underlying driver function fails
|
||||
*/
|
||||
GPIO_Output(GPIONum num);
|
||||
|
||||
/**
|
||||
* @brief Set GPIO to high level.
|
||||
*
|
||||
* @throws GPIOException if the underlying driver function fails.
|
||||
*/
|
||||
void set_high();
|
||||
|
||||
/**
|
||||
* @brief Set GPIO to low level.
|
||||
*
|
||||
* @throws GPIOException if the underlying driver function fails.
|
||||
*/
|
||||
void set_low();
|
||||
|
||||
using GPIOBase::set_drive_strength;
|
||||
using GPIOBase::get_drive_strength;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This class represents a GPIO which is configured as input.
|
||||
*/
|
||||
class GPIOInput : public GPIOBase {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct and configure a GPIO as input.
|
||||
*
|
||||
* @param num GPIO pin number of the GPIO to be configured.
|
||||
*
|
||||
* @throws GPIOException
|
||||
* - if the underlying driver function fails
|
||||
*/
|
||||
GPIOInput(GPIONum num);
|
||||
|
||||
/**
|
||||
* @brief Read the current level of the GPIO.
|
||||
*
|
||||
* @return The GPIO current level of the GPIO.
|
||||
*/
|
||||
GPIOLevel get_level() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Configure the internal pull-up and pull-down restors.
|
||||
*
|
||||
* @param mode The pull-up/pull-down configuration see \c GPIOPullMode.
|
||||
*
|
||||
* @throws GPIOException if the underlying driver function fails.
|
||||
*/
|
||||
void set_pull_mode(GPIOPullMode mode);
|
||||
|
||||
/**
|
||||
* @brief Configure the pin as wake up pin.
|
||||
*
|
||||
* @throws GPIOException if the underlying driver function fails.
|
||||
*/
|
||||
void wakeup_enable(GPIOWakeupIntrType interrupt_type);
|
||||
|
||||
/**
|
||||
* @brief Disable wake up functionality for this pin if it was enabled before.
|
||||
*
|
||||
* @throws GPIOException if the underlying driver function fails.
|
||||
*/
|
||||
void wakeup_disable();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This class represents a GPIO which is configured as open drain output and input at the same time.
|
||||
*
|
||||
* This class facilitates bit-banging for single wire protocols.
|
||||
*/
|
||||
class GPIO_OpenDrain : public GPIOInput {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct and configure a GPIO as open drain output as well as input.
|
||||
*
|
||||
* @param num GPIO pin number of the GPIO to be configured.
|
||||
*
|
||||
* @throws GPIOException
|
||||
* - if the underlying driver function fails
|
||||
*/
|
||||
GPIO_OpenDrain(GPIONum num);
|
||||
|
||||
/**
|
||||
* @brief Set GPIO to floating level.
|
||||
*
|
||||
* @throws GPIOException if the underlying driver function fails.
|
||||
*/
|
||||
void set_floating();
|
||||
|
||||
/**
|
||||
* @brief Set GPIO to low level.
|
||||
*
|
||||
* @throws GPIOException if the underlying driver function fails.
|
||||
*/
|
||||
void set_low();
|
||||
|
||||
using GPIOBase::set_drive_strength;
|
||||
using GPIOBase::get_drive_strength;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __cpp_exceptions
|
||||
#error system C++ classes only usable when C++ exceptions enabled. Enable CONFIG_COMPILER_CXX_EXCEPTIONS in Kconfig
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This is a "Strong Value Type" base class for types in IDF C++ classes.
|
||||
* The idea is that subclasses completely check the contained value during construction.
|
||||
* After that, it's trapped and encapsulated inside and cannot be changed anymore.
|
||||
* Consequently, the API functions receiving a correctly implemented sub class as parameter
|
||||
* don't need to check it anymore. Only at API boundaries the valid value will be retrieved
|
||||
* with get_value().
|
||||
*/
|
||||
template<typename ValueT>
|
||||
class StrongValue {
|
||||
protected:
|
||||
StrongValue(ValueT value_arg) : value(value_arg) { }
|
||||
|
||||
ValueT get_value() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
ValueT value;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class adds comparison properties to StrongValue, but no sorting properties.
|
||||
*/
|
||||
template<typename ValueT>
|
||||
class StrongValueComparable : public StrongValue<ValueT> {
|
||||
protected:
|
||||
StrongValueComparable(ValueT value_arg) : StrongValue<ValueT>(value_arg) { }
|
||||
|
||||
using StrongValue<ValueT>::get_value;
|
||||
|
||||
bool operator==(const StrongValueComparable<ValueT> &other_gpio) const
|
||||
{
|
||||
return get_value() == other_gpio.get_value();
|
||||
}
|
||||
|
||||
bool operator!=(const StrongValueComparable<ValueT> &other_gpio) const
|
||||
{
|
||||
return get_value() != other_gpio.get_value();
|
||||
}
|
||||
};
|
@ -4,3 +4,4 @@ cxx/experimental/experimental_cpp_component/
|
||||
main/
|
||||
build_system/cmake/
|
||||
mb_example_common/
|
||||
examples/cxx/experimental/blink_cxx
|
||||
|
@ -1,3 +1,4 @@
|
||||
build_system/cmake
|
||||
temp_
|
||||
examples/bluetooth/bluedroid/ble_50/
|
||||
examples/cxx/experimental/blink_cxx
|
||||
|
Loading…
Reference in New Issue
Block a user