Sending Data as a Host SPI Device

The host is the device that decides when to trigger communication and which client is the recipient of the message. SPI host devices are generally used in high-speed communication and the focus is to exchange data with other devices acting as clients (e.g. sensors, memories or other MCUs).

This use case follows the steps:

How to Configure the Location of the SPI Pins

The way to configure the location of the pins is independent of the application purpose and the SPI mode. Each microcontroller has its own default physical pin position for peripherals. The locations can be found in the PORTMUX peripheral section from the family data sheet of the megaAVR 0-series. For ATmega4809, the SPI pins are located on PA[7:4] and can be changed on PC[3:0] or PE[3:0] using the TWISPIROUTEA register of the PORTMUX module.

Figure 1. TWISPIROUTEA Register

The pin order is the following: MOSI, MISO, SCK, SS; MOSI representing the lowest pin number from the group. This is how a user can change the location of the SPI pins for option 1 with port C:

This translates into the following code:

PORTMUX.TWISPIROUTEA |= PORTMUX_SPI00_bm;

Or option 2 with port E:

This translates into the following code:

PORTMUX.TWISPIROUTEA |= PORTMUX_SPI01_bm;

How to Initialize the Peripheral

The clock frequency is derived from the main clock of the microcontroller and is reduced using a prescaler or divider circuit present in the SPI hardware. By default, the source of the main clock is a 20 MHz internal oscillator, which is divided by a prescaler whose default value is 6, resulting in a main clock frequency of approximately 3.33 MHz. More information about the clock can be found in the Clock Controller section of the megaAVR 0-series family data sheet.

The clock frequency of the SPI can also be increased using the Double Clock mode, which works only in Host mode. The Data Order bit represents the endianness (Most Significant bit or Least Significant bit) of the data, the order in which the bits are transmitted on the channel (starting with the last or the first bit from a register). All the configurations are related to the CTRLA register.

Figure 2. CTRLA Register

Next is an example of how to configure a host SPI device with the default main clock source and with the default pin location presented in the previous topic. A 416 kHz frequency will result by configuring the device in Double-Speed mode and with a 16 times divider. The data will be shifted out starting with the Most Significant bit (MSb):

This translates into the following code:

SPI0.CTRLA = SPI_CLK2X_bm 
           | SPI_DORD_bm
           | SPI_ENABLE_bm
           | SPI_MASTER_bm
           | SPI_PRESC_DIV16_gc;

How to Configure the Direction of the Pins

Since the host devices control and initiate transmissions, the MOSI, SCK and SS pins must be configured as output, while the MISO channel will keep its default direction as input. The default values, directions and configurations explained above are still applicable here. The following example is based on the default position of the SPI pins:

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

An SPI host device can control more than one client, thus requiring more SS pins. The additional SS channels can be configured just like the one in the example above. The user must choose an unused pin and configure its direction as output.

How to Control Client Devices

A host will control a client by pulling low the SS pin. If the client has set the direction of the MISO pin to output, when the SS pin is low, the SPI driver of the client will take control of the MISO pin, shifting data out from its transmit DATA register. All client devices can receive a message, but only those with SS pin pulled low can send data back. Therefore, it is not recommended to enable more than one client in a typical connection (like the one below) because all of them will try to respond to the message and there is only one MISO channel, thus the transmission will result in a write collision. The user can check the appearance of collisions by reading the value of the WRCOL bit in the INTFLAGS register.

Figure 3. Typical SPI Bus

How to Send Data as a Host Device

All the settings configured before are considered in the following example and the polling method is used for flag checking. Before sending data, the user must pull low an SS signal to let the client device know it is the recipient of the message.

PORTA.OUT &= ~PIN7_bm;

Once the user writes new data into the DATA register the hardware starts a new transfer, generating the clock on the line and shifting out the bits.

Figure 4. DATA Register
SPI0.DATA = data;

When the hardware finishes shifting all the bits, it activates a receive Interrupt flag, which can be found in the INTFLAGS register.

Figure 5. INTFLAGS Register

The user must check the state of the flag, before writing new data in the register, by either activating the interrupts or by constantly reading the value of the flag (a method called polling), else a write collision interrupt will occur.

while (!(SPI0.INTFLAGS & SPI_IF_bm))
{
    ;
}

The user can pull the SS channel high if there is nothing left to transmit.

PORTA.OUT |= PIN7_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: