Receiving Data as a Client SPI Device

The client devices are usually actuators. Clients do no initiate any action, they only act whenever the host initiates. A client must be always available and has to wait until the host pulls low its SS channel.

This use case follows the steps:

How to Initialize the Peripheral

The client gets its clock signal from the host device so there is no point changing the clock divider of the peripheral, a change that does not affect when in SPI Client mode. Though, the hardware peripheral has to sample the data received on the MOSI channel. For the data signal to be correctly reconstructed, the main clock frequency of the device must be at least double the clock received on the SPI SCK channel.

If the client device is a microcontroller, the user has to consider the frequency request and configure a powerful clock source. If the user does not have access to the clock generator of the client, it has to make sure the host does not exceed the limitations of the client. A host is part of the same system or application and is mainly represented by a microcontroller whose frequency can be easily changed - either SPI or main clock frequency.

To make the example easier to understand, some of the information presented in Sending Data as a Host SPI Device is also applied here. The device is configured as a client, with a main clock frequency of 3.33 MHz, and the data are shifted out starting with the MSb. Configuring the device as a client resumes mainly to enabling the module and deactivating the Host (MASTER) bit from the CTRLA register:

Figure 1. CTRLA Register

SPI0.CTRLA = SPI_DORD_bm
           | SPI_ENABLE_bm
           & (~SPI_MASTER_bm);

SPI Client Direction Pin Configuration

When the device is in SPI Client mode, the MOSI, SCK and SS pins require to be configured as input channels. By default, all Input/Output (I/O) pins are configured as input, so there is nothing that needs to be modified for these pins. Thus, the hardware circuit of the SPI will take control of these channels during transmission if the peripheral is enabled. Since it is not mandatory to send data back, the MISO channel can be configured either as output or input.

The normal mode is to configure the pin as an output, and the hardware circuit will control its behavior during data exchanges. If the pin is configured as input, it will act as an ordinary I/O pin and will not be used by the SPI.

When the pin value of the DIR register has the value 0, the pin acts as an input digital pin, respective output digital pin for value 1. The default location of the SPI pins will be considered. To be sure that the default direction value of the pins was not changed, all the required pins will be configured as follows:

PORTA.DIR &= ~PIN4_bm; // MOSI channel 
PORTA.DIR |= PIN5_bm;  // MISO channel
PORTA.DIR &= ~PIN6_bm; // SCK channel
PORTA.DIR &= ~PIN7_bm; // SS channel

How to Receive Data as a Client SPI

All the client devices connected to the SPI bus will receive the message sent on the MOSI channel by the host device. A client cannot respond to a message unless the SS channel is pulled low. When the host device pulls the SS pin low, the SPI peripheral of the client device will take control of the MISO pin and will shift data out. If the user does not write into the DATA register, the client will not send data out and the peripheral will shift out a byte full of zeros.

The peripheral will signal the reception of new data by activating the IF flag of the INTFLAGS register. The user has to check the value of the bit, either by the polling method as presented in the host example or by interrupts. The following example uses interrupts to establish the value of the bit since there is no way to tell when the host will send new data and interrupts are non-blocking. Therefore, the device can do whatever it has to do during idle SPI time.

When using interrupts, three important things must be taken into consideration:

  1. 1.Activating the interrupts for the microcontroller. The macro can be used by including the <avr/interrupt.h> file:
    sei();
    
  2. 2.Activating the interrupts for the peripheral can be done by activating the IE flag from the INTCTRL register:
    Figure 2. INTCTRL Register
    SPI0.INTCTRL = SPI_IE_bm;
  3. 3.Clearing the Interrupt flag, if it is not cleared automatically by the hardware. After receiving new data, the receive complete Interrupt flag will be activated. This one can be found in the INTFLAGS register.
    Figure 3. INTFLAGS Register

Clearing the Interrupt flag is done by writing ‘1’ to the bit inside the interrupt function, where the user may also insert its interrupt routine based on its application purpose.

In the example below, it is shown how to read the received data, clear the interrupt and write to the DATA register (it is the user’s choice what to do with the received data and what to write back to the host).

ISR(SPI0_INT_vect)
{
	receiveData = SPI0.DATA;

	SPI0.DATA = writeData;

	SPI0.INTFLAGS = SPI_IF_bm;
}
Tip: The full code example is also available in the Appendix section.

An MCC generated code example for AVR128DA48, with the same functionality as the one described in this section, can be found here: