[cxx]: simple spi master class
* spi cxx unit test (CATCH-based, on host) * added portmacro.h to driver mocking * added simple testing app to write/read SPI, using an MPU9250
This commit is contained in:
@@ -1,16 +1,8 @@
|
||||
// Copyright 2019 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.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -65,7 +57,7 @@ public:
|
||||
*/
|
||||
#define CHECK_THROW_SPECIFIC(error_, exception_type_) \
|
||||
do { \
|
||||
esp_err_t result = error_; \
|
||||
esp_err_t result = (error_); \
|
||||
if (result != ESP_OK) throw idf::exception_type_(result); \
|
||||
} while (0)
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if __cpp_exceptions
|
||||
|
||||
#include "esp_exception.hpp"
|
||||
#include "gpio_cxx.hpp"
|
||||
#include "system_cxx.hpp"
|
||||
|
||||
namespace idf {
|
||||
|
||||
/**
|
||||
* @brief Exception which is thrown in the context of SPI C++ classes.
|
||||
*/
|
||||
struct SPIException : public ESPException {
|
||||
SPIException(esp_err_t error);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The maximum SPI transfer size in bytes.
|
||||
*/
|
||||
class SPITransferSize : public StrongValueOrdered<size_t> {
|
||||
public:
|
||||
/**
|
||||
* @brief Create a valid SPI transfer size.
|
||||
*
|
||||
* @param transfer_size The raw transfer size in bytes.
|
||||
*/
|
||||
explicit SPITransferSize(size_t transfer_size) noexcept : StrongValueOrdered<size_t>(transfer_size) { }
|
||||
|
||||
static SPITransferSize default_size() {
|
||||
return SPITransferSize(0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Check if the raw uint32_t spi number is in the range according to the hardware.
|
||||
*/
|
||||
esp_err_t check_spi_num(uint32_t spi_num) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Represents a valid SPI host number.
|
||||
*
|
||||
* ESP chips may have different independent SPI peripherals. This SPI number distinguishes between them.
|
||||
*/
|
||||
class SPINum : public StrongValueComparable<uint32_t> {
|
||||
public:
|
||||
/**
|
||||
* @brief Create a valid SPI host number.
|
||||
*
|
||||
* @param host_id_raw The raw SPI host number.
|
||||
*
|
||||
* @throw SPIException if the passed SPI host number is incorrect.
|
||||
*/
|
||||
SPINum(uint32_t host_id_raw) : StrongValueComparable<uint32_t>(host_id_raw)
|
||||
{
|
||||
esp_err_t spi_num_check_result = check_spi_num(host_id_raw);
|
||||
if (spi_num_check_result != ESP_OK) {
|
||||
throw SPIException(spi_num_check_result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the raw value of the SPI host.
|
||||
*
|
||||
* This should only be used when calling driver and other interfaces which don't support the C++ class.
|
||||
*
|
||||
* @return the raw value of the SPI host.
|
||||
*/
|
||||
uint32_t get_spi_num() const
|
||||
{
|
||||
return get_value();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a valid MOSI signal pin number.
|
||||
*/
|
||||
class MOSI_type;
|
||||
using MOSI = GPIONumBase<class MOSI_type>;
|
||||
|
||||
/**
|
||||
* @brief Represents a valid MISO signal pin number.
|
||||
*/
|
||||
class MISO_type;
|
||||
using MISO = GPIONumBase<class MISO_type>;
|
||||
|
||||
/**
|
||||
* @brief Represents a valid SCLK signal pin number.
|
||||
*/
|
||||
class SCLK_type;
|
||||
using SCLK = GPIONumBase<class SCLK_type>;
|
||||
|
||||
/**
|
||||
* @brief Represents a valid CS (chip select) signal pin number.
|
||||
*/
|
||||
class CS_type;
|
||||
using CS = GPIONumBase<class CS_type>;
|
||||
|
||||
/**
|
||||
* @brief Represents a valid QSPIWP signal pin number.
|
||||
*/
|
||||
class QSPIWP_type;
|
||||
using QSPIWP = GPIONumBase<class QSPIWP_type>;
|
||||
|
||||
/**
|
||||
* @brief Represents a valid QSPIHD signal pin number.
|
||||
*/
|
||||
class QSPIHD_type;
|
||||
using QSPIHD = GPIONumBase<class QSPIHD_type>;
|
||||
|
||||
/**
|
||||
* @brief Represents a valid SPI DMA configuration. Use it similar to an enum.
|
||||
*/
|
||||
class SPI_DMAConfig : public StrongValueComparable<uint32_t> {
|
||||
/**
|
||||
* Constructor is hidden to enforce object invariants.
|
||||
* Use the static creation methods to create instances.
|
||||
*/
|
||||
explicit SPI_DMAConfig(uint32_t channel_num) : StrongValueComparable<uint32_t>(channel_num) { }
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Create a configuration with DMA disabled.
|
||||
*/
|
||||
static SPI_DMAConfig DISABLED();
|
||||
|
||||
/**
|
||||
* @brief Create a configuration where the driver allocates DMA.
|
||||
*/
|
||||
static SPI_DMAConfig AUTO();
|
||||
|
||||
/**
|
||||
* @brief Return the raw value of the DMA configuration.
|
||||
*
|
||||
* This should only be used when calling driver and other interfaces which don't support the C++ class.
|
||||
*
|
||||
* @return the raw value of the DMA configuration.
|
||||
*/
|
||||
uint32_t get_num() const {
|
||||
return get_value();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if __cpp_exceptions
|
||||
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <future>
|
||||
|
||||
#include "system_cxx.hpp"
|
||||
#include "spi_cxx.hpp"
|
||||
|
||||
namespace idf {
|
||||
|
||||
/**
|
||||
* @brief Exception which is thrown in the context of SPI Transactions.
|
||||
*/
|
||||
struct SPITransferException : public SPIException {
|
||||
SPITransferException(esp_err_t error);
|
||||
};
|
||||
|
||||
class SPIDevice;
|
||||
class SPIDeviceHandle;
|
||||
|
||||
/**
|
||||
* @brief Describes and encapsulates the transaction.
|
||||
*
|
||||
* @note This class is intended to be used internally by the SPI C++ classes, but not publicly.
|
||||
* Furthermore, currently only one transaction per time can be handled. If you need to
|
||||
* send several transactions in parallel, you need to build your own mechanism around a
|
||||
* FreeRTOS task and a queue.
|
||||
*/
|
||||
class SPITransactionDescriptor {
|
||||
friend class SPIDeviceHandle;
|
||||
public:
|
||||
/**
|
||||
* @brief Create a SPITransactionDescriptor object, describing a full duplex transaction.
|
||||
*
|
||||
* @param data_to_send The data sent to the SPI device. It can be dummy data if a read-only
|
||||
* transaction is intended. Its length determines the length of both write and read operation.
|
||||
* @param handle to the internal driver handle
|
||||
* @param pre_callback If non-empty, this callback will be called directly before the transaction.
|
||||
* @param post_callback If non-empty, this callback will be called directly after the transaction.
|
||||
* @param user_data optional data which will be accessible in the callbacks declared above
|
||||
*/
|
||||
SPITransactionDescriptor(const std::vector<uint8_t> &data_to_send,
|
||||
SPIDeviceHandle *handle,
|
||||
std::function<void(void *)> pre_callback = nullptr,
|
||||
std::function<void(void *)> post_callback = nullptr,
|
||||
void* user_data = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize and delete all data of the transaction.
|
||||
*
|
||||
* @note This destructor must not becalled before the transaction is finished by the driver.
|
||||
*/
|
||||
~SPITransactionDescriptor();
|
||||
|
||||
SPITransactionDescriptor(const SPITransactionDescriptor&) = delete;
|
||||
SPITransactionDescriptor operator=(const SPITransactionDescriptor&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Queue the transaction asynchronously.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* @brief Synchronously (blocking) wait for the result and return the result data or throw an exception.
|
||||
*
|
||||
* @return The data read from the SPI device. Its length is the length of \c data_to_send passed in the
|
||||
* constructor.
|
||||
* @throws SPIException in case of an error of the underlying driver or if the driver returns a wrong
|
||||
* transaction descriptor for some reason. In the former case, the error code is the one from the
|
||||
* underlying driver, in the latter case, the error code is ESP_ERR_INVALID_STATE.
|
||||
*/
|
||||
std::vector<uint8_t> get();
|
||||
|
||||
/**
|
||||
* @brief Wait until the asynchronous operation is done.
|
||||
*
|
||||
* @throws SPIException in case of an error of the underlying driver or if the driver returns a wrong
|
||||
* transaction descriptor for some reason. In the former case, the error code is the one from the
|
||||
* underlying driver, in the latter case, the error code is ESP_ERR_INVALID_STATE.
|
||||
*/
|
||||
void wait();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Wait for a result of the transaction up to timeout ms.
|
||||
*
|
||||
* @param timeout Maximum timeout value for waiting
|
||||
*
|
||||
* @return true if result is available, false if wait timed out
|
||||
*
|
||||
* @throws SPIException in case of an error of the underlying driver or if the driver returns a wrong
|
||||
* transaction descriptor for some reason. In the former case, the error code is the one from the
|
||||
* underlying driver, in the latter case, the error code is ESP_ERR_INVALID_STATE.
|
||||
*/
|
||||
bool wait_for(const std::chrono::milliseconds &timeout);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Private descriptor data.
|
||||
*/
|
||||
void *private_transaction_desc;
|
||||
|
||||
/**
|
||||
* Private device data.
|
||||
*/
|
||||
SPIDeviceHandle *device_handle;
|
||||
|
||||
/**
|
||||
* @brief If non-empty, this callback will be called directly before the transaction.
|
||||
*/
|
||||
std::function<void(void *)> pre_callback;
|
||||
|
||||
/**
|
||||
* @brief If non-empty, this callback will be called directly after the transaction.
|
||||
*/
|
||||
std::function<void(void *)> post_callback;
|
||||
|
||||
/**
|
||||
* Buffer in spi_transaction_t is const, so we have to declare it here because we want to
|
||||
* allocate and delete it.
|
||||
*/
|
||||
uint8_t *tx_buffer;
|
||||
|
||||
/**
|
||||
* @brief User data which will be provided in the callbacks.
|
||||
*/
|
||||
void *user_data;
|
||||
|
||||
/**
|
||||
* Tells if data has been received, i.e. the transaction has finished and the result can be acquired.
|
||||
*/
|
||||
bool received_data;
|
||||
|
||||
/**
|
||||
* Tells if the transaction has been initiated and is at least in-flight, if not finished.
|
||||
*/
|
||||
bool started;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief SPIFuture for asynchronous transaction results, mostly equivalent to std::future.
|
||||
*
|
||||
* This re-implementation is necessary as std::future is incompatible with the IDF SPI driver interface.
|
||||
*/
|
||||
class SPIFuture {
|
||||
public:
|
||||
/**
|
||||
* @brief Create an invalid future.
|
||||
*/
|
||||
SPIFuture();
|
||||
|
||||
/**
|
||||
* @brief Create a valid future with \c transaction as shared state.
|
||||
*
|
||||
* @param transaction the shared transaction state
|
||||
*/
|
||||
SPIFuture(std::shared_ptr<SPITransactionDescriptor> transaction);
|
||||
|
||||
SPIFuture(const SPIFuture &other) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor as in std::future, leaves \c other invalid.
|
||||
*
|
||||
* @param other object to move from, will become invalid during this constructor
|
||||
*/
|
||||
SPIFuture(SPIFuture &&other) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Move assignment as in std::future, leaves \c other invalid.
|
||||
*
|
||||
* @param other object to move from, will become invalid during this constructor
|
||||
* @return A reference to the newly created SPIFuture object
|
||||
*/
|
||||
SPIFuture &operator=(SPIFuture&& other) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Wait until the asynchronous operation is done and return the result or throw and exception.
|
||||
*
|
||||
* @throws std::future_error if this future is not valid.
|
||||
* @throws SPIException in case of an error of the underlying driver or if the driver returns a wrong
|
||||
* transaction descriptor for some reason. In the former case, the error code is the one from the
|
||||
* underlying driver, in the latter case, the error code is ESP_ERR_INVALID_STATE.
|
||||
* @return The result of the asynchronous SPI transaction.
|
||||
*/
|
||||
std::vector<uint8_t> get();
|
||||
|
||||
/**
|
||||
* @brief Wait for a result up to timeout ms.
|
||||
*
|
||||
* @param timeout Maximum timeout value for waiting
|
||||
*
|
||||
* @return std::future_status::ready if result is available, std::future_status::timeout if wait timed out
|
||||
*/
|
||||
std::future_status wait_for(std::chrono::milliseconds timeout);
|
||||
|
||||
/**
|
||||
* @brief Wait for a result indefinitely.
|
||||
*/
|
||||
void wait();
|
||||
|
||||
/**
|
||||
* @return true if this future is valid, otherwise false.
|
||||
*/
|
||||
bool valid() const noexcept;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The SPITransactionDescriptor, which is the shared state of this future.
|
||||
*/
|
||||
std::shared_ptr<SPITransactionDescriptor> transaction;
|
||||
|
||||
/**
|
||||
* Indicates if this future is valid.
|
||||
*/
|
||||
bool is_valid;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents an device on an initialized Master Bus.
|
||||
*/
|
||||
class SPIDevice {
|
||||
public:
|
||||
/**
|
||||
* @brief Create and initialize a device on the master bus corresponding to spi_host.
|
||||
*
|
||||
* @param cs The pin number of the chip select signal for the device to create.
|
||||
* @param spi_host the spi_host (bus) to which the device shall be attached.
|
||||
* @param frequency The devices frequency. this frequency will be set during transactions to the device which will be
|
||||
* created.
|
||||
* @param transaction_queue_size The of the transaction queue of this device. This determines how many
|
||||
* transactions can be queued at the same time. Currently, it is set to 1 since the
|
||||
* implementation exclusively acquires the bus for each transaction. This may change in the future.
|
||||
*/
|
||||
SPIDevice(SPINum spi_host,
|
||||
CS cs,
|
||||
Frequency frequency = Frequency::MHz(1),
|
||||
QueueSize transaction_queue_size = QueueSize(1u));
|
||||
|
||||
SPIDevice(const SPIDevice&) = delete;
|
||||
SPIDevice operator=(const SPIDevice&) = delete;
|
||||
|
||||
/**
|
||||
* @brief De-initializes and destroys the device.
|
||||
*
|
||||
* @warning Behavior is undefined if a device is destroyed while there is still an ongoing transaction
|
||||
* from that device.
|
||||
*/
|
||||
~SPIDevice();
|
||||
|
||||
/**
|
||||
* @brief Queue a transfer to this device.
|
||||
*
|
||||
* This method creates a full-duplex transfer to the device represented by the current instance of this class.
|
||||
* It then queues that transfer and returns a "future" object. The future object will become ready once
|
||||
* the transfer finishes.
|
||||
*
|
||||
* @param data_to_send Data which will be sent to the device. The length of the data determines the length
|
||||
* of the full-deplex transfer. I.e., the same amount of bytes will be received from the device.
|
||||
* @param pre_callback If non-empty, this callback will be called directly before the transaction.
|
||||
* If empty, it will be ignored.
|
||||
* @param post_callback If non-empty, this callback will be called directly after the transaction.
|
||||
* If empty, it will be ignored.
|
||||
* @param user_data This pointer will be sent to pre_callback and/or pre_callback, if any of them is non-empty.
|
||||
*
|
||||
* @return a future object which will become ready once the transfer has finished. See also \c SPIFuture.
|
||||
*/
|
||||
SPIFuture transfer(const std::vector<uint8_t> &data_to_send,
|
||||
std::function<void(void *)> pre_callback = nullptr,
|
||||
std::function<void(void *)> post_callback = nullptr,
|
||||
void* user_data = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Queue a transfer to this device like \c transfer, but using begin/end iterators instead of a
|
||||
* data vector.
|
||||
*
|
||||
* This method is equivalent to \c transfer(), except for the parameters.
|
||||
*
|
||||
* @param begin Iterator to the begin of the data which will be sent to the device.
|
||||
* @param end Iterator to the end of the data which will be sent to the device.
|
||||
* This iterator determines the length of the data and hence the length of the full-deplex transfer.
|
||||
* I.e., the same amount of bytes will be received from the device.
|
||||
* @param pre_callback If non-empty, this callback will be called directly before the transaction.
|
||||
* If empty, it will be ignored.
|
||||
* @param post_callback If non-empty, this callback will be called directly after the transaction.
|
||||
* If empty, it will be ignored.
|
||||
* @param user_data This pointer will be sent to pre_callback and/or pre_callback, if any of them is non-empty.
|
||||
*
|
||||
* @return a future object which will become ready once the transfer has finished. See also \c SPIFuture.
|
||||
*/
|
||||
template<typename IteratorT>
|
||||
SPIFuture transfer(IteratorT begin,
|
||||
IteratorT end,
|
||||
std::function<void(void *)> pre_callback = nullptr,
|
||||
std::function<void(void *)> post_callback = nullptr,
|
||||
void* user_data = nullptr);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Private device data.
|
||||
*/
|
||||
SPIDeviceHandle *device_handle;
|
||||
|
||||
/**
|
||||
* Saves the current transaction descriptor in case the user's loses its future with the other
|
||||
* reference to the transaction.
|
||||
*/
|
||||
std::shared_ptr<SPITransactionDescriptor> current_transaction;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents an SPI Master Bus.
|
||||
*/
|
||||
class SPIMaster {
|
||||
public:
|
||||
/*
|
||||
* @brief Create an SPI Master Bus.
|
||||
*
|
||||
* @param host The SPI host (bus) which should be used. ESP chips have a number of different possible SPI hosts,
|
||||
* each of which will create its own bus. Consult the datasheet and TRM on which host to choose.
|
||||
* @param mosi The pin number for the MOSI signal of this bus.
|
||||
* @param miso The pin number for the MISO signal of this bus.
|
||||
* @param sclk The pin number for the clock signal of this bus.
|
||||
* @param dma_config The DMA configuration for this bus, see \c DMAConfig.
|
||||
* @param max_transfer_size The maximum transfer size in bytes.
|
||||
*
|
||||
* @throws SPIException with IDF error code if the underlying driver fails.
|
||||
*/
|
||||
explicit SPIMaster(SPINum host,
|
||||
const MOSI &mosi,
|
||||
const MISO &miso,
|
||||
const SCLK &sclk,
|
||||
SPI_DMAConfig dma_config = SPI_DMAConfig::AUTO(),
|
||||
SPITransferSize max_transfer_size = SPITransferSize::default_size());
|
||||
|
||||
/*
|
||||
* @brief Create an SPI Master Bus.
|
||||
*
|
||||
* @param host The SPI host (bus) which should be used. ESP chips have a number of different possible SPI hosts,
|
||||
* each of which will create its own bus. Consult the datasheet and TRM on which host to choose.
|
||||
* @param mosi The pin number for the MOSI signal of this bus.
|
||||
* @param miso The pin number for the MISO signal of this bus.
|
||||
* @param sclk The pin number for the clock signal of this bus.
|
||||
* @param qspiwp The pin number for the QSPIWP signal of this bus.
|
||||
* @param qspihd The pin number for the QSPIHD signal of this bus.
|
||||
* @param dma_config The DMA configuration for this bus, see \c DMAConfig.
|
||||
* @param max_transfer_size The maximum transfer size in bytes.
|
||||
*
|
||||
* @throws SPIException with IDF error code if the underlying driver fails.
|
||||
*/
|
||||
explicit SPIMaster(SPINum host,
|
||||
const MOSI &mosi,
|
||||
const MISO &miso,
|
||||
const SCLK &sclk,
|
||||
const QSPIWP &qspiwp,
|
||||
const QSPIHD &qspihd,
|
||||
SPI_DMAConfig dma_config = SPI_DMAConfig::AUTO(),
|
||||
SPITransferSize max_transfer_size = SPITransferSize::default_size());
|
||||
|
||||
SPIMaster(const SPIMaster&) = delete;
|
||||
SPIMaster operator=(const SPIMaster&) = delete;
|
||||
|
||||
SPIMaster(SPIMaster&&) = default;
|
||||
SPIMaster &operator=(SPIMaster&&) = default;
|
||||
|
||||
/*
|
||||
* @brief De-initializes and destroys the SPI Master Bus.
|
||||
*
|
||||
* @note Devices created before which try to initialize an exception after the bus is destroyed will throw
|
||||
* and exception.
|
||||
*/
|
||||
virtual ~SPIMaster();
|
||||
|
||||
/**
|
||||
* @brief Create a representation of a device on this bus.
|
||||
*
|
||||
* @param cs The pin number for the CS (chip select) signal to talk to the device.
|
||||
* @param f The frequency used to talk to the device.
|
||||
*/
|
||||
std::shared_ptr<SPIDevice> create_dev(CS cs, Frequency frequency = Frequency::MHz(1));
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Host identifier for internal use.
|
||||
*/
|
||||
SPINum spi_host;
|
||||
};
|
||||
|
||||
template<typename IteratorT>
|
||||
SPIFuture SPIDevice::transfer(IteratorT begin,
|
||||
IteratorT end,
|
||||
std::function<void(void *)> pre_callback,
|
||||
std::function<void(void *)> post_callback,
|
||||
void* user_data)
|
||||
{
|
||||
std::vector<uint8_t> write_data;
|
||||
write_data.assign(begin, end);
|
||||
return transfer(write_data, pre_callback, post_callback, user_data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -4,12 +4,19 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file contains helper classes for commonly used IDF types. The classes make the use of these types easier and
|
||||
* safer.
|
||||
* In particular, their usage provides greater type-safety of function arguments and "correctness by construction".
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifndef __cpp_exceptions
|
||||
#error system C++ classes only usable when C++ exceptions enabled. Enable CONFIG_COMPILER_CXX_EXCEPTIONS in Kconfig
|
||||
#endif
|
||||
|
||||
#include "esp_exception.hpp"
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -32,13 +39,14 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* This class adds comparison properties to StrongValue, but no sorting properties.
|
||||
* This class adds comparison properties to StrongValue, but no sorting and ordering properties.
|
||||
*/
|
||||
template<typename ValueT>
|
||||
class StrongValueComparable : public StrongValue<ValueT> {
|
||||
protected:
|
||||
StrongValueComparable(ValueT value_arg) : StrongValue<ValueT>(value_arg) { }
|
||||
|
||||
public:
|
||||
using StrongValue<ValueT>::get_value;
|
||||
|
||||
bool operator==(const StrongValueComparable<ValueT> &other_gpio) const
|
||||
@@ -51,3 +59,87 @@ protected:
|
||||
return get_value() != other_gpio.get_value();
|
||||
}
|
||||
};
|
||||
|
||||
namespace idf {
|
||||
|
||||
/**
|
||||
* This class adds ordering and sorting properties to StrongValue.
|
||||
*/
|
||||
template<typename ValueT>
|
||||
class StrongValueOrdered : public StrongValueComparable<ValueT> {
|
||||
public:
|
||||
StrongValueOrdered(ValueT value) : StrongValueComparable<ValueT>(value) { }
|
||||
|
||||
using StrongValueComparable<ValueT>::get_value;
|
||||
|
||||
bool operator>(const StrongValueOrdered<ValueT> &other) const
|
||||
{
|
||||
return get_value() > other.get_value();
|
||||
}
|
||||
|
||||
bool operator<(const StrongValueOrdered<ValueT> &other) const
|
||||
{
|
||||
return get_value() < other.get_value();
|
||||
}
|
||||
|
||||
bool operator>=(const StrongValueOrdered<ValueT> &other) const
|
||||
{
|
||||
return get_value() >= other.get_value();
|
||||
}
|
||||
|
||||
bool operator<=(const StrongValueOrdered<ValueT> &other) const
|
||||
{
|
||||
return get_value() <= other.get_value();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A general frequency class to be used whereever an unbound frequency value is necessary.
|
||||
*/
|
||||
class Frequency : public StrongValueOrdered<size_t> {
|
||||
public:
|
||||
explicit Frequency(size_t frequency) : StrongValueOrdered<size_t>(frequency)
|
||||
{
|
||||
if (frequency == 0) {
|
||||
throw ESPException(ESP_ERR_INVALID_ARG);
|
||||
}
|
||||
}
|
||||
|
||||
Frequency(const Frequency&) = default;
|
||||
Frequency &operator=(const Frequency&) = default;
|
||||
|
||||
using StrongValueOrdered<size_t>::get_value;
|
||||
|
||||
static Frequency Hz(size_t frequency)
|
||||
{
|
||||
return Frequency(frequency);
|
||||
}
|
||||
|
||||
static Frequency KHz(size_t frequency)
|
||||
{
|
||||
return Frequency(frequency * 1000);
|
||||
}
|
||||
|
||||
static Frequency MHz(size_t frequency)
|
||||
{
|
||||
return Frequency(frequency * 1000 * 1000);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Queue size mainly for operating system queues.
|
||||
*/
|
||||
class QueueSize {
|
||||
public:
|
||||
explicit QueueSize(size_t q_size) : queue_size(q_size) { }
|
||||
|
||||
size_t get_size()
|
||||
{
|
||||
return queue_size;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t queue_size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user