Merge io_interfaces repo into helpers

This commit is contained in:
Henrik Stickann 2022-05-02 13:20:25 +02:00
commit f8666b69c8
24 changed files with 1726 additions and 4 deletions

View File

@ -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 <tim_handle>`: 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.
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 `<sta/config.hpp>`.

View File

@ -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 <sta/config.hpp>
#ifdef STA_ATOMIC_ENABLE
#include <sta/mutex.hpp>
#include <atomic>
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

View File

@ -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 <sta/config.hpp>
#ifdef STA_ATOMIC_ENABLE
#include <sta/signal.hpp>
#include <atomic>
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<bool> signal_; /**< Atomic bool used as signal */
};
} // namespace sta
#endif // STA_ATOMIC_ENABLE
#endif // STA_ATOMIC_SIGNAL_HPP

35
include/sta/gpio_pin.hpp Normal file
View File

@ -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

View File

@ -0,0 +1,60 @@
/**
* @brief Helper macros for HAL clock queries.
*/
#ifndef STA_HAL_CLOCKS_HPP
#define STA_HAL_CLOCKS_HPP
#include <sta/config.hpp>
#include <sta/hal.hpp>
/**
* @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

View File

@ -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 <sta/config.hpp>
#ifdef STA_HAL_GPIO_ENABLE
#include <sta/gpio_pin.hpp>
#include <sta/hal.hpp>
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

108
include/sta/hal/spi.hpp Normal file
View File

@ -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 <sta/config.hpp>
#ifdef STA_HAL_SPI_ENABLE
#ifndef STA_HAL_GPIO_ENABLE
#error "HAL GPIO required"
#endif // !STA_HAL_GPIO_ENABLE
#include <sta/hal.hpp>
#include <sta/spi_device.hpp>
#include <sta/hal/clocks.hpp>
#include <sta/hal/gpio_pin.hpp>
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_<handle>_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

43
include/sta/hal/uart.hpp Normal file
View File

@ -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 <sta/config.hpp>
#ifdef STA_HAL_UART_ENABLE
#include <sta/hal.hpp>
#include <sta/uart.hpp>
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

30
include/sta/mutex.hpp Normal file
View File

@ -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

40
include/sta/signal.hpp Normal file
View File

@ -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

109
include/sta/spi_device.hpp Normal file
View File

@ -0,0 +1,109 @@
/**
* @brief SPI interface definitions.
*/
#ifndef STA_SPI_DEVICE_HPP
#define STA_SPI_DEVICE_HPP
#include <sta/spi_interface.hpp>
#include <cstdint>
#include <cstddef>
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

View File

@ -0,0 +1,101 @@
/**
* @brief SPI interface definitions.
*/
#ifndef STA_SPI_INTERFACE_HPP
#define STA_SPI_INTERFACE_HPP
#include <sta/mutex.hpp>
#include <sta/spi_settings.hpp>
#include <cstdint>
#include <cstddef>
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

View File

@ -0,0 +1,112 @@
/**
* @brief SPI setting types.
*/
#ifndef STA_SPI_SETTINGS_HPP
#define STA_SPI_SETTINGS_HPP
#include <cstdint>
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

198
include/sta/uart.hpp Normal file
View File

@ -0,0 +1,198 @@
/**
* @brief UART interface definition.
*/
#ifndef STA_UART_HPP
#define STA_UART_HPP
#include <cstddef>
#include <cstdint>
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

23
src/atomic/mutex.cpp Normal file
View File

@ -0,0 +1,23 @@
#include <sta/atomic/mutex.hpp>
#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

33
src/atomic/signal.cpp Normal file
View File

@ -0,0 +1,33 @@
#include <sta/atomic/signal.hpp>
#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

38
src/hal/gpio_pin.cpp Normal file
View File

@ -0,0 +1,38 @@
#include <sta/hal/gpio_pin.hpp>
#ifdef STA_HAL_GPIO_ENABLE
#include <sta/assert.hpp>
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

194
src/hal/spi.cpp Normal file
View File

@ -0,0 +1,194 @@
#include <sta/hal/spi.hpp>
#ifdef STA_HAL_SPI_ENABLE
#include <sta/assert.hpp>
#include <sta/endian.hpp>
#include <sta/lang.hpp>
#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 <sta/config.hpp>"
# 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<uint8_t *>(&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<uint8_t *>(&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<uint8_t *>(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<uint8_t *>(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<uint8_t *>(&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

44
src/hal/uart.cpp Normal file
View File

@ -0,0 +1,44 @@
#include <sta/hal/uart.hpp>
#ifdef STA_HAL_UART_ENABLE
#include <sta/assert.hpp>
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<uint8_t *>(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 <sta/debug_serial.hpp>
#include <usart.h>
namespace sta
{
HalUART gHalDebugSerial(&STA_HAL_UART_DEBUG_SERIAL);
// Used by <sta/debug.hpp>
UART * const DebugSerial = &gHalDebugSerial;
} // namespace sta
#endif // STA_HAL_UART_DEBUG_SERIAL
#endif // STA_HAL_UART_ENABLE

20
src/mutex.cpp Normal file
View File

@ -0,0 +1,20 @@
#include <sta/mutex.hpp>
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

76
src/spi_device.cpp Normal file
View File

@ -0,0 +1,76 @@
#include <sta/spi_device.hpp>
#include <sta/assert.hpp>
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

21
src/spi_interface.cpp Normal file
View File

@ -0,0 +1,21 @@
#include <sta/spi_interface.hpp>
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

72
src/spi_settings.cpp Normal file
View File

@ -0,0 +1,72 @@
#include <sta/spi_settings.hpp>
#include <sta/assert.hpp>
#include <sta/lang.hpp>
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

203
src/uart.cpp Normal file
View File

@ -0,0 +1,203 @@
#include <sta/uart.hpp>
#include <sta/printf.hpp>
#include <cstring>
#include <cinttypes>
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<const uint8_t *>(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("<invalid_base>");
}
}
void UART::printDec(uintmax_t num, const char * fmt)
{
char buffer[64];
snprintf(buffer, sizeof(buffer), fmt, static_cast<uint32_t>(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("<bin_value_too_big>");
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("<hex_value_too_big>");
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