474 lines
12 KiB
C++
474 lines
12 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_
|
|
|
|
#ifdef SKULLC_USE_HAL_ST
|
|
|
|
#include <main.h>
|
|
|
|
#include <array>
|
|
|
|
#ifdef SKULLC_WITH_CORO
|
|
#include "skullc/coro/peripheral_awaiters.hpp"
|
|
#endif
|
|
|
|
#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 = decltype([] {})>
|
|
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)); }
|
|
|
|
#ifdef SKULLC_WITH_CORO
|
|
auto await_edge(const Edge& edge)
|
|
{
|
|
return skullc::coro::PinEdgeAwaiter(this, edge);
|
|
}
|
|
#endif
|
|
};
|
|
|
|
#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 SKULLC_WITH_CORO
|
|
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 SerialInterfaceCoro
|
|
{
|
|
private:
|
|
void on_rx_complete()
|
|
{
|
|
if (rx_awaiter_)
|
|
rx_awaiter_->set_completed();
|
|
}
|
|
|
|
void on_tx_complete()
|
|
{
|
|
if (tx_awaiter_)
|
|
tx_awaiter_->set_completed();
|
|
}
|
|
|
|
std::optional<skullc::coro::ISRAwaiter> rx_awaiter_ = std::nullopt;
|
|
std::optional<skullc::coro::ISRAwaiter> tx_awaiter_ = std::nullopt;
|
|
|
|
public:
|
|
using underlying_handle_type = T;
|
|
underlying_handle_type* handle;
|
|
|
|
SerialInterfaceCoro() = delete;
|
|
|
|
template<typename = decltype([] {})>
|
|
explicit SerialInterfaceCoro(underlying_handle_type* handle)
|
|
: handle(handle)
|
|
{
|
|
handle->RxCpltCallback = createCallback<T,
|
|
std::decay_t<decltype(*this)>,
|
|
&std::decay_t<decltype(*this)>::on_rx_complete>(*this);
|
|
|
|
handle->TxCpltCallback = createCallback<T,
|
|
std::decay_t<decltype(*this)>,
|
|
&std::decay_t<decltype(*this)>::on_tx_complete>(*this);
|
|
}
|
|
|
|
skullc::coro::Task<bool> transmit(std::uint8_t* data, const std::uint32_t data_len)
|
|
{
|
|
tx_awaiter_.emplace();
|
|
const auto status = transmit_(handle, data, data_len);
|
|
if (status != HAL_StatusTypeDef::HAL_OK)
|
|
co_return false;
|
|
|
|
co_await *tx_awaiter_;
|
|
tx_awaiter_ = std::nullopt;
|
|
|
|
co_return true;
|
|
}
|
|
|
|
template<typename Td, std::size_t N>
|
|
skullc::coro::Task<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));
|
|
}
|
|
|
|
skullc::coro::Task<bool> receive(std::uint8_t* data, const std::uint32_t data_len)
|
|
{
|
|
rx_awaiter_.emplace();
|
|
const auto status = receive_(handle, data, data_len);
|
|
if (status != HAL_StatusTypeDef::HAL_OK)
|
|
co_return false;
|
|
|
|
co_await *rx_awaiter_;
|
|
rx_awaiter_ = std::nullopt;
|
|
|
|
co_return true;
|
|
}
|
|
|
|
template<typename Td, std::size_t N>
|
|
skullc::coro::Task<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<std::size_t N>
|
|
skullc::coro::Task<std::array<std::uint8_t, N>> receive()
|
|
{
|
|
std::array<std::uint8_t, N> data;
|
|
data.fill(0);
|
|
|
|
co_await receive(data.data(), std::uint32_t(N));
|
|
|
|
co_return data;
|
|
}
|
|
};
|
|
#endif// SKULLC_WITH_CORO
|
|
|
|
#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(®, 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(®, 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(®, 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(®, 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);
|
|
}
|
|
|
|
inline HAL_StatusTypeDef uartTransmitIt(UART_HandleTypeDef* huart, std::uint8_t* data, const std::uint16_t size)
|
|
{
|
|
return HAL_UART_Transmit_IT(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>;
|
|
#ifdef SKULLC_WITH_CORO
|
|
using UartInterfaceCoro =
|
|
SerialInterfaceCoro<UART_HandleTypeDef, _Details::uartTransmitIt,
|
|
HAL_UART_Receive_IT>;
|
|
using UartInterfaceCoroDma =
|
|
SerialInterfaceCoro<UART_HandleTypeDef, _Details::uartTransmitDma,
|
|
HAL_UART_Receive_DMA>;
|
|
#endif// SKULLC_WITH_CORO
|
|
|
|
#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
|
|
|
|
#else
|
|
#warning "ST HAL included without SKULLC_USE_HAL_ST being defined."
|
|
#endif /* SKULLC_USE_HAL_ST */
|
|
|
|
#endif /* SKULLC_PERIPHERALS_HAL_ST_HPP_ */
|