diff --git a/libraries/SPI/examples/SPIMaster/SPIMaster.ino b/libraries/SPI/examples/SPIMaster/SPIMaster.ino new file mode 100644 index 0000000000..e42552156e --- /dev/null +++ b/libraries/SPI/examples/SPIMaster/SPIMaster.ino @@ -0,0 +1,205 @@ +/* + SPI Master + + This example implements a SPI Master device for the SPI Slave example. + The data is transmitted to the SPI Slave and its response received back. + + Every 1000ms the LED state toggles and the latest received data is printed + to the Serial port + + The circuit: + * A LED to show a heartbeat signal + * CS - to digital pin PA4 (SS pin) + * SCK - to digital pin PA5 (SCK pin) + * MISO - to digital pin PA6 (MISO pin) + * MOSI - to digital pin PA7 (MOSI pin) + + Default SPI pin definitions can be found at variants/STM32XXxx//variant_generic.h + + created 29 Dec 2021 + by brainelectronics +*/ + +#include + +// PC13 on the Bluepill board +#define DEBUG_LED PC13 + +// comment this line to disable debug output on Serial +#define DEBUG_OUTPUT + +// amount of data to be sent to the SPI Slave device +#define BUFFER_SIZE 8 + +uint8_t TX_Buffer[BUFFER_SIZE] = {0}; +uint8_t RX_Buffer[BUFFER_SIZE] = {0}; +uint8_t up_counter = 0; //< data to be transmitted + +uint32_t prevTime = 0; //< previous timestamp of printing received data +uint32_t interval = 1000; //< [ms] interval at which to print received data + +spi_t _spi; + +void setup() { + // put your setup code here, to run once: + pinMode(DEBUG_LED, OUTPUT); + + // setup serial communication at 115200 baud + Serial.begin(115200); + + // setup SPI interface + setup_spi(); +} + +void setup_spi() { + uint32_t clk = SPI_SPEED_CLOCK_DEFAULT; + + // communicating at full clock speed might not work + // check the documentation at system/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_spi.c + clk = spi_getClkFreq(&_spi) / SPI_CLOCK_DIV16; + +#ifdef DEBUG_OUTPUT + Serial.printf("PCLK: %d Hz\n", HAL_RCC_GetPCLK2Freq()); + // e.g. 48.000.000 Hz if running on the internal oscillator + Serial.printf("SPI CLK: %d Hz\n", clk); + // becomes 3.000.000 Hz in case PCLK is 48MHz and SPI_CLOCK_DIV16 is used +#endif + + // set the default CS pin as an output + pinMode(PIN_SPI_SS, OUTPUT); + + _spi.pin_mosi = digitalPinToPinName(PIN_SPI_MOSI); + _spi.pin_miso = digitalPinToPinName(PIN_SPI_MISO); + _spi.pin_sclk = digitalPinToPinName(PIN_SPI_SCK); + _spi.pin_ssel = NC; // in send_and_receive_data function + + _spi.mode = SPI_MASTER; //< master mode + + _spi.handle.State = HAL_SPI_STATE_RESET; + + spi_init(&_spi, clk, SPI_MODE_0, MSBFIRST); + +#ifdef DEBUG_OUTPUT + Serial.printf("SPI State: %d\n", _spi.handle.State); + // should be in state "1": Peripheral Initialized and ready for use +#endif +} + +void loop() { + uint32_t now = millis(); + + if (now > (prevTime + interval)) + { + // save the last time the data has been printed + prevTime = now; + + // toggle the LED state + digitalWrite(DEBUG_LED, !digitalRead(DEBUG_LED)); + +#ifdef DEBUG_OUTPUT + Serial.printf("HAL_SPI_GetError: %d\n", HAL_SPI_GetError(&(_spi.handle))); + Serial.printf("SPI State: %d\n", _spi.handle.State); +#endif + + // increment counter. This value will be sent to the SPI slave device + up_counter++; + + // reset value to zero, just to be save + if (up_counter >= 255) { + up_counter = 0; + } + + // to only send data to the slave device use this function + // send_data_to_slave(up_counter); + + // to send data and read the slave device response use this function + send_and_receive_data(up_counter); + } +} + +void send_data_to_slave(uint8_t data) { + uint8_t tmp_data_rx = 0; + + // take the SS pin low to select the chip + digitalWrite(PIN_SPI_SS, LOW); + + // transfer the given data 8 times in a row to slave -> Slave BUFFER_SIZE = 8 + // [1, 1, 1, 1, 1, 1, 1, 1] + for (uint8_t i = 0; i < 8; i++) + { + spi_transfer(&_spi, + &data, + &tmp_data_rx, + sizeof(uint8_t), + SPI_TRANSFER_TIMEOUT, + SPI_TRANSMITONLY); + } + + // take the SS pin high to de-select the chip + digitalWrite(PIN_SPI_SS, HIGH); +} + +void send_and_receive_data(uint8_t data) { + uint8_t tmp_data_rx = 0; + uint8_t tmp_data_tx = 0; + + for (uint8_t i = 0; i < BUFFER_SIZE; i++) + { + TX_Buffer[i] = data + i; + } + // e.g. [1, 2, 3, 4, 5, 6, 7, 8] if data = 1 + + Serial.print("Send data to Slave: "); + for (uint8_t i = 0; i < BUFFER_SIZE; i++) + { + Serial.printf("%d, ", TX_Buffer[i]); + } + Serial.println(); + + // take the CS pin low to select the Slave device + digitalWrite(PIN_SPI_SS, LOW); + + for (uint8_t i = 0; i < BUFFER_SIZE; i++) + { + tmp_data_tx = TX_Buffer[i]; + spi_transfer(&_spi, + &tmp_data_tx, + &tmp_data_rx, + sizeof(uint8_t), + SPI_TRANSFER_TIMEOUT, + SPI_TRANSMITONLY); + } + + // Serial print on slave requires approx. 1 ms + // wait for that amount of time until dummy data is sent to the slave in + // order to provide a CLK signal for the response of the slave device +#ifdef DEBUG_OUTPUT + delay(2); +#else + delay(1); +#endif + + tmp_data_tx = 0x0; // dummy data + for (uint8_t i = 0; i < BUFFER_SIZE; i++) + { + spi_transfer(&_spi, + &tmp_data_tx, + &tmp_data_rx, + sizeof(uint8_t), + SPI_TRANSFER_TIMEOUT, + SPI_TRANSMITRECEIVE); + RX_Buffer[i] = tmp_data_rx; + } + + // take the SS pin high to de-select the slave device again + digitalWrite(PIN_SPI_SS, HIGH); + + Serial.print("Received data from Slave: "); + for (uint8_t i = 0; i < BUFFER_SIZE; i++) + { + Serial.printf("%d, ", RX_Buffer[i]); + RX_Buffer[i] = 0; // clear buffer entry + } + // e.g. [2, 3, 4, 5, 6, 7, 8, 9] if data = 1 + Serial.println(); +} diff --git a/libraries/SPI/examples/SPISlave/SPISlave.ino b/libraries/SPI/examples/SPISlave/SPISlave.ino new file mode 100644 index 0000000000..b09fc6965d --- /dev/null +++ b/libraries/SPI/examples/SPISlave/SPISlave.ino @@ -0,0 +1,167 @@ +/* + SPI Slave + + This example implements a SPI Slave device for the SPI Master example. + The data is received from the SPI Master, incremented by one and sent back. + + Every 1000ms the LED state toggles and the latest received data is printed + to the Serial port + + The circuit: + * A LED to show a heartbeat signal + * CS - to digital pin PA4 (SS pin) + * SCK - to digital pin PA5 (SCK pin) + * MISO - to digital pin PA6 (MISO pin) + * MOSI - to digital pin PA7 (MOSI pin) + + Default SPI pin definitions can be found at variants/STM32XXxx//variant_generic.h + + created 29 Dec 2021 + by brainelectronics +*/ + +#include + +// PC13 on the Bluepill board +#define DEBUG_LED PC13 + +// comment this line to disable debug output on Serial +#define DEBUG_OUTPUT + +// amount of data being sent by the SPI Master device +#define BUFFER_SIZE 8 + +uint8_t RX_Buffer[BUFFER_SIZE] = {0}; + +uint32_t prevTime = 0; //< previous timestamp of printing received data +uint32_t interval = 1000; //< [ms] interval at which to print received data + +volatile uint32_t tmpCounterRx = 0, tmpCounterTx = 0; + +spi_t _spi; + +void setup() { + // put your setup code here, to run once: + pinMode(DEBUG_LED, OUTPUT); + + // setup serial communication at 115200 baud + Serial.begin(115200); + + // wait for 1 second in order to let the Master be ready before the slave + delay(1000); + + // setup SPI interface + setup_spi(); +} + +void setup_spi() { + uint32_t clk = SPI_SPEED_CLOCK_DEFAULT; + + // communicating at full clock speed might not work + // check the documentation at system/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_spi.c + clk = spi_getClkFreq(&_spi) / SPI_CLOCK_DIV16; + +#ifdef DEBUG_OUTPUT + Serial.printf("PCLK: %d Hz\n", HAL_RCC_GetPCLK2Freq()); + // e.g. 48.000.000 Hz if running on the internal oscillator + Serial.printf("SPI CLK: %d Hz\n", clk); + // becomes 3.000.000 Hz in case PCLK is 48MHz and SPI_CLOCK_DIV16 is used +#endif + + // set the default CS pin as an input + pinMode(PIN_SPI_SS, INPUT); + + _spi.pin_mosi = digitalPinToPinName(PIN_SPI_MOSI); + _spi.pin_miso = digitalPinToPinName(PIN_SPI_MISO); + _spi.pin_sclk = digitalPinToPinName(PIN_SPI_SCK); + _spi.pin_ssel = NC; // set later manually + + _spi.mode = SPI_SLAVE; //< slave mode + + _spi.handle.State = HAL_SPI_STATE_RESET; + + spi_init(&_spi, clk, SPI_MODE_0, MSBFIRST); + +#ifdef DEBUG_OUTPUT + Serial.printf("SPI State: %d\n", _spi.handle.State); + // should be in state "1": Peripheral Initialized and ready for use +#endif + + // attach an interrupt as last step to avoid calls before a full SPI setup + + // to receive only data from a Master, use the "receive_data" function + // attachInterrupt(PIN_SPI_SS, receive_data, FALLING); + + // to receive data and transmit a response back to the Master use this + attachInterrupt(PIN_SPI_SS, receive_and_respond_data, FALLING); +} + +void loop() { + uint32_t now = millis(); + + if (now > (prevTime + interval)) + { + // save the last time the data has been printed + prevTime = now; + + // toggle the LED state + digitalWrite(DEBUG_LED, !digitalRead(DEBUG_LED)); + +#ifdef DEBUG_OUTPUT + Serial.printf("HAL_SPI_GetError: %d\n", HAL_SPI_GetError(&(_spi.handle))); + Serial.printf("SPI State: %d\n", _spi.handle.State); + Serial.printf("RX Counter: %d\n", tmpCounterRx); + Serial.printf("TX Counter: %d\n", tmpCounterTx); +#endif + + // print the data in the received buffer in DEC format + for (uint8_t i = 0; i < BUFFER_SIZE; i++) + { + Serial.printf("%d, ", RX_Buffer[i]); + RX_Buffer[i] = 0; // clear buffer entry + } + Serial.println(); + } +} + +void receive_data() +{ + // put received data from SPI into the RX_Buffer + // return from function if no data is received within 10 ticks (10 ms) + HAL_SPI_Receive(&(_spi.handle), RX_Buffer, sizeof(RX_Buffer), 10); + tmpCounterRx++; + + // this is for demo purpose only, try to keep IRQ functions short and quick +#ifdef DEBUG_OUTPUT + // print received data directly after receiving it + for (uint8_t i = 0; i < BUFFER_SIZE; i++) + { + Serial.printf("%d, ", RX_Buffer[i]); + } + Serial.println(); +#endif +} + +void receive_and_respond_data() +{ + // put received data from SPI into the RX_Buffer + // return from function if no data is received within 10 ticks (10 ms) + HAL_SPI_Receive(&(_spi.handle), RX_Buffer, sizeof(RX_Buffer), 10); + tmpCounterRx++; + // e.g. [1, 2, 3, 4, 5, 6, 7, 8] + + // response data shall be available as soon as possible + // avoid long intermediate processing or other functions + uint8_t TX_Buffer[BUFFER_SIZE] = {0}; + for (uint8_t i = 0; i < BUFFER_SIZE; i++) + { + // create response message, which is the received data + 1 + TX_Buffer[i] = RX_Buffer[i] + 1; + } + // e.g. [2, 3, 4, 5, 6, 7, 8, 9] + + // send response as the data of the TX_Buffer to the SPI Master + // return from function within 10 ticks (10 ms) + HAL_SPI_Transmit(&(_spi.handle), TX_Buffer, sizeof(TX_Buffer), 10); + tmpCounterTx++; +} diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp index 0d836a0295..0e7a47afd1 100644 --- a/libraries/SPI/src/SPI.cpp +++ b/libraries/SPI/src/SPI.cpp @@ -69,6 +69,7 @@ void SPIClass::begin(uint8_t _pin) digitalWrite(_pin, HIGH); } + _spi.mode = SPI_MASTER; _spi.handle.State = HAL_SPI_STATE_RESET; spi_init(&_spi, spiSettings[idx].clk, spiSettings[idx].dMode, diff --git a/libraries/SPI/src/utility/spi_com.c b/libraries/SPI/src/utility/spi_com.c index 32d46261dc..3eb371444f 100644 --- a/libraries/SPI/src/utility/spi_com.c +++ b/libraries/SPI/src/utility/spi_com.c @@ -256,7 +256,11 @@ void spi_init(spi_t *obj, uint32_t speed, spi_mode_e mode, uint8_t msb) /* Fill default value */ handle->Instance = obj->spi; - handle->Init.Mode = SPI_MODE_MASTER; + if (obj->mode == SPI_SLAVE) { + handle->Init.Mode = SPI_MODE_SLAVE; + } else { + handle->Init.Mode = SPI_MODE_MASTER; + } spi_freq = spi_getClkFreqInst(obj->spi); if (speed >= (spi_freq / SPI_SPEED_CLOCK_DIV2_MHZ)) { diff --git a/libraries/SPI/src/utility/spi_com.h b/libraries/SPI/src/utility/spi_com.h index daba244b4c..788565abdf 100644 --- a/libraries/SPI/src/utility/spi_com.h +++ b/libraries/SPI/src/utility/spi_com.h @@ -49,6 +49,12 @@ extern "C" { /* Exported types ------------------------------------------------------------*/ +///@brief SPI device modes +typedef enum { + SPI_SLAVE = 0, + SPI_MASTER = 1 +} spi_device_mode_e; + struct spi_s { SPI_HandleTypeDef handle; SPI_TypeDef *spi; @@ -56,6 +62,7 @@ struct spi_s { PinName pin_mosi; PinName pin_sclk; PinName pin_ssel; + spi_device_mode_e mode; #if defined(SPI_IFCR_EOTC) // Delay before disabling SPI. // See https://github.com/stm32duino/Arduino_Core_STM32/issues/1294