SPI Overview
SPI is one of the most popular communication protocols in microcontrollers. Learn here how to communicate via SPI on the Autosar layered architecture
Whether you love tinkering on the weekends with the next trend of home automation in mind, or to create a cool university project, for sure you learned how important SPI is to the electronic world. So, it's very common to use SPI in ECU's, to communicate to an external EEPROM or watchdog (if you want to learn more about the memory and watchdog stacks in Autosar, refer to our articles Memory Stack Overview (opens in a new tab) and Watchdog Stack Overview (opens in a new tab)), and ASICs (Application-Specific Integrated Circuits). Hence, Autosar contains in the MCAL layer the SPI module.
Since it's located in one of the lowest layers, you might be wondering "how can I use it in my Application?". That is a valid point. So, let's look at the possibilities. There are two options: you use it through the ECU abstraction layer or through a Complex Device Driver (if these concepts sound strange to you, check our article Types of Software Components (opens in a new tab), where we go through all types of SW-C's and their use). That way, you abstract the SPI functionality. The same applies to other MCAL modules, such as DIO (Digital Input and Output), PWM (Pulse Width Modulation), among others, which we'll cover soon. Keep in mind what you need to implement, and your estimation efforts will always be right.
Now, imagine you just got assigned a task to configure SPI (let's consider only SPI, not the upper layers that support it). Where do you start? What are the steps to accomplish it? Let's see:
- Select a level of functionality and configure optional features. The level of functionality will define the behavior of SPI, and you have 3 options here:
Level | Description |
---|---|
0 | Synchronous behavior, blocking |
1 | Asynchronous behavior, non blocking through ISRs |
2 | Enhanced behavior, synchronous and asynchronous behavior |
Regarding the optional features, these are usually in line with the default from other modules, such as a connection to the DET (Default Error Tracer, you can learn about it here, in What are DEM, DLT, DCM and DET (opens in a new tab)) and the DEM (Diagnostic Event Manager, which we go over in detail in DEM Overview (opens in a new tab)), among other things that might come in handy for your specific application needs.
- Define SPI channels according to their data usage, along with their buffering, to then configure SPI jobs (according to HW properties) and SPI sequences (to transmit data in a sorted way). Let's look at these concepts. Autosar's SPI conceptualizes SPI communication into channels, jobs and sequences. Their relationship can be observed in the image below:
SPI channels, jobs and sequences, as per Autosar Specification
As you can see from above, a channel is the lowest granularity in SPI (usually two bytes, depending on the underlying device), the register-value pair. A job is a group of channels, paired together according to hardware properties (CS, Chip Select state), and the SPI sequence, a group of jobs, which allow you to transmit the data in a sorted way.
Lastly, the buffering of an SPI channel can be external or internal:
-
Internal (IB) - Buffering is handled internally on the SPI handler
-
External (EB) - Buffers are provided by the user
You have made it this far. The last thing you need to do is communicate with your device. We'll take a look at the most important APIs available. Remember, the level and buffering scheme used will influence your options:
-
void Spi_Init(const Spi_ConfigType* ConfigPtr) - Initialize the SPI, providing the pointer to the configuration set that you obtain from the Autosar code generation tool you use. For SPI level 2 you also specify if the SPI behavior is synchronous or asynchronous. You also have the Spi_DeInit function to de-initialize the module.
-
Std_ReturnType Spi_WriteIB(Spi_ChannelType Channel, const Spi_DataBufferType* DataBufferPtr) - Write data to an internal buffer of a specified SPI channel. If your DataBufferPtr is null, the default transmission value defined in the module configuration will be used.
-
Std_ReturnType Spi_AsyncTransmit(Spi_SequenceType Sequence) - Transmit a sequence into the SPI bus. This function is only relevant for level 1 and level 2. If you SPI is configured to level 0, then you should use Spi_SyncTransmit and wait for the transmission to end. In Spi_AsyncTransmit, you are allowed to configure a notification function when the transmission is done, which is useful for error handling purposes.
-
Std_ReturnType Spi_ReadIB(Spi_ChannelType Channel, Spi_DataBufferType* DataBufferPointer) - Read synchornously data from the SPI internal buffer associated to the specified SPI channel. Its external buffer counterpart does not exist, since you have the buffers directly at your disposal.
-
Std_ReturnType Spi_SetupEB( Spi_ChannelType Channel, const Spi_DataBufferType* SrcDataBufferPtr, Spi_DataBufferType* DesDataBufferPtr, Spi_NumberOfDataType Length) - Configure the data buffers of source and destination for each SPI channel.
-
Std_ReturnType Spi_SetAsyncMode( Spi_AsyncModeType Mode) - When in SPI level 2, select the mode to snynchronous or asynchronous in runtime, according to your application needs.
As some final notes, we hope this article made you an expert in all SPI topics! Alright, not so fast. But, at least you are now ready to create a module to interface via SPI with devices. Happy coding!
Author: Micael Coutinho (opens in a new tab)
References:
© AutosarToday —@LinkedIn