skullc-peripherals/Peripherals/Inc/peripherals_hal_st.hpp

347 lines
8.5 KiB
C++

/*
* peripherals_hal_st.hpp
*
* Created on: Mar 30, 2021
* Author: erki
*/
#ifndef SKULLC_PERIPHERALS_HAL_ST_HPP_
#define SKULLC_PERIPHERALS_HAL_ST_HPP_
#include <main.h>
#include <array>
#define USE_DELAY_US
namespace Peripherals
{
namespace Hal
{
namespace St
{
template<typename Origin>
using IsrCallbackFn = void (*)(Origin*);
template<typename Origin, typename Handler, void (Handler::*func)(),
typename Tag>
IsrCallbackFn<Origin> createCallback(Handler& h_in)
{
static Handler* h = &h_in;
return +[](Origin*) { (h->*func)(); };
}
struct StaticHal
{
static void initialize()
{
#ifdef USE_DELAY_US
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
#endif
}
static std::uint32_t getMillis()
{
return HAL_GetTick();
}
static void delay(const std::uint32_t milliseconds)
{
HAL_Delay(milliseconds);
}
static void delayUs(const std::uint32_t micros)
{
#ifdef USE_DELAY_US
const std::uint32_t tick_start = DWT->CYCCNT;
const std::uint32_t ticks_delay = micros * (SystemCoreClock / 1'000'000);
while (DWT->CYCCNT - tick_start < ticks_delay)
;
#else
(void) micros;
#endif
}
static void enableInterrupts() { __enable_irq(); }
static void disableInterrupts() { __disable_irq(); }
};
#ifdef HAL_GPIO_MODULE_ENABLED
struct Gpio
{
GPIO_TypeDef* port = nullptr;
std::uint16_t pin = 0;
const bool inverted = false;
Gpio() = delete;
explicit Gpio(GPIO_TypeDef* port, const std::uint16_t pin, const bool inverted)
: port(port), pin(pin), inverted(inverted) {}
void set(const bool& state)
{
HAL_GPIO_WritePin(port, pin, GPIO_PinState(inverted ? !state : state));
}
void toggle() { HAL_GPIO_TogglePin(port, pin); }
bool read() const { return inverted ? !bool(HAL_GPIO_ReadPin(port, pin)) : bool(HAL_GPIO_ReadPin(port, pin)); }
};
#define CREATE_GPIO(name) \
Peripherals::Hal::St::Gpio \
{ \
name##_GPIO_Port, name##_Pin, false \
}
#define CREATE_INV_GPIO(name) \
Peripherals::Hal::St::Gpio \
{ \
name##_GPIO_Port, name##_Pin, true \
}
#endif// HAL_GPIO_MODULE_ENABLED
template<
typename T,
HAL_StatusTypeDef (*transmit_)(
T*, std::uint8_t* data, std::uint16_t data_len, std::uint32_t timeout),
HAL_StatusTypeDef (*receive_)(T*, std::uint8_t* data,
std::uint16_t data_len, std::uint32_t timeout)>
struct SerialInterface
{
using underlying_handle_type = T;
underlying_handle_type* handle;
SerialInterface() = delete;
explicit SerialInterface(underlying_handle_type* handle) : handle(handle) {}
bool transmit(std::uint8_t* data, const std::uint32_t data_len)
{
return transmit_(handle, data, data_len, 100) == HAL_StatusTypeDef::HAL_OK;
}
template<typename Td, std::size_t N>
bool transmit(std::array<Td, N>& array)
{
static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large.");
return transmit(reinterpret_cast<std::uint8_t*>(array.data()), std::uint32_t(N));
}
bool receive(std::uint8_t* data, const std::uint32_t data_len)
{
return receive_(handle, data, data_len, 100) == HAL_StatusTypeDef::HAL_OK;
}
template<typename Td, std::size_t N>
bool receive(std::array<Td, N>& array)
{
static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large.");
return receive(reinterpret_cast<std::uint8_t*>(array.data()), std::uint32_t(N));
}
};
template<typename T,
HAL_StatusTypeDef (*transmit_)(T*, std::uint8_t* data,
std::uint16_t data_len),
HAL_StatusTypeDef (*receive_)(T*, std::uint8_t* data,
std::uint16_t data_len)>
struct SerialInterfaceAsync
{
using underlying_handle_type = T;
underlying_handle_type* handle;
SerialInterfaceAsync() = delete;
explicit SerialInterfaceAsync(underlying_handle_type* handle)
: handle(handle) {}
bool transmit(std::uint8_t* data, const std::uint32_t data_len)
{
return transmit_(handle, data, data_len) == HAL_StatusTypeDef::HAL_OK;
}
template<typename Td, std::size_t N>
bool transmit(std::array<Td, N>& array)
{
static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large.");
return transmit_(reinterpret_cast<std::uint8_t*>(array.data()), std::uint32_t(N));
}
bool receive(std::uint8_t* data, const std::uint32_t data_len)
{
return receive_(handle, data, data_len) == HAL_StatusTypeDef::HAL_OK;
}
template<typename Td, std::size_t N>
bool receive(std::array<Td, N>& array)
{
static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large.");
return receive(reinterpret_cast<std::uint8_t*>(array.data()), std::uint32_t(N));
}
};
#ifdef HAL_SPI_MODULE_ENABLED
using SpiInterface =
SerialInterface<SPI_HandleTypeDef, HAL_SPI_Transmit, HAL_SPI_Receive>;
struct SpiRegisters
{
SpiInterface handle;
Gpio chip_select;
SpiRegisters() = delete;
explicit SpiRegisters(const SpiInterface& handle, const Gpio& cs)
: handle(handle), chip_select(cs)
{
chip_select.set(true);
}
void writeRegister(std::uint8_t reg, uint8_t data)
{
chip_select.set(false);
handle.transmit(&reg, 1);
handle.transmit(&data, 1);
chip_select.set(true);
}
void writeRegisterMultibyte(std::uint8_t reg, std::uint8_t* data,
const std::uint32_t len)
{
chip_select.set(false);
handle.transmit(&reg, 1);
handle.transmit(data, len);
chip_select.set(true);
}
std::uint8_t readRegister(std::uint8_t reg,
const std::uint32_t read_delay = 0)
{
chip_select.set(false);
handle.transmit(&reg, 1);
std::uint8_t output = 255;
if (read_delay)
StaticHal::delayUs(read_delay);
handle.receive(&output, 1);
chip_select.set(true);
return output;
}
void readRegisterMultibyte(std::uint8_t reg, std::uint8_t* data,
const std::uint32_t len,
const std::uint32_t read_delay = 0)
{
chip_select.set(false);
handle.transmit(&reg, 1);
if (read_delay)
StaticHal::delayUs(read_delay);
handle.receive(data, len);
chip_select.set(true);
}
};
#endif// HAL_SPI_MODULE_ENABLED
#ifdef HAL_UART_MODULE_ENABLED
namespace _Details
{
/**
* @hack: Temporary workarounds to make the HAL compile for FW1.27. ST made it so that only UART libraries
* are const-correct. The others remain unconst. So these wrappers will exist until we're no longer partially
* const-correct.
*/
inline HAL_StatusTypeDef uartTransmit(UART_HandleTypeDef* huart, std::uint8_t* data, const std::uint16_t size, const std::uint32_t timeout)
{
return HAL_UART_Transmit(huart, data, size, timeout);
}
inline HAL_StatusTypeDef uartTransmitDma(UART_HandleTypeDef* huart, std::uint8_t* data, const std::uint16_t size)
{
return HAL_UART_Transmit_DMA(huart, data, size);
}
}// namespace _Details
using UartInterface =
SerialInterface<UART_HandleTypeDef, _Details::uartTransmit, HAL_UART_Receive>;
using UartInterfaceDMA =
SerialInterfaceAsync<UART_HandleTypeDef, _Details::uartTransmitDma,
HAL_UART_Receive_DMA>;
#endif// HAL_UART_MODULE_ENABLED
#ifdef HAL_TIM_MODULE_ENABLED
struct PwmChannel
{
TIM_HandleTypeDef* handle;
std::uint32_t channel;
PwmChannel() = delete;
explicit PwmChannel(TIM_HandleTypeDef* handle, const std::uint32_t channel)
: handle(handle), channel(channel) {}
void enable() { HAL_TIM_PWM_Start(handle, channel); }
void disable() { HAL_TIM_PWM_Stop(handle, channel); }
void setCompare(const std::uint32_t compare)
{
__HAL_TIM_SET_COMPARE(handle, channel, compare);
}
std::uint32_t maxValue() { return handle->Init.Period; }
};
#endif// HAL_TIM_MODULE_ENABLED
struct ItmSerialInterface
{
bool transmit(std::uint8_t* data, const std::uint32_t data_len)
{
for (std::uint32_t i = 0; i < data_len; i++)
{
ITM_SendChar(char(data[i]));
}
return true;
}
template<typename T, std::size_t N>
bool transmit(std::array<T, N>& array)
{
static_assert(sizeof(T) == sizeof(std::uint8_t), "Data is not a byte large.");
return transmit(reinterpret_cast<std::uint8_t*>(array.data()), std::uint32_t(N));
}
};
}// namespace St
}// namespace Hal
}// namespace Peripherals
#endif /* SKULLC_PERIPHERALS_HAL_ST_HPP_ */