commit 460f4e3c25072304dfd89d59edbd1621ca8acadb Author: Henrik Stickann <4376396-Mithradir@users.noreply.gitlab.com> Date: Mon May 9 21:19:06 2022 +0200 Move STM32 related code to sta-stm32-core repo diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/include/sta/stm32/can.hpp b/include/sta/stm32/can.hpp new file mode 100644 index 0000000..537a5f8 --- /dev/null +++ b/include/sta/stm32/can.hpp @@ -0,0 +1,129 @@ +/** + * @file + * @brief Implementation of CanController using STM32 HAL. + */ +#ifndef STA_STM32_CAN_HPP +#define STA_STM32_CAN_HPP + +/** + * @defgroup stm32CAN CAN + * @ingroup stm32 + * @brief STM32 CAN module. + * + * Check @ref stm32BuildConfig for configuration options. + */ + +#ifdef DOXYGEN +/** + * @def STA_STM32_CAN_ENABLE + * @brief Enable module. + * + * @ingroup stm32BuildConfig + */ +# define STA_STM32_CAN_ENABLE + +/** + * @def STA_STM32_CAN_GLOBAL + * @brief Create global CanBus object using this CAN instance. + * + * @ingroup stm32BuildConfig + */ +# define STA_STM32_CAN_GLOBAL +#endif // DOXYGEN + + +#include +#ifdef STA_STM32_CAN_ENABLE + +#include + +#include + + +namespace sta +{ + /** + * @brief Implementation of CanController interface using HAL. + * + * @ingroup stm32CAN + */ + class STM32CanController : public CanController + { + public: + static constexpr uint8_t MAX_FILTER_COUNT = 14; /**< Max number of filters */ + static constexpr uint8_t MAX_FIFO_COUNT = 2; /**< Max number of FIFOs */ + static constexpr uint8_t MAX_PAYLOAD_SIZE = 8; /**< Maximum payload size */ + + public: + /** + * @param handle CAN handle + */ + STM32CanController(CAN_HandleTypeDef * handle); + + /** + * @brief Enable RX pending interrupts. + */ + void enableRxInterrupts(); + + /** + * @brief Start CAN controller. + */ + void start(); + /** + * @brief Stop CAN controller. + */ + void stop(); + + + // RX/TX + // + + bool sendFrame(const CanTxHeader & header, const uint8_t * payload) override; + bool receiveFrame(uint8_t fifo, CanRxHeader * header, uint8_t * payload) override; + + uint32_t getRxFifoFlags() override; + + // RX Filter + // + + void configureFilter(uint8_t idx, const CanFilter & filter, bool active = false) override; + void enableFilter(uint8_t idx) override; + void disableFilter(uint8_t idx) override; + void clearFilters() override; + + private: + /** + * @brief Initialize filter settings. + */ + void initFilters(); + + private: + CAN_HandleTypeDef * handle_; /**< CAN handle */ + CAN_FilterTypeDef filters_[MAX_FILTER_COUNT]; /**< Filter settings */ + }; + + + +#ifdef STA_STM32_CAN_GLOBAL + /** + * @brief Global CAN instance. + * + * @ingroup stm32CAN + */ + extern STM32CanController CanBus; + + /** + * @brief Interrupt handler for pending RX frames. + * + * May be implemented by application. + * + * @ingroup stm32CAN + */ + void CanBus_RxPendingCallback(); +#endif // STA_STM32_CAN_GLOBAL +} // namespace sta + + +#endif // STA_STM32_CAN_ENABLE + +#endif // STA_STM32_CAN_HPP diff --git a/include/sta/stm32/clocks.hpp b/include/sta/stm32/clocks.hpp new file mode 100644 index 0000000..65e5d27 --- /dev/null +++ b/include/sta/stm32/clocks.hpp @@ -0,0 +1,82 @@ +/** + * @file + * @brief Helper macros for STM32 clock queries. + */ +#ifndef STA_STM32_CLOCKS_HPP +#define STA_STM32_CLOCKS_HPP + +/** + * @defgroup stm32 STM32 + * @brief Modules implemented for STM32 MCUs. + */ + +/** + * @defgroup stm32BuildConfig Build config + * @ingroup stm32 + * @brief Build configuration options. + */ + +/** + * @defgroup stm32Clocks Clocks + * @ingroup stm32 + * @brief STM32 clock queries. + * @{ + */ + +#include + +#include + + +/** + * @brief Get function returning PCLK frequency. + * + * @param n Index of peripheral clock + */ +#define STA_STM32_GET_PCLK_FREQ_FN(n) HAL_RCC_GetPCLK ## n ## Freq + + +// Internal helper for macro expansion +#define _STA_STM32_GET_PCLK_FREQ_FN(n) STA_STM32_GET_PCLK_FREQ_FN(n) +// Get instance to PCLK index map macro +#define _STA_STM32_PCLK_IDX_MAP(type, idx) STA_STM32_ ## type ## _ ## idx ## _PCLK_IDX +// Get HAL handle to PCLK index map macro +#define _STA_STM32_HANDLE_PCLK_IDX_MAP(handle) STA_STM32_ ## handle ## _PCLK_IDX + + +/** + * @brief Get function returning frequency of PCLK used by TIM. + * + * @param n TIM index + */ +#define STA_STM32_GET_TIM_PCLK_FREQ_FN(n) _STA_STM32_GET_PCLK_FREQ_FN(_STA_STM32_PCLK_IDX_MAP(TIM, n)) +/** + * @brief Get function returning frequency of PCLK used by SPI interface. + * + * @param n SPI interface index + */ +#define STA_STM32_GET_SPI_PCLK_FREQ_FN(n) _STA_STM32_GET_PCLK_FREQ_FN(_STA_STM32_PCLK_IDX_MAP(SPI, n)) +/** + * @brief Get function returning frequency of PCLK used by I2C interface. + * + * @param n I2C interface index + */ +#define STA_STM32_GET_I2C_PCLK_FREQ_FN(n) _STA_STM32_GET_PCLK_FREQ_FN(_STA_STM32_PCLK_IDX_MAP(I2C, n)) +/** + * @brief Get function returning frequency of PCLK used by USART interface. + * + * @param n USART interface index + */ +#define STA_STM32_GET_USART_PCLK_FREQ_FN(n) _STA_STM32_GET_PCLK_FREQ_FN(_STA_STM32_PCLK_IDX_MAP(USART, n)) + +/** + * @brief Get function returning frequency of PCLK used by HAL instance. + * + * @param handle Instance handle + */ +#define STA_STM32_GET_HANDLE_PCLK_FREQ_FN(handle) _STA_STM32_GET_PCLK_FREQ_FN(_STA_STM32_HANDLE_PCLK_IDX_MAP(handle)) + + +/** @} */ + +#endif // STA_STM32_CLOCKS_HPP diff --git a/include/sta/stm32/delay.hpp b/include/sta/stm32/delay.hpp new file mode 100644 index 0000000..fee499c --- /dev/null +++ b/include/sta/stm32/delay.hpp @@ -0,0 +1,68 @@ +/** + * @file + * @brief Delay functions. + */ +#ifndef STA_STM32_DELAY_HPP +#define STA_STM32_DELAY_HPP + +/** + * @defgroup stm32Delay Delay + * @ingroup stm32 + * @brief STM32 Delay module. + */ + +#ifdef DOXYGEN +/** + * @def STA_STM32_DELAY_ENABLE + * @brief Enable module. + * + * @ingroup stm32BuildConfig + */ +# define STA_STM32_DELAY_ENABLE + +/** + * @def STA_STM32_DELAY_US_TIM + * @brief 1 MHz TIM instance used by sta::delayUs. + * + * NOTE: TIM time base must be started before use of sta::delayUs by calling sta::initHAL. + * When using startup system task this is handled automatically. + * + * @ingroup stm32BuildConfig + */ +# define STA_STM32_DELAY_US_TIM +#endif // DOXYGEN + + +#include +#ifdef STA_STM32_DELAY_ENABLE + +#include + + +namespace sta +{ + /** + * @brief Millisecond delay. + * + * @param ms Milliseconds + * + * @ingroup stm32Delay + */ + void delayMs(uint32_t ms); + +#ifdef STA_STM32_DELAY_US_TIM + /** + * @brief Microsecond delay. + * + * @param us Microseconds + * + * @ingroup stm32Delay + */ + void delayUs(uint32_t us); +#endif // STA_STM32_DELAY_US_TIM +} // namespace sta + + +#endif // STA_STM32_DELAY_ENABLE + +#endif // STA_STM32_DELAY_HPP diff --git a/include/sta/stm32/gpio_pin.hpp b/include/sta/stm32/gpio_pin.hpp new file mode 100644 index 0000000..8fc5938 --- /dev/null +++ b/include/sta/stm32/gpio_pin.hpp @@ -0,0 +1,82 @@ +/** + * @file + * @brief Wrapper for STM32 GPIO pins. + */ +#ifndef STA_STM32_GPIO_PIN_HPP +#define STA_STM32_GPIO_PIN_HPP + +/** + * @defgroup stm32GPIO GPIO + * @ingroup stm32 + * @brief STM GPIO module. + */ + +#ifdef DOXYGEN +/** + * @def STA_STM32_GPIO_ENABLE + * @brief Enable module. + * + * @ingroup stm32BuildConfig + */ +# define STA_STM32_GPIO_ENABLE +#endif // DOXYGEN + + +#include +#ifdef STA_STM32_GPIO_ENABLE + +#include + +#include + + +namespace sta +{ + /** + * @brief Container for STM GPIO Pin data. + * + * @ingroup stm32GPIO + */ + class STM32GpioPin : public GpioPin + { + public: + STM32GpioPin(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 Interrupt trigger edge. + */ + enum class InterruptEdge + { + RISING, /**< Rising edge */ + FALLING, /**< Falling edge */ + BOTH /**< Rising and falling edge */ + }; + + bool isInterruptEdge(const STM32GpioPin & pin, InterruptEdge edge); +} // namespace sta + +/** + * @brief Create STM32GpioPin object from pin label. + * + * @param label Pin label + * + * @ingroup stm32GPIO + */ +#define STA_STM32_GPIO_PIN(label) sta::STM32GpioPin{label##_GPIO_Port, label##_Pin} + + +#endif // STA_STM32_GPIO_ENABLE + +#endif // STA_STM32_GPIO_PIN_HPP diff --git a/include/sta/stm32/hal.hpp b/include/sta/stm32/hal.hpp new file mode 100644 index 0000000..322b44e --- /dev/null +++ b/include/sta/stm32/hal.hpp @@ -0,0 +1,8 @@ +#ifndef STA_STM32_HAL_HPP +#define STA_STM32_HAL_HPP + +// Include STM32 HAL headers +#include + + +#endif // STA_STM32_HAL_HPP diff --git a/include/sta/stm32/init.hpp b/include/sta/stm32/init.hpp new file mode 100644 index 0000000..7b1f350 --- /dev/null +++ b/include/sta/stm32/init.hpp @@ -0,0 +1,20 @@ +/** + * @file + * @brief Global STM32 HAL initialization. + */ +#ifndef STA_STM32_INIT_HPP +#define STA_STM32_INIT_HPP + + +namespace sta +{ + /** + * @brief Initialize global HAL objects. + * + * @ingroup stm32 + */ + void initHAL(); +} // namespace sta + + +#endif // STA_STM32_INIT_HPP diff --git a/include/sta/stm32/mcu/STM32F411xE.hpp b/include/sta/stm32/mcu/STM32F411xE.hpp new file mode 100644 index 0000000..1d4060b --- /dev/null +++ b/include/sta/stm32/mcu/STM32F411xE.hpp @@ -0,0 +1,77 @@ +/** + * @brief Configuration for STM32F411xE family. + */ +#ifndef STA_STM32_MCU_STM32F411xE_HPP +#define STA_STM32_MCU_STM32F411xE_HPP + + +#ifndef STM32F411xE +# error "MCU config incompatible" +#endif // !STM32F411xE + + +#include + + +// Peripheral clock mappings +// + +// TIM to PCLK +#define STA_STM32_TIM_1_PCLK_IDX 2 +#define STA_STM32_TIM_2_PCLK_IDX 1 +#define STA_STM32_TIM_3_PCLK_IDX 1 +#define STA_STM32_TIM_4_PCLK_IDX 1 +#define STA_STM32_TIM_5_PCLK_IDX 1 +#define STA_STM32_TIM_9_PCLK_IDX 2 +#define STA_STM32_TIM_10_PCLK_IDX 2 +#define STA_STM32_TIM_11_PCLK_IDX 2 + +// SPI to PCLK +#define STA_STM32_SPI_1_PCLK_IDX 2 +#define STA_STM32_SPI_2_PCLK_IDX 1 +#define STA_STM32_SPI_3_PCLK_IDX 1 +#define STA_STM32_SPI_4_PCLK_IDX 2 +#define STA_STM32_SPI_5_PCLK_IDX 2 + +// I2C to PCLK +#define STA_STM32_I2C_1_PCLK_IDX 1 +#define STA_STM32_I2C_2_PCLK_IDX 1 +#define STA_STM32_I2C_3_PCLK_IDX 1 + +// USART to PCLK +#define STA_STM32_USART_1_PCLK_IDX 2 +#define STA_STM32_USART_2_PCLK_IDX 1 +#define STA_STM32_USART_6_PCLK_IDX 2 + + +// HAL handle mappings +// + +#define STA_STM32_htim1_PCLK_IDX STA_STM32_TIM_1_PCLK_IDX +#define STA_STM32_htim2_PCLK_IDX STA_STM32_TIM_2_PCLK_IDX +#define STA_STM32_htim3_PCLK_IDX STA_STM32_TIM_3_PCLK_IDX +#define STA_STM32_htim4_PCLK_IDX STA_STM32_TIM_4_PCLK_IDX +#define STA_STM32_htim5_PCLK_IDX STA_STM32_TIM_5_PCLK_IDX +#define STA_STM32_htim9_PCLK_IDX STA_STM32_TIM_9_PCLK_IDX +#define STA_STM32_htim10_PCLK_IDX STA_STM32_TIM_10_PCLK_IDX +#define STA_STM32_htim11_PCLK_IDX STA_STM32_TIM_11_PCLK_IDX + +// SPI to PCLK +#define STA_STM32_hspi1_PCLK_IDX STA_STM32_SPI_1_PCLK_IDX +#define STA_STM32_hspi2_PCLK_IDX STA_STM32_SPI_2_PCLK_IDX +#define STA_STM32_hspi3_PCLK_IDX STA_STM32_SPI_3_PCLK_IDX +#define STA_STM32_hspi4_PCLK_IDX STA_STM32_SPI_4_PCLK_IDX +#define STA_STM32_hspi5_PCLK_IDX STA_STM32_SPI_5_PCLK_IDX + +// I2C to PCLK +#define STA_STM32_hi2c1_PCLK_IDX STA_STM32_I2C_1_PCLK_IDX +#define STA_STM32_hi2c2_PCLK_IDX STA_STM32_I2C_2_PCLK_IDX +#define STA_STM32_h12c3_PCLK_IDX STA_STM32_I2C_3_PCLK_IDX + +// USART to PCLK +#define STA_STM32_husart1_PCLK_IDX STA_STM32_USART_1_PCLK_IDX +#define STA_STM32_husart2_PCLK_IDX STA_STM32_USART_2_PCLK_IDX +#define STA_STM32_husart6_PCLK_IDX STA_STM32_USART_6_PCLK_IDX + + +#endif // STA_STM32_MCU_STM32F411xE_HPP diff --git a/include/sta/stm32/mcu/STM32F413xx.hpp b/include/sta/stm32/mcu/STM32F413xx.hpp new file mode 100644 index 0000000..dc02bbe --- /dev/null +++ b/include/sta/stm32/mcu/STM32F413xx.hpp @@ -0,0 +1,16 @@ +/** + * @brief Configuration for STM32F413xx family. + */ +#ifndef STA_STM32_MCU_STM32F413xx_HPP +#define STA_STM32_MCU_STM32F413xx_HPP + + +#ifndef STM32F413xx +# error "MCU config incompatible" +#endif // !STM32F413xx + + +#include + + +#endif // STA_STM32_MCU_STM32F413xx_HPP diff --git a/include/sta/stm32/mcu/common.hpp b/include/sta/stm32/mcu/common.hpp new file mode 100644 index 0000000..afe4d55 --- /dev/null +++ b/include/sta/stm32/mcu/common.hpp @@ -0,0 +1,12 @@ +/** + * @brief Common configuration for STM32 MCUs + */ +#ifndef STA_STM32_MCU_COMMON_HPP +#define STA_STM32_MCU_COMMON_HPP + + +// TODO: Are all STM32 MCUs little endian? +#define STA_MCU_LITTLE_ENDIAN + + +#endif // STA_STM32_MCU_COMMON_HPP diff --git a/include/sta/stm32/spi.hpp b/include/sta/stm32/spi.hpp new file mode 100644 index 0000000..d68b262 --- /dev/null +++ b/include/sta/stm32/spi.hpp @@ -0,0 +1,133 @@ +/** + * @file + * @brief Implementations for SpiInterface and SpiDevice using STM32 HAL. + */ +#ifndef STA_STM32_SPI_HPP +#define STA_STM32_SPI_HPP + +/** + * @defgroup stm32SPI SPI + * @ingroup stm32 + * @brief STM32 SPI module. + */ + +#ifdef DOXYGEN +/** + * @def STA_STM32_SPI_ENABLE + * @brief Enable module. + * + * Requires **STM_GPIO** module. + * + * @ingroup stm32BuildConfig + */ +# define STA_STM32_SPI_ENABLE +#endif // DOXYGEN + + +#include +#ifdef STA_STM32_SPI_ENABLE + +#ifndef STA_STM32_GPIO_ENABLE +#error "STM32 GPIO module required" +#endif // !STA_STM32_GPIO_ENABLE + + +#include +#include + +#include +#include + + +namespace sta +{ + /** + * @ingroup stm32SPI + * @{ + */ + + + /** + * @brief Get peripheral clock frequency. + * + * @return Clock frequency + */ + using STM32SpiPCLKFreqFn = uint32_t (*)(); + + /** + * @brief Info related to STM SPI interface. + */ + struct STM32SpiInterfaceInfo + { + SPI_HandleTypeDef * handle; /**< Interface handle */ + STM32SpiPCLKFreqFn getPCLKFreq; /**< Getter for peripheral clock used by interface */ + }; + + + /** + * @brief Implementation of SpiInterface interface using STM32 HAL. + */ + class STM32SpiInterface : public SpiInterface + { + public: + /** + * @param info SPI interface info + * @param mutex Mutex object for managing access. Pass nullptr for no access control + */ + STM32SpiInterface(const STM32SpiInterfaceInfo & 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: + STM32SpiInterfaceInfo info_; /**< SPI interface info */ + }; + + + /** + * @brief Implementation of SpiDevice interface using STM32 HAL. + */ + class STM32SpiDevice : public SpiDevice + { + public: + /** + * @param intf SPI interface + * @param csPin Device CS pin + */ + STM32SpiDevice(STM32SpiInterface * intf, STM32GpioPin csPin); + + private: + STM32GpioPin csPin_; /**< Device CS pin */ + }; + + + /** @} */ +} // namespace sta + + +/** + * @brief Get SPI interface info struct for STM32 HAL handle. + * + * Requires STA_STM32__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 + * + * @ingroup halSPI + */ +#define STA_STM32_SPI_INFO(handle) sta::STM32SpiInterfaceInfo{&handle, STA_STM32_GET_HANDLE_PCLK_FREQ_FN(handle)} + + +#endif // STA_STM32_SPI_ENABLE + +#endif // STA_STM32_SPI_HPP diff --git a/include/sta/stm32/uart.hpp b/include/sta/stm32/uart.hpp new file mode 100644 index 0000000..97fb2dc --- /dev/null +++ b/include/sta/stm32/uart.hpp @@ -0,0 +1,66 @@ +/** + * @file + * @brief Implementation of UART using STM32 HAL. + */ +#ifndef STA_STM32_UART_HPP +#define STA_STM32_UART_HPP + +/** + * @defgroup stm32UART UART + * @ingroup stm32 + * @brief STM32 UART module. + */ + +#ifdef DOXYGEN +/** + * @def STA_STM32_UART_ENABLE + * @brief Enable module. + * + * @ingroup stm32BuildConfig + */ +# define STA_STM32_UART_ENABLE + +/** + * @def STA_STM32_UART_DEBUG_SERIAL + * @brief Create global sta::DebugSerial object using this HAL UART instance. + * + * @ingroup stm32BuildConfig + */ +# define STA_STM32_UART_DEBUG_SERIAL +#endif // DOXYGEN + + +#include +#ifdef STA_STM32_UART_ENABLE + +#include + +#include + + +namespace sta +{ + /** + * @brief Implementation of UART interface using HAL. + * + * @ingroup stm32UART + */ + class STM32UART : public UART + { + public: + /** + * @param handle UART handle + */ + STM32UART(UART_HandleTypeDef * handle); + + void write(const uint8_t * buffer, size_t size) override; + + private: + UART_HandleTypeDef * handle_; /**< UART handle */ + }; +} // namespace sta + + +#endif // STA_STM32_UART_ENABLE + +#endif // STA_STM32_UART_HPP diff --git a/library.json b/library.json new file mode 100644 index 0000000..5bbf196 --- /dev/null +++ b/library.json @@ -0,0 +1,11 @@ +{ + "owner" : "sta", + "name": "sta-stm32-core", + "version": "0.1.0", + "dependencies": [ + { + "url": "git@gitlab.com:sta-git/avionics/stm32/libs/sta-core.git", + "ref": "main" + } + ] +} \ No newline at end of file diff --git a/src/can.cpp b/src/can.cpp new file mode 100644 index 0000000..ec83936 --- /dev/null +++ b/src/can.cpp @@ -0,0 +1,210 @@ +#include +#ifdef STA_STM32_CAN_ENABLE + +#include +#include + + +namespace sta +{ + STM32CanController::STM32CanController(CAN_HandleTypeDef * handle) + : handle_{handle} + { + initFilters(); + } + + + void STM32CanController::enableRxInterrupts() + { + HAL_CAN_ActivateNotification(handle_, + CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_RX_FIFO1_MSG_PENDING + ); + } + + + void STM32CanController::start() + { + HAL_CAN_Start(handle_); + } + + void STM32CanController::stop() + { + HAL_CAN_Stop(handle_); + } + + + bool STM32CanController::sendFrame(const CanTxHeader & header, const uint8_t * payload) + { + STA_ASSERT_MSG(header.payloadLength <= 8, "CAN 2.0B payload size exceeded"); + + CAN_TxHeaderTypeDef halHeader; + + if (header.id.format == CanIdFormat::STD) + { + halHeader.StdId = header.id.sid & 0x7FF; + halHeader.IDE = CAN_ID_STD; + } + else + { + // Combine SID and EID + halHeader.ExtId = ((header.id.sid & 0x7FF) << 18) | (header.id.eid & 0x3FFFF); + halHeader.IDE = CAN_ID_EXT; + } + + halHeader.DLC = header.payloadLength; + + uint32_t mailbox; // Don't care + return (HAL_OK == HAL_CAN_AddTxMessage(handle_, &halHeader, const_cast(payload), &mailbox)); + } + + bool STM32CanController::receiveFrame(uint8_t fifo, CanRxHeader * header, uint8_t * payload) + { + // Check if message is available + if (HAL_CAN_GetRxFifoFillLevel(handle_, fifo) == 0) + return false; + + // Retrieve message + CAN_RxHeaderTypeDef halHeader; + HAL_CAN_GetRxMessage(handle_, fifo, &halHeader, payload); + + if (halHeader.IDE == CAN_ID_STD) + { + header->id.format = CanIdFormat::STD; + header->id.sid = halHeader.StdId; + header->id.eid = 0; + } + else + { + header->id.format = CanIdFormat::EXT; + // Separate SID and EID + header->id.sid = (halHeader.ExtId >> 18); + header->id.eid = halHeader.ExtId & 0x3FFFF; + } + // No conversion required for CAN 2B standard + header->payloadLength = halHeader.DLC; + header->timestamp = halHeader.Timestamp; + header->filter = halHeader.FilterMatchIndex; + + return true; + } + + uint32_t STM32CanController::getRxFifoFlags() + { + // + return (HAL_CAN_GetRxFifoFillLevel(handle_, CAN_RX_FIFO0) != 0) + | (HAL_CAN_GetRxFifoFillLevel(handle_, CAN_RX_FIFO1) != 0) << 1; + } + + + void STM32CanController::configureFilter(uint8_t idx, const CanFilter & filter, bool active /* = false */) + { + CAN_FilterTypeDef * config = &filters_[idx]; + + if (filter.type == CanFilterIdFormat::STD) + { + config->FilterIdHigh = 0; + config->FilterIdLow = filter.obj.sid & 0x7FF; + config->FilterMaskIdHigh = 0; + config->FilterMaskIdLow = filter.mask.sid & 0x7FF; + } + else + { + config->FilterIdHigh = ((filter.obj.sid & 0x7FF) << 2) | ((filter.obj.eid >> 16) & 0x3); + config->FilterIdLow = filter.obj.eid & 0xFFFF; + config->FilterMaskIdHigh = ((filter.mask.sid & 0x7FF) << 2) | ((filter.mask.eid >> 16) & 0x3); + config->FilterMaskIdLow = filter.mask.eid & 0xFFFF; + } + + config->FilterFIFOAssignment = filter.fifo; + config->FilterActivation = (active ? CAN_FILTER_ENABLE : CAN_FILTER_DISABLE); + + HAL_CAN_ConfigFilter(handle_, config); + } + + void STM32CanController::enableFilter(uint8_t idx) + { + CAN_FilterTypeDef * config = &filters_[idx]; + + config->FilterActivation = CAN_FILTER_ENABLE; + + HAL_CAN_ConfigFilter(handle_, config); + } + + void STM32CanController::disableFilter(uint8_t idx) + { + CAN_FilterTypeDef * config = &filters_[idx]; + + config->FilterActivation = CAN_FILTER_DISABLE; + + HAL_CAN_ConfigFilter(handle_, config); + } + + void STM32CanController::clearFilters() + { + for (uint32_t i = 0; i < MAX_FILTER_COUNT; ++i) + { + CAN_FilterTypeDef * config = &filters_[i]; + + // Only disable active filters + if (config->FilterActivation == CAN_FILTER_ENABLE) + { + config->FilterActivation = CAN_FILTER_DISABLE; + HAL_CAN_ConfigFilter(handle_, config); + } + } + } + + + void STM32CanController::initFilters() + { + for (uint32_t i = 0; i < MAX_FILTER_COUNT; ++i) + { + CAN_FilterTypeDef * config = &filters_[i]; + + config->FilterBank = i; + config->FilterMode = CAN_FILTERMODE_IDMASK; + config->FilterScale = CAN_FILTERSCALE_32BIT; + config->FilterActivation = CAN_FILTER_DISABLE; + config->SlaveStartFilterBank = MAX_FILTER_COUNT; + } + } +} // namespace sta + + +#ifdef STA_STM32_CAN_GLOBAL + +#include + +namespace sta +{ + STM32CanController CanBus(&STA_STM32_CAN_GLOBAL); + + STA_WEAK + void CanBus_RxPendingCallback() + {} +} // namespace sta + + +extern "C" +{ + void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) + { + if (hcan == &STA_STM32_CAN_GLOBAL) + { + sta::CanBus_RxPendingCallback(); + } + } + + void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) + { + if (hcan == &STA_STM32_CAN_GLOBAL) + { + sta::CanBus_RxPendingCallback(); + } + } +} + +#endif // STA_STM32_CAN_GLOBAL + + +#endif // STA_STM32_CAN_ENABLE diff --git a/src/delay.cpp b/src/delay.cpp new file mode 100644 index 0000000..9fef137 --- /dev/null +++ b/src/delay.cpp @@ -0,0 +1,66 @@ +#include +#ifdef STA_STM32_DELAY_ENABLE + +#include +#include + +#include +#include + + +namespace sta +{ + void delayMs(uint32_t ms) + { + HAL_Delay(ms); + } +} // namespace sta + + +#ifdef STA_STM32_DELAY_US_TIM + +#include + +namespace sta +{ + void delayUs(uint32_t us) + { + __HAL_TIM_SET_COUNTER(&STA_STM32_DELAY_US_TIM, 0); + while (__HAL_TIM_GET_COUNTER(&STA_STM32_DELAY_US_TIM) < us); + } + + + bool isValidDelayUsTIM() + { + // Get PCLK multiplier for TIM clock + uint32_t pclkMul = 1; + switch (STA_STM32_DELAY_US_TIM.Init.ClockDivision) + { + case TIM_CLOCKDIVISION_DIV1: + pclkMul = 1; + break; + case TIM_CLOCKDIVISION_DIV2: + pclkMul = 2; + break; + case TIM_CLOCKDIVISION_DIV4: + pclkMul = 4; + break; + default: + STA_ASSERT(false); + STA_UNREACHABLE(); + } + + // Calculate TIM clock frequency + uint32_t clkFreq = pclkMul * STA_STM32_GET_HANDLE_PCLK_FREQ_FN(STA_STM32_DELAY_US_TIM)(); + // Calculate update frequency based on prescaler value + uint32_t updateFreq = clkFreq / STA_STM32_DELAY_US_TIM.Init.Prescaler; + + // TIM must have at least microsecond precision (>= 1 MHz frequency) + return (updateFreq == 1000000); + } +} // namespace sta + +#endif // STA_STM32_DELAY_US_TIM + + +#endif // STA_STM32_DELAY_ENABLE diff --git a/src/gpio_pin.cpp b/src/gpio_pin.cpp new file mode 100644 index 0000000..cb52d1d --- /dev/null +++ b/src/gpio_pin.cpp @@ -0,0 +1,83 @@ +#include +#ifdef STA_STM32_GPIO_ENABLE + +#include +#include + + +namespace sta +{ + STM32GpioPin::STM32GpioPin(GPIO_TypeDef * port, uint16_t pin) + : port_{port}, pin_{pin} + { + STA_ASSERT(port != nullptr); + } + + void STM32GpioPin::setState(GpioPinState state) + { + HAL_GPIO_WritePin(port_, pin_, (state == GpioPinState::LOW) ? GPIO_PIN_RESET : GPIO_PIN_SET); + } + + GPIO_TypeDef * STM32GpioPin::getPort() const + { + return port_; + } + + uint16_t STM32GpioPin::getPin() const + { + return pin_; + } + + uint8_t STM32GpioPin::getIndex() const + { + return GPIO_GET_INDEX(port_); + } + + + bool isInterruptEdge(const STM32GpioPin & gpioPin, InterruptEdge edge) + { + uint32_t pin = gpioPin.getPin(); + + for (uint32_t i = 0; i < 8 * sizeof(pin); ++i) + { + uint32_t ioPos = 1U << i; + if (pin & ioPos) + { + // Check input mode + uint32_t mode = (gpioPin.getPort()->MODER >> (2U * i)) & GPIO_MODE; + if (mode != MODE_INPUT) + { + return false; + } + + // Is EXTI configured? + if (EXTI->IMR & ioPos) + { + bool rising = (EXTI->RTSR & ioPos); + bool falling = (EXTI->FTSR & ioPos); + + switch (edge) + { + case InterruptEdge::RISING: + return rising; + + case InterruptEdge::FALLING: + return falling; + + case InterruptEdge::BOTH: + return rising && falling; + + default: + STA_ASSERT(false); + STA_UNREACHABLE(); + } + } + } + } + + return false; + } +} // namespace sta + + +#endif // STA_STM32_GPIO_ENABLE diff --git a/src/init.cpp b/src/init.cpp new file mode 100644 index 0000000..7b59818 --- /dev/null +++ b/src/init.cpp @@ -0,0 +1,22 @@ +#include + +#include + +#ifdef STA_STM32_DELAY_US_TIM +#include +#endif // STA_STM32_DELAY_US_TIM + + +namespace sta +{ + void initHAL() + { +#ifdef STA_STM32_DELAY_US_TIM + // Validate TIM used for delayUs + extern bool isValidDelayUsTIM(); + STA_ASSERT(isValidDelayUsTIM()); + // Start timer base + HAL_TIM_Base_Start(&STA_STM32_DELAY_US_TIM); +#endif // STA_STM32_DELAY_US_TIM + } +} // namespace sta diff --git a/src/spi.cpp b/src/spi.cpp new file mode 100644 index 0000000..90243aa --- /dev/null +++ b/src/spi.cpp @@ -0,0 +1,181 @@ +#include +#ifdef STA_STM32_SPI_ENABLE + +#include +#include +#include + + +#ifdef STA_MCU_LITTLE_ENDIAN +# define STA_STM32_SPI_REVERSE_BIT_ORDER SpiBitOrder::MSB +#elif STA_MCU_BIG_ENDIAN +# define STA_STM32_SPI_REVERSE_BIT_ORDER SpiBitOrder::LSB +#else // !STA_MCU_LITTLE_ENDIAN && !STA_MCU_BIG_ENDIAN +# ifdef STA_STM32_SPI_REVERSE_BIT_ORDER +# warning "Internal STA_STM32_SPI_REVERSE_BIT_ORDER macro manually defined! Better now what you are doing!!!" +# else // !STA_STM32_SPI_REVERSE_BIT_ORDER +# error "Unknown endian-ness. Define STA_MCU_LITTLE_ENDIAN or STA_MCU_BIG_ENDIAN in " +# endif // !STA_STM32_SPI_REVERSE_BIT_ORDER +#endif // !STA_MCU_LITTLE_ENDIAN && !STA_MCU_BIG_ENDIAN + + +namespace sta +{ + static SpiSettings getSpiSettings(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; + } + + + STM32SpiInterface::STM32SpiInterface(const STM32SpiInterfaceInfo & info, Mutex * mutex /* = nullptr */) + : SpiInterface(mutex), info_{info} + { + STA_ASSERT(info.handle != nullptr); + STA_ASSERT(info.getPCLKFreq != nullptr); + } + + + void STM32SpiInterface::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 STM32SpiInterface::transfer16(uint16_t value) + { + 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_STM32_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 STM32SpiInterface::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 STM32SpiInterface::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 STM32SpiInterface::receive(uint8_t * buffer, size_t size) + { + STA_ASSERT(buffer != nullptr); + + HAL_SPI_Receive(info_.handle, buffer, size, HAL_MAX_DELAY); + } + + + void STM32SpiInterface::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 & STM32SpiInterface::settings() const + { + // Cache settings + static SpiSettings settings = getSpiSettings(info_.handle, info_.getPCLKFreq()); + + return settings; + } + + + + + STM32SpiDevice::STM32SpiDevice(STM32SpiInterface * intf, STM32GpioPin csPin) + : SpiDevice(intf, &csPin_), csPin_{csPin} + {} +} // namespace sta + + +#endif // STA_HAL_SPI_ENABLE diff --git a/src/uart.cpp b/src/uart.cpp new file mode 100644 index 0000000..3a99564 --- /dev/null +++ b/src/uart.cpp @@ -0,0 +1,43 @@ +#include +#ifdef STA_STM32_UART_ENABLE + +#include + + +namespace sta +{ + STM32UART::STM32UART(UART_HandleTypeDef * handle) + : handle_{handle} + { + STA_ASSERT(handle != nullptr); + } + + + void STM32UART::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_STM32_UART_DEBUG_SERIAL + +// Get extern declaration for DebugSerial because const namespace level variables have internal linkage by default +#include + +#include + +namespace sta +{ + STM32UART gStm32DebugSerial(&STA_STM32_UART_DEBUG_SERIAL); + + // Used by + PrintableUART DebugSerial(&gStm32DebugSerial); +} // namespace sta + +#endif // STA_STM32_UART_DEBUG_SERIAL + + +#endif // STA_STM32_UART_ENABLE