/* * 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 #include #ifdef SKULLC_WITH_CORO #include "skullc/coro/peripheral_awaiters.hpp" #endif #define USE_DELAY_US namespace Peripherals { namespace Hal { namespace St { template using IsrCallbackFn = void (*)(Origin*); template IsrCallbackFn 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 bool transmit(std::array& array) { static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large."); return transmit(reinterpret_cast(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 bool receive(std::array& array) { static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large."); return receive(reinterpret_cast(array.data()), std::uint32_t(N)); } }; template 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 bool transmit(std::array& array) { static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large."); return transmit_(reinterpret_cast(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 bool receive(std::array& array) { static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large."); return receive(reinterpret_cast(array.data()), std::uint32_t(N)); } }; #ifdef SKULLC_WITH_CORO template 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 rx_awaiter_ = std::nullopt; std::optional tx_awaiter_ = std::nullopt; public: using underlying_handle_type = T; underlying_handle_type* handle; SerialInterfaceCoro() = delete; template explicit SerialInterfaceCoro(underlying_handle_type* handle) : handle(handle) { handle->RxCpltCallback = createCallback, &std::decay_t::on_rx_complete>(*this); handle->TxCpltCallback = createCallback, &std::decay_t::on_tx_complete>(*this); } skullc::coro::Task 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 skullc::coro::Task transmit(std::array& array) { static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large."); return transmit(reinterpret_cast(array.data()), std::uint32_t(N)); } skullc::coro::Task 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 skullc::coro::Task receive(std::array& array) { static_assert(sizeof(Td) == sizeof(std::uint8_t), "Data is not a byte large."); return receive(reinterpret_cast(array.data()), std::uint32_t(N)); } template skullc::coro::Task> receive() { std::array 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; 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; using UartInterfaceDMA = SerialInterfaceAsync; #ifdef SKULLC_WITH_CORO using UartInterfaceCoro = SerialInterfaceCoro; using UartInterfaceCoroDma = SerialInterfaceCoro; #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 bool transmit(std::array& array) { static_assert(sizeof(T) == sizeof(std::uint8_t), "Data is not a byte large."); return transmit(reinterpret_cast(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_ */