diff --git a/README.md b/README.md index e273c9a..2531a30 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,9 @@ Debug serial output macros. Configuration: * `#define STA_DEBUG_SERIAL_ENABLE`: Enable module -* `#define STA_DEBUG_SERIAL_DISABLE`: Forces module off when defined -* `DEBUG`: Automatically enables module when defined -* `NDEBUG`: Forces module off when defined +* `#define STA_DEBUG_SERIAL_FORCE`: Ignore debug defines and always enable output +* `DEBUG`: Enables output when defined +* `NDEBUG`: Disables output when defined (overrides DEBUG) The `sta::DebugSerial` instance must be provided. @@ -78,4 +78,37 @@ Configuration: * `#define STA_HAL_DELAY_US_TIM `: 1 MHz TIM instance used by `sta::delayUs` TIM time base must be started before using `sta::delayUs` by calling `sta::initHAL`. -When using the startup system task this is handled automatically. \ No newline at end of file +When using the startup system task this is handled automatically. + + +## Interfaces + +The intention of these interfaces is to provide an abstraction layer for commonly used +resources. Libraries using these interfaces can be reused on different platforms +by simply implementing the required interfaces for the selected platform. + +Interfaces for the following resources are provided: +* GPIO pin +* Mutex +* Signal +* SPI +* UART + + +## HAL implementations + +Implementations using the HAL are provided for the following interfaces: +* GpioPin +* SpiInterface, SpiDevice +* UART + +To enable these implementations follow the instructions from the individual headers. + + +## Atomic implementations + +Implementations using atomic variables are provided for the following interfaces: +* Mutex +* Signal + +To enable these implementations define `STA_ATOMIC_ENABLE` in ``. diff --git a/include/sta/atomic/mutex.hpp b/include/sta/atomic/mutex.hpp new file mode 100644 index 0000000..a41b89f --- /dev/null +++ b/include/sta/atomic/mutex.hpp @@ -0,0 +1,39 @@ +/** + * @brief Atomic mutex implementation. + * + * Configuration: + * STA_ATOMIC_ENABLE: Enable module + */ +#ifndef STA_ATOMIC_MUTEX_HPP +#define STA_ATOMIC_MUTEX_HPP + +#include +#ifdef STA_ATOMIC_ENABLE + +#include + +#include + + +namespace sta +{ + /** + * @brief Implementation of `Mutex` interface using `std::atomic_flag`. + */ + class AtomicMutex : public Mutex + { + public: + AtomicMutex(); + + void acquire() override; + void release() override; + + private: + std::atomic_flag lock_; /**< Atomic flag used as lock */ + }; +} // namespace sta + + +#endif // STA_ATOMIC_ENABLE + +#endif // STA_ATOMIC_MUTEX_HPP diff --git a/include/sta/atomic/signal.hpp b/include/sta/atomic/signal.hpp new file mode 100644 index 0000000..b84f030 --- /dev/null +++ b/include/sta/atomic/signal.hpp @@ -0,0 +1,41 @@ +/** + * @brief Atomic signal implementation. + * + * Configuration: + * STA_ATOMIC_ENABLE: Enable module + */ +#ifndef STA_ATOMIC_SIGNAL_HPP +#define STA_ATOMIC_SIGNAL_HPP + +#include +#ifdef STA_ATOMIC_ENABLE + +#include + +#include + + +namespace sta +{ + /** + * @brief Implementation of `Signal` interface using `std::atomic`. + */ + class AtomicSignal : public Signal + { + public: + AtomicSignal(); + + void notify() override; + bool peek() override; + bool test() override; + void wait() override; + + private: + std::atomic signal_; /**< Atomic bool used as signal */ + }; +} // namespace sta + + +#endif // STA_ATOMIC_ENABLE + +#endif // STA_ATOMIC_SIGNAL_HPP diff --git a/include/sta/gpio_pin.hpp b/include/sta/gpio_pin.hpp new file mode 100644 index 0000000..d976d4b --- /dev/null +++ b/include/sta/gpio_pin.hpp @@ -0,0 +1,35 @@ +/** + * @brief GPIO pin interface definitions. + */ +#ifndef STA_GPIO_PIN_HPP +#define STA_GPIO_PIN_HPP + + +namespace sta +{ + /** + * @brief GPIO pin state + */ + enum class GpioPinState + { + LOW, + HIGH + }; + + /** + * @brief Interface for GPIO pins. + */ + class GpioPin + { + public: + /** + * @brief Set pin output state. + * + * @param state Output state + */ + virtual void setState(GpioPinState state) = 0; + }; +} // namespace sta + + +#endif // STA_GPIO_PIN_HPP diff --git a/include/sta/hal/clocks.hpp b/include/sta/hal/clocks.hpp new file mode 100644 index 0000000..d29a23a --- /dev/null +++ b/include/sta/hal/clocks.hpp @@ -0,0 +1,60 @@ +/** + * @brief Helper macros for HAL clock queries. + */ +#ifndef STA_HAL_CLOCKS_HPP +#define STA_HAL_CLOCKS_HPP + +#include +#include + + +/** + * @brief Get function returning PCLK frequency. + * + * @param n Index of peripheral clock + */ +#define STA_HAL_GET_PCLK_FREQ_FN(n) HAL_RCC_GetPCLK ## n ## Freq + + +// Internal helper for macro expansion +#define _STA_HAL_GET_PCLK_FREQ_FN(n) STA_HAL_GET_PCLK_FREQ_FN(n) +// Get instance to PCLK index map macro +#define _STA_PCLK_IDX_MAP(type, idx) STA_ ## type ## _ ## idx ## _PCLK_IDX +// Get HAL handle to PCLK index map macro +#define _STA_HAL_PCLK_IDX_MAP(handle) STA_HAL_ ## handle ## _PCLK_IDX + + +/** + * @brief Get function returning frequency of PCLK used by TIM. + * + * @param n TIM index + */ +#define STA_HAL_GET_TIM_PCLK_FREQ_FN(n) _STA_HAL_GET_PCLK_FREQ_FN(_STA_PCLK_IDX_MAP(TIM, n)) +/** + * @brief Get function returning frequency of PCLK used by SPI interface. + * + * @param n SPI interface index + */ +#define STA_HAL_GET_SPI_PCLK_FREQ_FN(n) _STA_HAL_GET_PCLK_FREQ_FN(_STA_PCLK_IDX_MAP(SPI, n)) +/** + * @brief Get function returning frequency of PCLK used by I2C interface. + * + * @param n I2C interface index + */ +#define STA_HAL_GET_I2C_PCLK_FREQ_FN(n) _STA_HAL_GET_PCLK_FREQ_FN(_STA_PCLK_IDX_MAP(I2C, n)) +/** + * @brief Get function returning frequency of PCLK used by USART interface. + * + * @param n USART interface index + */ +#define STA_HAL_GET_USART_PCLK_FREQ_FN(n) _STA_HAL_GET_PCLK_FREQ_FN(_STA_PCLK_IDX_MAP(USART, n)) + +/** + * @brief Get function returning frequency of PCLK used by HAL instance. + * + * @param handle Instance handle + */ +#define STA_HAL_GET_HANDLE_PCLK_FREQ_FN(handle) _STA_HAL_GET_PCLK_FREQ_FN(_STA_HAL_PCLK_IDX_MAP(handle)) + + +#endif // STA_HAL_CLOCKS_HPP diff --git a/include/sta/hal/gpio_pin.hpp b/include/sta/hal/gpio_pin.hpp new file mode 100644 index 0000000..08b7f9c --- /dev/null +++ b/include/sta/hal/gpio_pin.hpp @@ -0,0 +1,49 @@ +/** + * @brief Wrapper for HAL GPIO pins. + * + * Configuration: + * STA_HAL_GPIO_ENABLE: Enable module + */ +#ifndef STA_HAL_GPIO_PIN_HPP +#define STA_HAL_GPIO_PIN_HPP + +#include +#ifdef STA_HAL_GPIO_ENABLE + +#include +#include + + +namespace sta +{ + /** + * @brief Container for HAL GPIO Pin objects. + */ + class HalGpioPin : public GpioPin + { + public: + HalGpioPin(GPIO_TypeDef * port, uint16_t pin); + + void setState(GpioPinState state) override; + + GPIO_TypeDef * getPort() const; + uint16_t getPin() const; + uint8_t getIndex() const; + + private: + GPIO_TypeDef * port_; /**< GPIO port */ + uint16_t pin_; /**< GPIO pin */ + }; +} + +/** + * @brief Create HalGpioPin object from pin label. + * + * @param label Pin label + */ +#define STA_HAL_GPIO_PIN(label) sta::HalGpioPin{label##_GPIO_Port, label##_Pin} + + +#endif // STA_HAL_GPIO_ENABLE + +#endif // STA_HAL_GPIO_PIN_HPP diff --git a/include/sta/hal/spi.hpp b/include/sta/hal/spi.hpp new file mode 100644 index 0000000..b992513 --- /dev/null +++ b/include/sta/hal/spi.hpp @@ -0,0 +1,108 @@ +/** + * @brief Implementations for `SpiInterface` and `SpiDevice` using HAL. + * + * Configuration: + * STA_HAL_SPI_ENABLE: Enable module + * + * Requires **HAL_GPIO** module. + */ +#ifndef STA_HAL_SPI_HPP +#define STA_HAL_SPI_HPP + +#include +#ifdef STA_HAL_SPI_ENABLE + +#ifndef STA_HAL_GPIO_ENABLE +#error "HAL GPIO required" +#endif // !STA_HAL_GPIO_ENABLE + +#include +#include +#include +#include + + +namespace sta +{ + /** + * @brief Get peripheral clock frequency. + * + * @return Clock frequency + */ + using HalSpiPCLKFreqFn = uint32_t (*)(); + + /** + * @brief Info related to HAL SPI interface. + */ + struct HalSpiInterfaceInfo + { + SPI_HandleTypeDef * handle; /**< Interface handle */ + HalSpiPCLKFreqFn getPCLKFreq; /**< Getter for peripheral clock used by interface */ + }; + + + /** + * @brief Implementation of `SpiInterface` interface using HAL. + */ + class HalSpiInterface : public SpiInterface + { + public: + /** + * @param info SPI interface info + * @param mutex Mutex object for managing access. Pass nullptr for no access control + */ + HalSpiInterface(const HalSpiInterfaceInfo & info, Mutex * mutex = nullptr); + + void transfer(uint8_t value) override; + void transfer16(uint16_t value) override; + void transfer(const uint8_t * buffer, size_t size) override; + void transfer(const uint8_t * txBuffer, uint8_t * rxBuffer, size_t size) override; + void receive(uint8_t * buffer, size_t size) override; + + void fill(uint8_t value, size_t count) override; + + const SpiSettings & settings() const override; + + private: + HalSpiInterfaceInfo info_; /**< SPI interface info */ + }; + + + /** + * @brief Implementation of `SpiDevice` interface using HAL. + */ + class HalSpiDevice : public SpiDevice + { + public: + /** + * @param intf SPI interface + * @param csPin Device CS pin + */ + HalSpiDevice(SpiInterface * intf, HalGpioPin csPin); + + void select() override; + void deselect() override; + + private: + HalGpioPin csPin_; /**< Device CS pin */ + }; +} // namespace sta + + +/** + * @brief Get SPI interface info struct for HAL handle. + * + * Requires STA_HAL__PCLK_IDX to be defined for the MCU. + * MCU mappings are found in `core` -> sta/mcu/.hpp files. + * + * Check the MCUs Reference Manual RCC register documentation to see which + * peripheral clock is used. + * + * @param handle SPI interface handle + */ +#define STA_HAL_SPI_INFO(handle) sta::HalSpiInterfaceInfo{&handle, STA_HAL_GET_HANDLE_PCLK_FREQ_FN(handle)} + + +#endif // STA_HAL_SPI_ENABLE + +#endif // STA_HAL_SPI_HPP diff --git a/include/sta/hal/uart.hpp b/include/sta/hal/uart.hpp new file mode 100644 index 0000000..398a00e --- /dev/null +++ b/include/sta/hal/uart.hpp @@ -0,0 +1,43 @@ +/** + * @brief Implementation of UART using HAL. + * + * Configuration: + * STA_HAL_UART_ENABLE: Enable module + * STA_HAL_UART_DEBUG_SERIAL: Create global `sta::DebugSerial` object using this UART instance + */ +#ifndef STA_HAL_UART_HPP +#define STA_HAL_UART_HPP + +#include +#ifdef STA_HAL_UART_ENABLE + +#include +#include + + +namespace sta +{ + /** + * @brief Implementation of `UART` interface using HAL. + */ + class HalUART : public UART + { + public: + /** + * @param handle UART handle + */ + HalUART(UART_HandleTypeDef * handle); + + using UART::print; + + void write(const uint8_t * buffer, size_t size) override; + + private: + UART_HandleTypeDef * handle_; /**< UART handle */ + }; +} // namespace sta + + +#endif // STA_HAL_UART_ENABLE + +#endif // STA_HAL_UART_HPP diff --git a/include/sta/mutex.hpp b/include/sta/mutex.hpp new file mode 100644 index 0000000..bfc04a5 --- /dev/null +++ b/include/sta/mutex.hpp @@ -0,0 +1,30 @@ +/** + * @brief Mutex interface definition. + */ +#ifndef STA_MUTEX_HPP +#define STA_MUTEX_HPP + + +namespace sta +{ + /** + * @brief Interface for mutex objects. + */ + class Mutex + { + public: + /** + * @brief Block until mutex has been acquired + */ + virtual void acquire() = 0; + /** + * @brief Release mutex + */ + virtual void release() = 0; + + static Mutex * ALWAYS_FREE; /**< Fake mutex that can always be acquired */ + }; +} // namespace sta + + +#endif // STA_MUTEX_HPP diff --git a/include/sta/signal.hpp b/include/sta/signal.hpp new file mode 100644 index 0000000..b38abf3 --- /dev/null +++ b/include/sta/signal.hpp @@ -0,0 +1,40 @@ +/** + * @brief Signal interface definition. + */ +#ifndef STA_SIGNAL_HPP +#define STA_SIGNAL_HPP + + +namespace sta +{ + /** + * @brief Interface for signal objects. + */ + class Signal + { + public: + /** + * @brief Enter signaled state. + */ + virtual void notify() = 0; + /** + * @brief Check signal state w/o changing it. + * + * @return True if in signaled state + */ + virtual bool peek() = 0; + /** + * @brief Check signal state. + * + * @return True if in signaled state + */ + virtual bool test() = 0; + /** + * @brief Wait until signaled state is entered. + */ + virtual void wait() = 0; + }; +} // namespace sta + + +#endif // STA_SIGNAL_HPP diff --git a/include/sta/spi_device.hpp b/include/sta/spi_device.hpp new file mode 100644 index 0000000..44253cc --- /dev/null +++ b/include/sta/spi_device.hpp @@ -0,0 +1,109 @@ +/** + * @brief SPI interface definitions. + */ +#ifndef STA_SPI_DEVICE_HPP +#define STA_SPI_DEVICE_HPP + +#include + +#include +#include + + +namespace sta +{ + /** + * @brief Interface for SPI devices. + */ + class SpiDevice + { + public: + /** + * @param intf SPI hardware interface + */ + SpiDevice(SpiInterface * intf); + + + /** + * @brief Start transmission with device. + * + * Must be called before any I/O operations. + */ + void beginTransmission(); + /** + * @brief End transmission with device. + * + * Must be called after last I/O operation. + */ + void endTransmission(); + + + /** + * @brief Send single byte of data. + * + * @param value 8-bit value + */ + void transfer(uint8_t value); + /** + * @brief Send two bytes of data. + * + * @param value 16-bit value + */ + void transfer16(uint16_t data); + /** + * @brief Send data from buffer. + * + * @param buffer Source buffer + * @param size Number of bytes to transfer + */ + void transfer(const uint8_t * buffer, size_t size); + /** + * @brief Send and receive data simultaneously. + * + * @param txBuffer Send buffer + * @param rxBuffer Receive buffer + * @param size Number of bytes to transfer + */ + void transfer(const uint8_t * txBuffer, uint8_t * rxBuffer, size_t size); + /** + * @brief Read incoming data to buffer. + * + * @param buffer Destination buffer + * @param size Number of bytes to read + */ + void receive(uint8_t * buffer, size_t size); + + + /** + * @brief Send byte value repeatedly. + * + * @param value 8-bit value to repeat + * @param count Number of repetitions + */ + void fill(uint8_t value, size_t count); + + + /** + * @brief Get SPI interface settings. + * + * @return SPI settings + */ + const SpiSettings & settings() const; + + + /** + * @brief Activate device via CS pin. + */ + virtual void select() = 0; + /** + * @brief Deactivate device via CS pin. + */ + virtual void deselect() = 0; + + private: + SpiInterface * intf_; /**< SPI hardware interface */ + }; +} // namespace sta + + +#endif // STA_SPI_DEVICE_HPP diff --git a/include/sta/spi_interface.hpp b/include/sta/spi_interface.hpp new file mode 100644 index 0000000..4d6ef17 --- /dev/null +++ b/include/sta/spi_interface.hpp @@ -0,0 +1,101 @@ +/** + * @brief SPI interface definitions. + */ +#ifndef STA_SPI_INTERFACE_HPP +#define STA_SPI_INTERFACE_HPP + +#include +#include + +#include +#include + + +namespace sta +{ + /** + * @brief Interface for SPI hardware. + */ + class SpiInterface + { + public: + /** + * @param settings SPI interface settings + * @param mutex Mutex object for managing shared access. Pass nullptr for no access control + */ + SpiInterface(Mutex * mutex = nullptr); + + + /** + * @brief Send single byte of data. + * + * @param value 8-bit value + */ + virtual void transfer(uint8_t value) = 0; + /** + * @brief Send two bytes of data. + * + * @param value 16-bit value + */ + virtual void transfer16(uint16_t value) = 0; + /** + * @brief Send data from buffer. + * + * @param buffer Source buffer + * @param size Number of bytes to transfer + */ + virtual void transfer(const uint8_t * buffer, size_t size) = 0; + /** + * @brief Send and receive data simultaneously. + * + * @param txBuffer Send buffer + * @param rxBuffer Receive buffer + * @param size Number of bytes to transfer + */ + virtual void transfer(const uint8_t * txBuffer, uint8_t * rxBuffer, size_t size) = 0; + /** + * @brief Read incoming data to buffer. + * + * @param buffer Destination buffer + * @param size Number of bytes to read + */ + virtual void receive(uint8_t * buffer, size_t size) = 0; + + + /** + * @brief Send byte value repeatedly. + * + * @param value 8-bit value to repeat + * @param count Number of repetitions + */ + virtual void fill(uint8_t value, size_t count) = 0; + + + /** + * @brief Get SPI interface settings. + * + * @return SPI settings + */ + virtual const SpiSettings & settings() const = 0; + + + /** + * @brief Acquire usage rights to use the interface. + * + * Must be called before any I/O operations are executed. + */ + virtual void acquire(); + /** + * @brief Release usage rights for interface. + * + * Must be called after last I/O operation. + */ + virtual void release(); + + private: + Mutex * mutex_; /**< Mutex object */ + }; +} // namespace sta + + +#endif // STA_SPI_INTERFACE_HPP diff --git a/include/sta/spi_settings.hpp b/include/sta/spi_settings.hpp new file mode 100644 index 0000000..decec26 --- /dev/null +++ b/include/sta/spi_settings.hpp @@ -0,0 +1,112 @@ +/** + * @brief SPI setting types. + */ +#ifndef STA_SPI_SETTINGS_HPP +#define STA_SPI_SETTINGS_HPP + +#include + + +namespace sta +{ + /** + * @brief SPI clock polarity. + */ + enum class SpiClkPolarity + { + LOW, + HIGH + }; + + /** + * @brief SPI clock phase. + */ + enum class SpiClkPhase + { + EDGE_1, + EDGE_2 + }; + + /** + * @brief SPI clock mode. + */ + enum class SpiMode + { + MODE_0, + MODE_1, + MODE_2, + MODE_3 + }; + + /** + * @brief SPI data size. + */ + enum class SpiDataSize + { + SIZE_8, + SIZE_16 + }; + + /** + * @brief SPI bit order. + */ + enum class SpiBitOrder + { + MSB, + LSB + }; + + /** + * @brief Spi baud rate prescaler. + */ + enum class SpiBaudRatePrescaler + { + DIV_2, + DIV_4, + DIV_8, + DIV_16, + DIV_32, + DIV_64, + DIV_128, + DIV_256 + }; + + + /** + * @brief SPI settings. + */ + struct SpiSettings + { + SpiMode mode; /**< SPI clock mode */ + SpiDataSize dataSize; /**< SPI data size */ + SpiBitOrder bitOrder; /**< SPI bit order */ + uint32_t clkSpeed; /**< SPI clock speed */ + }; + + + /** + * @brief Get SPI clock polarity from clock mode. + * + * @param mode SPI clock mode + * @return SPI clock polarity + */ + SpiClkPolarity getSpiClkPolarity(SpiMode mode); + /** + * @brief Get SPI clock phase from clock mode. + * + * @param mode SPI clock mode + * @return SPI clock phase + */ + SpiClkPhase getSpiClkPhase(SpiMode mode); + /** + * @brief Get SPI clock mode from clock phase and polarity. + * + * @param polarity SPI clock polarity + * @param phase SPI clock phase + * @return SPI clock mode + */ + SpiMode getSpiMode(SpiClkPolarity polarity, SpiClkPhase phase); +} // namespace sta + + +#endif // STA_SPI_SETTINGS_HPP diff --git a/include/sta/uart.hpp b/include/sta/uart.hpp new file mode 100644 index 0000000..a287bd7 --- /dev/null +++ b/include/sta/uart.hpp @@ -0,0 +1,198 @@ +/** + * @brief UART interface definition. + */ +#ifndef STA_UART_HPP +#define STA_UART_HPP + +#include +#include + + +namespace sta +{ + /** + * @brief Integer representation. + */ + enum class IntegerBase + { + DEC, /**< Decimal */ + BIN, /**< Binary */ + HEX /**< Hexadecimal */ + }; + + /** + * @brief Interface for UART. + */ + class UART + { + public: + /** + * @brief Print single character. + * + * @param c Character to print + */ + void print(char c); + /** + * @brief Print boolean value. + * + * @param b Boolean value + */ + void print(bool b); + /** + * @brief Print floating point value. + * + * @param d Floating point value + */ + void print(double d); + /** + * @brief Print integer in selected base. + * + * @param num 8-bit unsigned integer + * @param base Integer base + */ + void print(uint8_t num, IntegerBase base = IntegerBase::DEC); + /** + * @brief Print integer in selected base. + * + * @param num 16-bit unsigned integer + * @param base Integer base + */ + void print(uint16_t num, IntegerBase base = IntegerBase::DEC); + /** + * @brief Print integer in selected base. + * + * @param num 32-bit unsigned integer + * @param base Integer base + */ + void print(uint32_t num, IntegerBase base = IntegerBase::DEC); + /** + * @brief Print integer in selected base. + * + * @param num Integer + * @param base Integer base + */ + void print(size_t num, IntegerBase base = IntegerBase::DEC); + /** + * @brief Print c-string. + * + * @param str Null terminated string + */ + void print(const char * str); + /** + * @brief Print string. + * + * @param str String buffer + * @parma length String length + */ + void print(const char * str, size_t length); + + + /** + * @brief Print new-line. + */ + void println(); + /** + * @brief Print single character followed by a new-line. + * + * @param c Character to print + */ + void println(char c); + /** + * @brief Print boolean value followed by a new-line. + * + * @param b Boolean value + */ + void println(bool b); + /** + * @brief Print floating point value followed by a new-line. + * + * @param d Floating point value + */ + void println(double d); + /** + * @brief Print integer in selected base followed by a new-line. + * + * @param num 8-bit unsigned integer + * @param base Integer base + */ + void println(uint8_t num, IntegerBase base = IntegerBase::DEC); + /** + * @brief Print integer in selected base followed by a new-line. + * + * @param num 16-bit unsigned integer + * @param base Integer base + */ + void println(uint16_t num, IntegerBase base = IntegerBase::DEC); + /** + * @brief Print integer in selected base followed by a new-line. + * + * @param num 32-bit unsigned integer + * @param base Integer base + */ + void println(uint32_t num, IntegerBase base = IntegerBase::DEC); + /** + * @brief Print integer in selected base followed by a new-line. + * + * @param num Integer + * @param base Integer base + */ + void println(size_t num, IntegerBase base = IntegerBase::DEC); + /** + * @brief Print c-string followed by a new-line. + * + * @param str Null terminated string + */ + void println(const char * str); + /** + * @brief Print string followed by a new-line. + * + * @param str String buffer + * @parma length String length + */ + void println(const char * str, size_t length); + + + /** + * @brief Write buffer to UART. + * + * @param buffer Source buffer + * @param size Number of bytes in buffer + */ + virtual void write(const uint8_t * buffer, size_t size) = 0; + + private: + /** + * @brief Print unsigned integer in selected base. + * + * @param value Unsigned integer value + * @param base Integer base + * @param fmt printf format string for base 10 + * @param size Size of value in bytes + */ + void printBase(uintmax_t value, IntegerBase base, const char * fmt, size_t size); + /** + * @brief Print unsigned integer in base 10. + * + * @param value Unsigned integer value + * @param fmt printf format string + */ + void printDec(uintmax_t value, const char * fmt); + /** + * @brief Print unsigned integer in base 2. + * + * @param value Unsigned integer value + * @param digits Number of digits to print + */ + void printBin(uintmax_t value, size_t digits); + /** + * @brief Print unsigned integer in base 16. + * + * @param value Unsigned integer value + * @param digits Number of digits to print + */ + void printHex(uintmax_t value, size_t digits); + }; +} // namespace sta + + +#endif // STA_UART_HPP diff --git a/src/atomic/mutex.cpp b/src/atomic/mutex.cpp new file mode 100644 index 0000000..60dc82b --- /dev/null +++ b/src/atomic/mutex.cpp @@ -0,0 +1,23 @@ +#include +#ifdef STA_ATOMIC_ENABLE + + +namespace sta +{ + AtomicMutex::AtomicMutex() + : lock_{ATOMIC_FLAG_INIT} + {} + + void AtomicMutex::acquire() + { + while (lock_.test_and_set()); + } + + void AtomicMutex::release() + { + lock_.clear(); + } +} // namespace sta + + +#endif // STA_ATOMIC_ENABLE diff --git a/src/atomic/signal.cpp b/src/atomic/signal.cpp new file mode 100644 index 0000000..e33d7f6 --- /dev/null +++ b/src/atomic/signal.cpp @@ -0,0 +1,33 @@ +#include +#ifdef STA_ATOMIC_ENABLE + + +namespace sta +{ + AtomicSignal::AtomicSignal() + : signal_{false} + {} + + void AtomicSignal::notify() + { + signal_.store(true); + } + + bool AtomicSignal::peek() + { + return signal_.load(); + } + + bool AtomicSignal::test() + { + return signal_.exchange(false); + } + + void AtomicSignal::wait() + { + while (!signal_.exchange(false)); + } +} // namespace sta + + +#endif // STA_ATOMIC_ENABLE diff --git a/src/hal/gpio_pin.cpp b/src/hal/gpio_pin.cpp new file mode 100644 index 0000000..678b936 --- /dev/null +++ b/src/hal/gpio_pin.cpp @@ -0,0 +1,38 @@ +#include + +#ifdef STA_HAL_GPIO_ENABLE + +#include + + +namespace sta +{ + HalGpioPin::HalGpioPin(GPIO_TypeDef * port, uint16_t pin) + : port_{port}, pin_{pin} + { + STA_ASSERT(port != nullptr); + } + + void HalGpioPin::setState(GpioPinState state) + { + HAL_GPIO_WritePin(port_, pin_, (state == GpioPinState::LOW) ? GPIO_PIN_RESET : GPIO_PIN_SET); + } + + GPIO_TypeDef * HalGpioPin::getPort() const + { + return port_; + } + + uint16_t HalGpioPin::getPin() const + { + return pin_; + } + + uint8_t HalGpioPin::getIndex() const + { + return GPIO_GET_INDEX(port_); + } +} // namespace sta + + +#endif // STA_HAL_GPIO_ENABLE diff --git a/src/hal/spi.cpp b/src/hal/spi.cpp new file mode 100644 index 0000000..708a5c2 --- /dev/null +++ b/src/hal/spi.cpp @@ -0,0 +1,194 @@ +#include + +#ifdef STA_HAL_SPI_ENABLE + +#include +#include +#include + + +#ifdef STA_MCU_LITTLE_ENDIAN +# define STA_HAL_SPI_REVERSE_BIT_ORDER SpiBitOrder::MSB +#elif STA_MCU_BIG_ENDIAN +# define STA_HAL_SPI_REVERSE_BIT_ORDER SpiBitOrder::LSB +#else // !STA_MCU_LITTLE_ENDIAN && !STA_MCU_BIG_ENDIAN +# ifdef STA_HAL_SPI_REVERSE_BIT_ORDER +# warning "Internal STA_HAL_SPI_REVERSE_BIT_ORDER macro manually defined! Better now what you are doing!!!" +# else // !STA_HAL_SPI_REVERSE_BIT_ORDER +# error "Unknown endian-ness. Define STA_MCU_LITTLE_ENDIAN or STA_MCU_BIG_ENDIAN in " +# endif // !STA_HAL_SPI_REVERSE_BIT_ORDER +#endif // !STA_MCU_LITTLE_ENDIAN && !STA_MCU_BIG_ENDIAN + + +namespace sta +{ + static SpiSettings getHalSpiSettings(SPI_HandleTypeDef * handle, uint32_t pclkFreq) + { + SpiSettings settings; + + settings.mode = getSpiMode( + (handle->Init.CLKPolarity == SPI_POLARITY_LOW) ? SpiClkPolarity::LOW : SpiClkPolarity::HIGH, + (handle->Init.CLKPhase == SPI_PHASE_1EDGE) ? SpiClkPhase::EDGE_1 : SpiClkPhase::EDGE_2 + ); + settings.dataSize = (handle->Init.DataSize == SPI_DATASIZE_8BIT) ? SpiDataSize::SIZE_8 : SpiDataSize::SIZE_16; + settings.bitOrder = (handle->Init.FirstBit == SPI_FIRSTBIT_MSB) ? SpiBitOrder::MSB : SpiBitOrder::LSB; + + uint32_t prescaler = 1; + switch (handle->Init.BaudRatePrescaler) + { + case SPI_BAUDRATEPRESCALER_2: + prescaler = 2; + break; + case SPI_BAUDRATEPRESCALER_4: + prescaler = 4; + break; + case SPI_BAUDRATEPRESCALER_8: + prescaler = 8; + break; + case SPI_BAUDRATEPRESCALER_16: + prescaler = 16; + break; + case SPI_BAUDRATEPRESCALER_32: + prescaler = 32; + break; + case SPI_BAUDRATEPRESCALER_64: + prescaler = 64; + break; + case SPI_BAUDRATEPRESCALER_128: + prescaler = 128; + break; + case SPI_BAUDRATEPRESCALER_256: + prescaler = 256; + break; + default: + // Unreachable case + STA_ASSERT_MSG(false, "Case for SPI_BAUDRATEPRESCALER not handled"); + STA_UNREACHABLE(); + } + + // SPI clock speed is based of PCLK + settings.clkSpeed = pclkFreq / prescaler; + + return settings; + } + + + HalSpiInterface::HalSpiInterface(const HalSpiInterfaceInfo & info, Mutex * mutex /* = nullptr */) + : SpiInterface(mutex), info_{info} + { + STA_ASSERT(info.handle != nullptr); + STA_ASSERT(info.getPCLKFreq != nullptr); + } + + + void HalSpiInterface::transfer(uint8_t value) + { + if (settings().dataSize == SpiDataSize::SIZE_8) + { + HAL_SPI_Transmit(info_.handle, &value, 1, HAL_MAX_DELAY); + } + else + { + // Required since tx buffer is cast to uint16_t * internally + uint16_t dummy = value; + HAL_SPI_Transmit(info_.handle, reinterpret_cast(&dummy), 1, HAL_MAX_DELAY); + } + } + + void HalSpiInterface::transfer16(uint16_t value) + { + static_assert(sizeof(value) == 2, "Unexpected uint16_t size"); + + uint16_t size = 1; + + // Send as two bytes if data size is 8-bit + if (settings().dataSize == SpiDataSize::SIZE_8) + { + size = 2; + + if (settings().bitOrder == STA_HAL_SPI_REVERSE_BIT_ORDER) + { + // Reverse byte order from internal representation + value = STA_UINT16_SWAP_BYTE_ORDER(value); + } + } + + HAL_SPI_Transmit(info_.handle, reinterpret_cast(&value), size, HAL_MAX_DELAY); + } + + void HalSpiInterface::transfer(const uint8_t * buffer, size_t size) + { + STA_ASSERT(buffer != nullptr); + STA_ASSERT(size != 0); + + HAL_SPI_Transmit(info_.handle, const_cast(buffer), size, HAL_MAX_DELAY); + } + + void HalSpiInterface::transfer(const uint8_t * txBuffer, uint8_t * rxBuffer, size_t size) + { + STA_ASSERT(txBuffer != nullptr); + STA_ASSERT(rxBuffer != nullptr); + STA_ASSERT(size != 0); + + HAL_SPI_TransmitReceive(info_.handle, const_cast(txBuffer), rxBuffer, size, HAL_MAX_DELAY); + } + + void HalSpiInterface::receive(uint8_t * buffer, size_t size) + { + STA_ASSERT(buffer != nullptr); + + HAL_SPI_Receive(info_.handle, buffer, size, HAL_MAX_DELAY); + } + + + void HalSpiInterface::fill(uint8_t value, size_t count) + { + STA_ASSERT(count != 0); + + if (settings().dataSize == SpiDataSize::SIZE_8) + { + for (size_t i = 0; i < count; ++i) + { + HAL_SPI_Transmit(info_.handle, &value, 1, HAL_MAX_DELAY); + } + } + else + { + // Required since tx buffer is cast to uint16_t * internally + uint16_t dummy = value; + for (size_t i = 0; i < count; ++i) + { + HAL_SPI_Transmit(info_.handle, reinterpret_cast(&dummy), 1, HAL_MAX_DELAY); + } + } + } + + + const SpiSettings & HalSpiInterface::settings() const + { + // Cache settings + static SpiSettings settings = getHalSpiSettings(info_.handle, info_.getPCLKFreq()); + + return settings; + } + + + + + HalSpiDevice::HalSpiDevice(SpiInterface * intf, HalGpioPin csPin) + : SpiDevice(intf), csPin_{csPin} + {} + + void HalSpiDevice::select() + { + csPin_.setState(GpioPinState::LOW); + } + + void HalSpiDevice::deselect() + { + csPin_.setState(GpioPinState::HIGH); + } +} // namespace sta + + +#endif // STA_HAL_SPI_ENABLE diff --git a/src/hal/uart.cpp b/src/hal/uart.cpp new file mode 100644 index 0000000..d0bd319 --- /dev/null +++ b/src/hal/uart.cpp @@ -0,0 +1,44 @@ +#include + +#ifdef STA_HAL_UART_ENABLE + +#include + + +namespace sta +{ + HalUART::HalUART(UART_HandleTypeDef * handle) + : handle_{handle} + { + STA_ASSERT(handle != nullptr); + } + + + void HalUART::write(const uint8_t * buffer, size_t size) + { + STA_ASSERT(buffer != nullptr); + + HAL_UART_Transmit(handle_, const_cast(buffer), size, HAL_MAX_DELAY); + } +} // namespace sta + + +#ifdef STA_HAL_UART_DEBUG_SERIAL + +// Get extern declaration for DebugSerial because const namespace level variables have internal linkage by default +#include + +#include + +namespace sta +{ + HalUART gHalDebugSerial(&STA_HAL_UART_DEBUG_SERIAL); + + // Used by + UART * const DebugSerial = &gHalDebugSerial; +} // namespace sta + +#endif // STA_HAL_UART_DEBUG_SERIAL + + +#endif // STA_HAL_UART_ENABLE diff --git a/src/mutex.cpp b/src/mutex.cpp new file mode 100644 index 0000000..b861ebd --- /dev/null +++ b/src/mutex.cpp @@ -0,0 +1,20 @@ +#include + + +namespace sta +{ + /** + * @brief Dummy mutex implementation with no access control. + */ + class DummyMutex : public Mutex + { + public: + void acquire() override {} + void release() override {} + }; + + static DummyMutex dummyMutex; + + + Mutex * Mutex::ALWAYS_FREE = &dummyMutex; +} // namespace sta diff --git a/src/spi_device.cpp b/src/spi_device.cpp new file mode 100644 index 0000000..4f54883 --- /dev/null +++ b/src/spi_device.cpp @@ -0,0 +1,76 @@ +#include + +#include + + +namespace sta +{ + SpiDevice::SpiDevice(SpiInterface * intf) + : intf_{intf} + { + STA_ASSERT(intf != nullptr); + } + + void SpiDevice::beginTransmission() + { + // Acquire SPI access and activate device + intf_->acquire(); + select(); + } + + void SpiDevice::endTransmission() + { + // Deactivate device and release SPI access + deselect(); + intf_->release(); + } + + + // Forward I/O operations to SPI interface + + void SpiDevice::transfer(uint8_t data) + { + intf_->transfer(data); + } + + void SpiDevice::transfer16(uint16_t data) + { + intf_->transfer16(data); + } + + void SpiDevice::transfer(const uint8_t * buffer, size_t size) + { + STA_ASSERT(buffer != nullptr); + + intf_->transfer(buffer, size); + } + + void SpiDevice::transfer(const uint8_t * txBuffer, uint8_t * rxBuffer, size_t size) + { + STA_ASSERT(txBuffer != nullptr); + STA_ASSERT(rxBuffer != nullptr); + STA_ASSERT(size != 0); + + intf_->transfer(txBuffer, rxBuffer, size); + } + + void SpiDevice::receive(uint8_t * buffer, size_t size) + { + STA_ASSERT(buffer != nullptr); + + intf_->receive(buffer, size); + } + + void SpiDevice::fill(uint8_t value, size_t count) + { + STA_ASSERT(count != 0); + + intf_->fill(value, count); + } + + + const SpiSettings & SpiDevice::settings() const + { + return intf_->settings(); + } +} // namespace sta diff --git a/src/spi_interface.cpp b/src/spi_interface.cpp new file mode 100644 index 0000000..26a141e --- /dev/null +++ b/src/spi_interface.cpp @@ -0,0 +1,21 @@ +#include + + +namespace sta +{ + SpiInterface::SpiInterface(Mutex * mutex /* = nullptr */) + : mutex_{mutex} + {} + + void SpiInterface::acquire() + { + if (mutex_ != nullptr) + mutex_->acquire(); + } + + void SpiInterface::release() + { + if (mutex_ != nullptr) + mutex_->release(); + } +} // namespace sta diff --git a/src/spi_settings.cpp b/src/spi_settings.cpp new file mode 100644 index 0000000..73478e7 --- /dev/null +++ b/src/spi_settings.cpp @@ -0,0 +1,72 @@ +#include + +#include +#include + + +namespace sta +{ + SpiClkPolarity getSpiClkPolarity(SpiMode mode) + { + switch (mode) + { + case SpiMode::MODE_0: + case SpiMode::MODE_1: + return SpiClkPolarity::LOW; + + case SpiMode::MODE_2: + case SpiMode::MODE_3: + return SpiClkPolarity::HIGH; + + default: + // Unreachable case + STA_ASSERT_MSG(false, "Case for SpiMode enum not handled"); + STA_UNREACHABLE(); + } + } + + SpiClkPhase getSpiClkPhase(SpiMode mode) + { + switch (mode) + { + case SpiMode::MODE_0: + case SpiMode::MODE_2: + return SpiClkPhase::EDGE_1; + + case SpiMode::MODE_1: + case SpiMode::MODE_3: + return SpiClkPhase::EDGE_2; + + default: + // Unreachable case + STA_ASSERT_MSG(false, "Case for SpiMode enum not handled"); + STA_UNREACHABLE(); + } + } + + SpiMode getSpiMode(SpiClkPolarity polarity, SpiClkPhase phase) + { + if (polarity == SpiClkPolarity::LOW) + { + if (phase == SpiClkPhase::EDGE_1) + { + return SpiMode::MODE_0; + } + else + { + return SpiMode::MODE_1; + } + } + else + { + if (phase == SpiClkPhase::EDGE_1) + { + return SpiMode::MODE_2; + } + else + { + return SpiMode::MODE_3; + } + } + } +} // namespace sta diff --git a/src/uart.cpp b/src/uart.cpp new file mode 100644 index 0000000..84240e7 --- /dev/null +++ b/src/uart.cpp @@ -0,0 +1,203 @@ +#include + +#include + +#include +#include + + + +namespace sta +{ + void UART::print(char c) + { + print(&c, 1); + } + + void UART::print(bool b) + { + print(b ? "true" : "false"); + } + + void UART::print(double d) + { + char buffer[64]; + snprintf(buffer, sizeof(buffer), "%f", d); + print(buffer); + } + + void UART::print(uint8_t num, IntegerBase base /* = IntegerBase::DEC */) + { + printBase(num, base, "%" PRIu8, sizeof(num)); + } + + void UART::print(uint16_t num, IntegerBase base /* = IntegerBase::DEC */) + { + printBase(num, base, "%" PRIu16, sizeof(num)); + } + + void UART::print(uint32_t num, IntegerBase base /* = IntegerBase::DEC */) + { + printBase(num, base, "%" PRIu32, sizeof(num)); + } + + void UART::print(size_t num, IntegerBase base /* = IntegerBase::DEC */) + { + printBase(num, base, "%z", sizeof(num)); + } + + void UART::print(const char * str) + { + print(str, strlen(str)); + } + + void UART::print(const char * str, size_t length) + { + write(reinterpret_cast(str), length); + } + + + void UART::println() + { + print("\r\n", 2); + } + + void UART::println(char c) + { + print(&c, 1); + println(); + } + + void UART::println(bool b) + { + print(b); + println(); + } + + void UART::println(double d) + { + print(d); + println(); + } + + void UART::println(uint8_t num, IntegerBase base /* = IntegerBase::DEC */) + { + print(num, base); + println(); + } + + void UART::println(uint16_t num, IntegerBase base /* = IntegerBase::DEC */) + { + print(num, base); + println(); + } + + void UART::println(uint32_t num, IntegerBase base /* = IntegerBase::DEC */) + { + print(num, base); + println(); + } + + void UART::println(size_t num, IntegerBase base /* = IntegerBase::DEC */) + { + print(num, base); + println(); + } + + void UART::println(const char * str) + { + println(str, strlen(str)); + } + + void UART::println(const char * str, size_t length) + { + print(str, length); + println(); + } + + + + void UART::printBase(uintmax_t num, IntegerBase base, const char * fmt, size_t size) + { + switch (base) + { + case IntegerBase::DEC: + printDec(num, fmt); + break; + + case IntegerBase::BIN: + // Digits in base 2 = size in bytes * 8 + printBin(num, size * 8); + break; + + case IntegerBase::HEX: + // Digits in base 16 = size in bytes * 2 + printHex(num, size * 2); + break; + + default: + print(""); + } + } + + void UART::printDec(uintmax_t num, const char * fmt) + { + char buffer[64]; + snprintf(buffer, sizeof(buffer), fmt, static_cast(num)); + print(buffer); + } + + void UART::printBin(uintmax_t value, size_t digits) + { + // Need 8 digits for every byte + char buffer[sizeof(value) * 8]; + + // Check bounds + if (digits > sizeof(buffer)) + { + print(""); + return; + } + // Nothing to do + if (digits == 0) + return; + + for (size_t i = 0; i < digits; ++i) + { + // Convert bit to '0' or '1' + // First digit in buffer is MSB in value, so shift from high to low + buffer[i] = '0' + ((value >> (digits - 1 - i)) & 0x1); + } + + print(buffer, digits); + } + + void UART::printHex(uintmax_t value, size_t digits) + { + // Need 2 digits for every byte + char buffer[sizeof(value) * 2]; + + // Check bounds + if (digits > sizeof(buffer)) + { + print(""); + return; + } + // Nothing to do + if (digits == 0) + return; + + for (size_t i = 0; i < digits; ++i) + { + // Convert 4 bits to hex + // First digit in buffer is 4 MSBs in value, so shift from high to low + uint8_t hex = ((value >> ((digits - 1 - i) * 4)) & 0xF); + if (hex > 9) + buffer[i] = 'A' + (hex - 10); + else + buffer[i] = '0' + hex; + } + + print(buffer, digits); + } +} // namespace sta