From 50fcec95821b1140cfec39086db549f2dd015be1 Mon Sep 17 00:00:00 2001 From: Brendan <2bndy5@gmail.com> Date: Fri, 9 Sep 2022 23:52:23 -0700 Subject: [PATCH] refactored patch - ran clang-format - conforms to utility/template - create how-to doc (w/ pics) - CMake support mimics PicoSDK implementation --- CMakeLists.txt | 4 + CMakeLists_required_to_stm32.txt | 8 - RF24.cpp | 6 +- RF24_config.h | 4 + docs/stm32cube.md | 48 +++++ images/STM32Cube-language-symbols.png | Bin 0 -> 14865 bytes images/STM32Cube-project-path-symbols.png | Bin 0 -> 4691 bytes images/STM32Cube-project-properties.png | Bin 0 -> 14072 bytes images/STM32Cube-project-symbols.png | Bin 0 -> 22044 bytes utility/STM32/CMakeLists.txt | 27 +++ utility/STM32/RF24_arch_config.h | 224 ++------------------- utility/STM32/compatibility.cpp | 14 ++ utility/STM32/compatibility.h | 16 ++ utility/STM32/{rf24_stm32.cpp => gpio.cpp} | 46 +---- utility/STM32/gpio.h | 139 +++++++++++++ utility/STM32/includes.h | 31 ++- utility/STM32/spi.cpp | 23 +++ utility/STM32/spi.h | 29 +++ 18 files changed, 347 insertions(+), 272 deletions(-) delete mode 100644 CMakeLists_required_to_stm32.txt create mode 100644 docs/stm32cube.md create mode 100644 images/STM32Cube-language-symbols.png create mode 100644 images/STM32Cube-project-path-symbols.png create mode 100644 images/STM32Cube-project-properties.png create mode 100644 images/STM32Cube-project-symbols.png create mode 100644 utility/STM32/CMakeLists.txt create mode 100644 utility/STM32/compatibility.cpp create mode 100644 utility/STM32/compatibility.h rename utility/STM32/{rf24_stm32.cpp => gpio.cpp} (56%) create mode 100644 utility/STM32/gpio.h create mode 100644 utility/STM32/spi.cpp create mode 100644 utility/STM32/spi.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bbb5ebc6..f67a8d1f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,10 @@ if (PICO_SDK_PATH) # If so, load the relevant CMakeLists-file but don't do anything else include(${CMAKE_CURRENT_LIST_DIR}/utility/rp2/CMakeLists.txt) return() +elseif(DEFINED STM32) + option(STM32_ARCH "The STM32 architecture family (eg STM32F1 or STM32F4)" "STM32F1") + include(${CMAKE_CURRENT_LIST_DIR}/utility/STM32/CMakeLists.txt) + return() endif() cmake_minimum_required(VERSION 3.15) diff --git a/CMakeLists_required_to_stm32.txt b/CMakeLists_required_to_stm32.txt deleted file mode 100644 index a0651b64f..000000000 --- a/CMakeLists_required_to_stm32.txt +++ /dev/null @@ -1,8 +0,0 @@ -######## -# RF24 # -######## -add_library(rf24 RF24.cpp utility/STM32/rf24_stm32.cpp) -target_compile_definitions(rf24 PUBLIC -DSTM32 -DSTM32F1) -target_include_directories(rf24 PRIVATE . ${STM32CubeHalIncludes} ../Core/Inc) -target_compile_options(rf24 PRIVATE -Ofast) -target_link_libraries(rf24 PRIVATE STM32CubeHal) diff --git a/RF24.cpp b/RF24.cpp index 018783a8f..139254425 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -593,7 +593,7 @@ void RF24::_init_obj() { // Use a pointer on the Arduino platform -#if defined(RF24_SPI_PTR) && !defined(RF24_RP2) +#if defined(RF24_SPI_PTR) && !defined(RF24_RP2) && !defined(STM32) _spi = &SPI; #endif // defined (RF24_SPI_PTR) @@ -1518,7 +1518,7 @@ uint8_t RF24::getDynamicPayloadSize(void) bool RF24::available(void) { - return available(NULL); + return available(nullptr); } /****************************************************************************/ @@ -1786,7 +1786,7 @@ bool RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) bool RF24::isAckPayloadAvailable(void) { - return available(NULL); + return available(nullptr); } /****************************************************************************/ diff --git a/RF24_config.h b/RF24_config.h index 7accb71cf..a0ed1d1a4 100644 --- a/RF24_config.h +++ b/RF24_config.h @@ -56,6 +56,10 @@ #include "utility/rp2/RF24_arch_config.h" #define sprintf_P sprintf +// STM32Cube IDE. +#elif !defined(ARDUINO) && defined(STM32) + #include "utility/STM32/RF24_arch_config.h" + #elif (!defined(ARDUINO)) // Any non-arduino device is handled via configure/Makefile // The configure script detects device and copies the correct includes.h file to /utility/includes.h // This behavior can be overridden by calling configure with respective parameters diff --git a/docs/stm32cube.md b/docs/stm32cube.md new file mode 100644 index 000000000..1217705e5 --- /dev/null +++ b/docs/stm32cube.md @@ -0,0 +1,48 @@ +# Using RF24 in the STM32Cube IDE + +The RF24 library can be integrated with any STM32Cube project. + +## Required Hardware + +The nRF24L01 radio requires 1 SPI bus and 2 GPIO pins for minimal usage. If using the radio's IRQ pin, then that will require an additional GPIO pin. + +You can use the IDE set up the necessary GPIO/SPI pins and have the IDE automatically generate the code to initialize the resources. + +The RF24 libaray manages the CSN pin without using the STM32HAL features. When setting up the SPI bus, there's no need to use the chips reserved CS pin associated with the selected SPI bus. The RF24 library allows using any GPIO output pin. + +## Basic Project Setup + +To integrate the RF24 library code into your STM32Cube project, follow these steps: + +1. Create a copy of the library in your project's "Drivers" directory. +2. Exclude all sub-directories except "STM32" in the RF24/utility/ directory. +3. Open your project's settings and add 2 symbols. + 1. right-click the project's root folder and select properties. + ![select project properties](../images/STM32Cube-project-properties.png) + 2. In the "C/C++ General" -> "Paths and Symbols" from the side menu. + ![select path and symbols](../images/STM32Cube-project-path-symbols.png) + 3. Select the "Symbols" tab in the main portion of the properties dialogue. + ![select symbols](../images/STM32Cube-project-symbols.png) + 4. Add the following variables (case-sensitive) to both "GNU C" and "GNU C++" categories: + - `STM32` (used by RF24_arch_config.h and RF24.cpp) + - `STM32yx` where `yx` describes the family of the desired STM32 chip. + For example: + + - If using an STM32F103 chip, then set a variable named `STM32F1` + - If using an STM32F411 chip, then set a variable named `STM32F4` + + ![add symbols](../images/STM32Cube-language-symbols.png) +4. Now you can use the RF24 library anywhere in your project like so: + + ```cpp + #include "RF24.h" + + RF24 radio = RF24(GPIO_PIN_3, GPIO_PIN_4); + if (!radio.begin()) { + return 1; // failed to init radio hardware + } + ``` + +## Using multiple SPI buses + +TODO diff --git a/images/STM32Cube-language-symbols.png b/images/STM32Cube-language-symbols.png new file mode 100644 index 0000000000000000000000000000000000000000..02274548b298cbcd12fc5ae42ab5892a0ff4e5e9 GIT binary patch literal 14865 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

u<%daXHpcCJ0cbw1E&eF^vN7=QP0QDh`q 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 literal 0 HcmV?d00001 diff --git a/utility/STM32/CMakeLists.txt b/utility/STM32/CMakeLists.txt new file mode 100644 index 000000000..6684af502 --- /dev/null +++ b/utility/STM32/CMakeLists.txt @@ -0,0 +1,27 @@ +# # Include this file if you want to use the RF24 library +# # in YOUR (STM32Cube) project. +# # NOTE: This is preliminary support for CMake (which is not typically used in STM32Cube IDE) + +cmake_minimum_required(VERSION 3.12) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +add_library(rf24 INTERFACE + ../../RF24.cpp + ${CMAKE_CURRENT_LIST_DIR}/RF24_arch_config.h + ${CMAKE_CURRENT_LIST_DIR}/spi.cpp + ${CMAKE_CURRENT_LIST_DIR}/gpio.cpp + ${CMAKE_CURRENT_LIST_DIR}/compatibility.cpp +) + +# STM32_ARCH option set in RF24/CMakeLists.txt +target_compile_definitions(rf24 PUBLIC -DSTM32 -D${STM32_ARCH}) + +# STM32CubeHalIncludes array/variable set in the project's CMakeLists.txt +target_include_directories(rf24 PRIVATE . ${STM32CubeHalIncludes} ../Core/Inc) + +target_compile_options(rf24 PRIVATE -Ofast) + +# STM32CubeHal library discovery in project's CMakeLists.txt +target_link_libraries(rf24 PRIVATE STM32CubeHal) diff --git a/utility/STM32/RF24_arch_config.h b/utility/STM32/RF24_arch_config.h index 51c3ad14f..277ed8623 100644 --- a/utility/STM32/RF24_arch_config.h +++ b/utility/STM32/RF24_arch_config.h @@ -18,11 +18,8 @@ * @{ */ -#ifndef RF24_UTILITY_TEMPLATE_RF24_ARCH_CONFIG_H_ -#define RF24_UTILITY_TEMPLATE_RF24_ARCH_CONFIG_H_ - -#include -#include +#ifndef RF24_UTILITY_STM32_RF24_ARCH_CONFIG_H_ +#define RF24_UTILITY_STM32_RF24_ARCH_CONFIG_H_ // define here the architecture that you are using //#define STM32F1 @@ -32,19 +29,11 @@ // builds the rf24 library with minimal requirements #define MINIMAL -#if defined(STM32F1) -#include "stm32f1xx_hal.h" -#include "stm32f1xx_hal_gpio.h" -#include "stm32f1xx_hal_spi.h" -#elif defined(STM32F3) -#include "stm32f3xx_hal.h" -#include "stm32f3xx_hal_gpio.h" -#include "stm32f3xx_hal_spi.h" -#elif defined(STM32F4) -#include "stm32f4xx_hal.h" -#include "stm32f4xx_hal_gpio.h" -#include "stm32f4xx_hal_spi.h" -#endif +#include +#include "compatibility.h" +#include "spi.h" +#include "gpio.h" +#include "includes.h" #undef SERIAL_DEBUG #ifdef SERIAL_DEBUG @@ -53,197 +42,22 @@ #define IF_SERIAL_DEBUG(x) #endif -#if !defined(PROGMEM) -#define PROGMEM -#endif - -#if !defined(_BV) -#define _BV(bit) (1<<(bit)) -#endif - -#if !defined(PSTR) -#define PSTR(x) (x) -#endif - #if !defined(printf_P) -#define printf_P(x) -#endif - -#if !defined(pgm_read_word) -#define pgm_read_word(p) (*(p)) -#endif - -#if !defined(pgm_read_byte) -#define pgm_read_byte(p) (*(p)) -#endif - -#if !defined(pgm_read_ptr) -#define pgm_read_ptr(p) (*(p)) -#endif - -typedef enum { - RF24_PA0 = 0, - RF24_PA1, - RF24_PA2, - RF24_PA3, - RF24_PA4, - RF24_PA5, - RF24_PA6, - RF24_PA7, - RF24_PA8, - RF24_PA9, - RF24_PA10, - RF24_PA11, - RF24_PA12, - RF24_PA13, - RF24_PA14, - RF24_PA15, - RF24_PB0, - RF24_PB1, - RF24_PB2, - RF24_PB3, - RF24_PB4, - RF24_PB5, - RF24_PB6, - RF24_PB7, - RF24_PB8, - RF24_PB9, - RF24_PB10, - RF24_PB11, - RF24_PB12, - RF24_PB13, - RF24_PB14, - RF24_PB15, - RF24_PC0, - RF24_PC1, - RF24_PC2, - RF24_PC3, - RF24_PC4, - RF24_PC5, - RF24_PC6, - RF24_PC7, - RF24_PC8, - RF24_PC9, - RF24_PC10, - RF24_PC11, - RF24_PC12, - RF24_PC13, - RF24_PC14, - RF24_PC15, - RF24_PD0, - RF24_PD1, - RF24_PD2, - RF24_PD3, - RF24_PD4, - RF24_PD5, - RF24_PD6, - RF24_PD7, - RF24_PD8, - RF24_PD9, - RF24_PD10, - RF24_PD11, - RF24_PD12, - RF24_PD13, - RF24_PD14, - RF24_PD15, - RF24_PE0, - RF24_PE1, - RF24_PE2, - RF24_PE3, - RF24_PE4, - RF24_PE5, - RF24_PE6, - RF24_PE7, - RF24_PE8, - RF24_PE9, - RF24_PE10, - RF24_PE11, - RF24_PE12, - RF24_PE13, - RF24_PE14, - RF24_PE15, - RF24_PF0, - RF24_PF1, - RF24_PF2, - RF24_PF3, - RF24_PF4, - RF24_PF5, - RF24_PF6, - RF24_PF7, - RF24_PF8, - RF24_PF9, - RF24_PF10, - RF24_PF11, - RF24_PF12, - RF24_PF13, - RF24_PF14, - RF24_PF15, - RF24_PG0, - RF24_PG1, - RF24_PG2, - RF24_PG3, - RF24_PG4, - RF24_PG5, - RF24_PG6, - RF24_PG7, - RF24_PG8, - RF24_PG9, - RF24_PG10, - RF24_PG11, - RF24_PG12, - RF24_PG13, - RF24_PG14, - RF24_PG15, -} rf24_pin; - -#if !defined(HIGH) -#define HIGH true + #define printf_P(x) #endif -#if !defined(LOW) -#define LOW false -#endif - -#if !defined(INPUT) -#define INPUT GPIO_MODE_INPUT -#endif - -#if !defined(OUTPUT) -#define OUTPUT GPIO_MODE_OUTPUT_PP -#endif - -#if !defined(digitalWrite) -void digitalWrite(uint8_t pin, uint8_t value); -#endif - -#if !defined(pinMode) -void pinMode(uint8_t pin, uint8_t direction); -#endif - -#if !defined(millis) -#define millis HAL_GetTick -#endif - -#if !defined(delayMicroseconds) -void delayMicroseconds(uint32_t usecs); -#endif - -#if !defined(delay) -#define delay(msecs) delayMicroseconds(1000*msecs) -#endif - -class RF24_SPI { -public: - RF24_SPI(); - void begin(); - void begin(SPI_HandleTypeDef* hspi); - uint8_t transfer(uint8_t data_to_send); -private: - SPI_HandleTypeDef* _hspi; -}; +#define _BV(bit) (1 << (bit)) +#define PROGMEM +#define PSTR(x) (x) +#define PRIPSTR "%s" +#define pgm_read_word(p) (*(const unsigned char*)(p)) +#define pgm_read_byte(p) (*(const unsigned short*)(p)) +#define pgm_read_ptr(p) (*(const void*)(p)) -extern RF24_SPI _SPI; +#define delayMicroseconds(usecs) __usleep(usecs) +#define delay(msecs) HAL_Delay(msecs) +#define millis HAL_GetTick /**@}*/ -#endif // RF24_UTILITY_TEMPLATE_RF24_ARCH_CONFIG_H_ +#endif // RF24_UTILITY_STM32_RF24_ARCH_CONFIG_H_ diff --git a/utility/STM32/compatibility.cpp b/utility/STM32/compatibility.cpp new file mode 100644 index 000000000..6db823eb3 --- /dev/null +++ b/utility/STM32/compatibility.cpp @@ -0,0 +1,14 @@ +#include "compatibility.h" + +uint32_t rf24_get_time_us() +{ + return 1000 * HAL_GetTick() + 1000 - (SysTick->VAL / (SystemCoreClock / 1000000)); +}; + +void __usleep(int32_t usecs) +{ + uint32_t now = rf24_get_time_us(); + uint32_t blocked_until = now + usecs; + while (blocked_until > rf24_get_time_us()) { + } +}; diff --git a/utility/STM32/compatibility.h b/utility/STM32/compatibility.h new file mode 100644 index 000000000..1f174b565 --- /dev/null +++ b/utility/STM32/compatibility.h @@ -0,0 +1,16 @@ +/** + * @file compatibility.h + * declaration for timing helper functions + */ + +#ifndef RF24_UTILITY_STM32_COMPATIBLITY_H_ +#define RF24_UTILITY_STM32_COMPATIBLITY_H_ + +#include +#include "includes.h" + +uint32_t rf24_get_time_us(); + +void __usleep(int32_t usecs); + +#endif // RF24_UTILITY_STM32_COMPATIBLITY_H_ diff --git a/utility/STM32/rf24_stm32.cpp b/utility/STM32/gpio.cpp similarity index 56% rename from utility/STM32/rf24_stm32.cpp rename to utility/STM32/gpio.cpp index 28f6c8ffc..64407f99b 100644 --- a/utility/STM32/rf24_stm32.cpp +++ b/utility/STM32/gpio.cpp @@ -1,7 +1,7 @@ -#include "RF24_arch_config.h" +#include "gpio.h" - -static GPIO_TypeDef* decode_pin(uint8_t pin, uint16_t* decoded_pin) { +GPIO_TypeDef* decode_pin(uint8_t pin, uint16_t* decoded_pin) +{ GPIO_TypeDef* port; *decoded_pin = 1 << (pin % 16); @@ -32,12 +32,12 @@ static GPIO_TypeDef* decode_pin(uint8_t pin, uint16_t* decoded_pin) { break; #endif #if defined(GPIOF) - case 5: + case 5: port = GPIOF; break; #endif #if defined(GPIOG) - case 6: + case 6: port = GPIOG; break; #endif @@ -48,12 +48,8 @@ static GPIO_TypeDef* decode_pin(uint8_t pin, uint16_t* decoded_pin) { return port; } -static uint32_t rf24_get_time_us() +void pinMode(uint8_t pin, uint8_t direction) { - return 1000 * HAL_GetTick() + 1000 - (SysTick->VAL / (SystemCoreClock / 1000000)); -} - -void pinMode(uint8_t pin, uint8_t direction) { uint16_t decoded_pin; GPIO_TypeDef* port = decode_pin(pin, &decoded_pin); @@ -65,35 +61,9 @@ void pinMode(uint8_t pin, uint8_t direction) { HAL_GPIO_Init(port, &config); } -void digitalWrite(uint8_t pin, uint8_t value) { +void digitalWrite(uint8_t pin, uint8_t value) +{ uint16_t decoded_pin; GPIO_TypeDef* port = decode_pin(pin, &decoded_pin); HAL_GPIO_WritePin(port, decoded_pin, value ? GPIO_PIN_SET : GPIO_PIN_RESET); } - -void delayMicroseconds(uint32_t usecs) -{ - uint32_t now = rf24_get_time_us(); - uint32_t blocked_until = now + usecs; - while (blocked_until > rf24_get_time_us()) {} -} - -RF24_SPI::RF24_SPI() { - _hspi = nullptr; -} - -void RF24_SPI::begin() { - HAL_SPI_Init(_hspi); -} - -void RF24_SPI::begin(SPI_HandleTypeDef* hspi) { - _hspi = hspi; -} - -uint8_t RF24_SPI::transfer(uint8_t data_to_send) { - uint8_t rx_data; - HAL_SPI_TransmitReceive(_hspi, &data_to_send, &rx_data, 1, HAL_MAX_DELAY); - return rx_data; -} - -RF24_SPI _SPI; diff --git a/utility/STM32/gpio.h b/utility/STM32/gpio.h new file mode 100644 index 000000000..be180fc0a --- /dev/null +++ b/utility/STM32/gpio.h @@ -0,0 +1,139 @@ +/** + * @file gpio.h + * Class declaration for GPIO helper files + */ + +#ifndef RF24_UTILITY_STM32_GPIO_H_ +#define RF24_UTILITY_STM32_GPIO_H_ + +#include +#include "includes.h" + +#define INPUT GPIO_MODE_INPUT +#define OUTPUT GPIO_MODE_OUTPUT_PP +#define HIGH true +#define LOW false + +typedef enum +{ + RF24_PA0 = 0, + RF24_PA1, + RF24_PA2, + RF24_PA3, + RF24_PA4, + RF24_PA5, + RF24_PA6, + RF24_PA7, + RF24_PA8, + RF24_PA9, + RF24_PA10, + RF24_PA11, + RF24_PA12, + RF24_PA13, + RF24_PA14, + RF24_PA15, + RF24_PB0, + RF24_PB1, + RF24_PB2, + RF24_PB3, + RF24_PB4, + RF24_PB5, + RF24_PB6, + RF24_PB7, + RF24_PB8, + RF24_PB9, + RF24_PB10, + RF24_PB11, + RF24_PB12, + RF24_PB13, + RF24_PB14, + RF24_PB15, + RF24_PC0, + RF24_PC1, + RF24_PC2, + RF24_PC3, + RF24_PC4, + RF24_PC5, + RF24_PC6, + RF24_PC7, + RF24_PC8, + RF24_PC9, + RF24_PC10, + RF24_PC11, + RF24_PC12, + RF24_PC13, + RF24_PC14, + RF24_PC15, + RF24_PD0, + RF24_PD1, + RF24_PD2, + RF24_PD3, + RF24_PD4, + RF24_PD5, + RF24_PD6, + RF24_PD7, + RF24_PD8, + RF24_PD9, + RF24_PD10, + RF24_PD11, + RF24_PD12, + RF24_PD13, + RF24_PD14, + RF24_PD15, + RF24_PE0, + RF24_PE1, + RF24_PE2, + RF24_PE3, + RF24_PE4, + RF24_PE5, + RF24_PE6, + RF24_PE7, + RF24_PE8, + RF24_PE9, + RF24_PE10, + RF24_PE11, + RF24_PE12, + RF24_PE13, + RF24_PE14, + RF24_PE15, + RF24_PF0, + RF24_PF1, + RF24_PF2, + RF24_PF3, + RF24_PF4, + RF24_PF5, + RF24_PF6, + RF24_PF7, + RF24_PF8, + RF24_PF9, + RF24_PF10, + RF24_PF11, + RF24_PF12, + RF24_PF13, + RF24_PF14, + RF24_PF15, + RF24_PG0, + RF24_PG1, + RF24_PG2, + RF24_PG3, + RF24_PG4, + RF24_PG5, + RF24_PG6, + RF24_PG7, + RF24_PG8, + RF24_PG9, + RF24_PG10, + RF24_PG11, + RF24_PG12, + RF24_PG13, + RF24_PG14, + RF24_PG15, +} rf24_pin; + +GPIO_TypeDef* decode_pin(uint8_t pin, uint16_t* decoded_pin); + +void digitalWrite(uint8_t pin, uint8_t value); + +void pinMode(uint8_t pin, uint8_t direction); + +#endif // RF24_UTILITY_STM32_GPIO_H_ diff --git a/utility/STM32/includes.h b/utility/STM32/includes.h index 5f6dd93da..30b3ef288 100644 --- a/utility/STM32/includes.h +++ b/utility/STM32/includes.h @@ -1,23 +1,18 @@ -/** - * @file includes.h - * Configuration defines for RF24/Linux - */ - -/** - * Example of includes.h for RF24 Linux portability - * - * @defgroup Porting_Includes Porting: Includes - * @{ - */ - #ifndef RF24_UTILITY_STM32_INCLUDES_H_ #define RF24_UTILITY_STM32_INCLUDES_H_ -/** - * Load the correct configuration for this platform - */ -#include "RF24_arch_config.h" - -/**@}*/ +#if defined(STM32F1) + #include "stm32f1xx_hal.h" + #include "stm32f1xx_hal_gpio.h" + #include "stm32f1xx_hal_spi.h" +#elif defined(STM32F3) + #include "stm32f3xx_hal.h" + #include "stm32f3xx_hal_gpio.h" + #include "stm32f3xx_hal_spi.h" +#elif defined(STM32F4) + #include "stm32f4xx_hal.h" + #include "stm32f4xx_hal_gpio.h" + #include "stm32f4xx_hal_spi.h" +#endif #endif // RF24_UTILITY_STM32_INCLUDES_H_ diff --git a/utility/STM32/spi.cpp b/utility/STM32/spi.cpp new file mode 100644 index 000000000..a2e4e10bf --- /dev/null +++ b/utility/STM32/spi.cpp @@ -0,0 +1,23 @@ +#include "spi.h" + +RF24_SPI::RF24_SPI() : _hspi(nullptr) +{ +} + +void RF24_SPI::begin() +{ + HAL_SPI_Init(_hspi); +} + +void RF24_SPI::begin(SPI_HandleTypeDef* hspi) +{ + _hspi = hspi; + RF24_SPI::begin(); +} + +uint8_t RF24_SPI::transfer(uint8_t data_to_send) +{ + uint8_t rx_data; + HAL_SPI_TransmitReceive(_hspi, &data_to_send, &rx_data, 1, HAL_MAX_DELAY); + return rx_data; +} diff --git a/utility/STM32/spi.h b/utility/STM32/spi.h new file mode 100644 index 000000000..d1d03e093 --- /dev/null +++ b/utility/STM32/spi.h @@ -0,0 +1,29 @@ +/** + * @file spi.h + * Class declaration for SPI helper files + */ + +#ifndef RF24_UTILITY_STM32_SPI_H_ +#define RF24_UTILITY_STM32_SPI_H_ + +#include +#include "includes.h" + +class RF24_SPI +{ +public: + RF24_SPI(); + void begin(); + void begin(SPI_HandleTypeDef* hspi); + uint8_t transfer(uint8_t data_to_send); + +private: + SPI_HandleTypeDef* _hspi; +}; + +#define _SPI RF24_SPI +#define RF24_SPI_PTR + +static RF24_SPI spi; + +#endif // RF24_UTILITY_STM32_SPI_H_