Hierodule 1.6.2
Utility module set for STM32 MCUs
Loading...
Searching...
No Matches
SPI Module

With this module, you can

  • Initiate an SPI transmission as master.
  • Unidirectionally parse the data received, which is kept in a ring buffer.
  • Set up the data to be transmitted during the next transmission.
  • Assign callback routines to be invoked at the end of a transmission as master or slave.

HIERODULE_SPI_Wrapper is the main item in the module, with which you can access the variables related to the ring buffer and use the module routines to implement your SPI comm procedure.

The module does not address peripheral configuration and initialization, it is assumed those are performed beforehand. Make sure to have configured your SPI peripheral accordingly. Only full duplex slave and master modes are supported for 8-bit long transmissions.
NSS a.k.a. chip selection is not handled by the module, you might want to simply assign a GPIO pin to each slave device, or use a multiplexer, as the master. You might as well just use the hardware input/output functionality of the peripheral.
Make sure to use external PU/PD resistors on the data and chip select lines, especially at higher baud rates. Consult the device manual the manufacturer provides for further directives.

Also notice that the SPI IRQs are defined in the module's source file. The compiler will throw a multiple definition error if an IRQ is defined somewhere else.

To start using the module, first thing you need is a double pointer to HIERODULE_SPI_Wrapper to create an instance for the SPI peripheral.

HIERODULE_SPI_Wrapper **My_SPI1_Wrapper = NULL;
Struct that keeps variables for the data buffers, a pointer to the SPI peripheral,...

It has to be a double pointer, since the wrapper initializer passes you the wrapper pointer defined in the module by reference. The wrapper instance is handled in the module with a pointer and not with a variable of type HIERODULE_SPI_Wrapper, since you use pointers for dynamic memory allocation.

Next, define callback routine to be called at the end of a transmission.

void TC_Handler(void)
{
/*
* This will be performed at the end of a transmission.
*/
}


You only need to implement the callback routines according to the device role and the consequent procedure, and let the module handle the communication. It's possible to switch to a different callback routine for a transmission mode as well, as long as the pointer to the routine is of type void(*)(void).
You can also just pass a null pointer if you do not need anything done at the end of a certain transmission mode.
Keep in mind that the callback routine will be Called at the end of an entire transmission in master mode. As for slave mode, the routine will get invoked at the end of each byte transmission.



Next, initialize the wrapper as master for SPI1 with the callback routine and an arbitrary RX buffer size, 24 for this instance.

My_SPI1_Wrapper = HIERODULE_SPI_InitWrapper (SPI1, 1, 24, TC_Handler);
HIERODULE_SPI_Wrapper ** HIERODULE_SPI_InitWrapper(SPI_TypeDef *_SPI, uint8_t Mode, uint16_t RX_BufferSize, void(*TC_Handler)(void))
Initializes a wrapper for the specified SPI peripheral.


Or, in case you intend to use the peripheral in slave mode, simply call the initializer with a mode parameter value of 0.

My_SPI1_Wrapper = HIERODULE_SPI_InitWrapper (SPI1, 0, 24, TC_Handler);


Now you're all set. If you've initialized the wrapper in slave mode, the peripheral should be able to handle the incoming transmissions from masters. Depending on your implementation, you might want to load an initial value to the data register, an exclamation mark for example:

HIERODULE_SPI_TransmitByte(*My_SPI1_Wrapper, '!');
void HIERODULE_SPI_TransmitByte(HIERODULE_SPI_Wrapper *Wrapper, uint8_t Byte)
Writes a byte into the data register of the SPI peripheral.

Likewise, use HIERODULE_SPI_TransmitByte to set up the data response for the next transmission inside your transmission complete callback, which is invoked whenever the peripheral receives a byte at the MOSI line and sends its respond through the MISO line. You can parse the received bytes via HIERODULE_SPI_GetNextByte.
You can also manipulate the transmit buffer to determine the next response with TX_Buffer and TX_Counter. Keeping different byte arrays and swapping between them on the fly with the received data in regard might be a good idea.



To start a transmission as master, call the master transmit routine with your wrapper, a byte array and the number of bytes to be transmitted.

uint8_t SPI1_TX_Buffer[6];
/*
* ...
*/
HIERODULE_SPI_TransmitPackage(*My_SPI1_Wrapper, SPI1_TX_Buffer, 6);
void HIERODULE_SPI_TransmitPackage(HIERODULE_SPI_Wrapper *Wrapper, uint8_t *TX_Buffer, uint32_t Size)
Starts a transmission as the master.

In this case, your transmission complete callback is invoked when the entire transmission sequence is completed and the peripherals have returned to the idle state. Again, you can parse the received bytes via HIERODULE_SPI_GetNextByte.

You can "release" the instance of your wrapper to free memory:

HIERODULE_SPI_ReleaseWrapper(*My_SPI1_Wrapper);
void HIERODULE_SPI_ReleaseWrapper(HIERODULE_SPI_Wrapper *Wrapper)
Frees the memory allocated to an SPI wrapper.

Keep in mind this only frees the wrapper pointer in the module and not your double pointer that points to it. It's best to free and nullify that, as well:

free(My_SPI1_Wrapper);
My_SPI1_Wrapper = NULL;

Notice that trying to access a freed memory address might turn out ugly for your run-time. However, you can reuse your double pointer to re-initialize the SPI peripheral after releasing it.