The ATmega328 datasheet claims a maximum SPI slave speed of Fosc/4. For a chip clocked at 20MHz, that would be 5Mbps.

However, my tests show that even with no processing whatsoever in the interrupt (other than setting the response), any speed over 128Kbps results in corrupted data. I have no idea why this should be the case.

In my setup, the master runs at 3.3V and 12MHz; and the slave at 5V and 20MHz.

Master code:

#include <SPI.h>
#include "pins_arduino.h"

unsigned int successcount = 0;

void setup(void) {
  SPI.setClockDivider(SPI_CLOCK_DIV4);   // 3Mbps

void print_successful_transfers() {
  if (successcount > 0) {
    Serial.println(" successful transfers.)");
    successcount = 0;

void loop(void) {
  unsigned char n = 0;
  unsigned char received;
  unsigned int failurecount = 0;

  delay(500);       // wait for the slave to be ready

  digitalWrite(SS, LOW);    // SS is pin 10

  while (failurecount < 10) {
    for (unsigned int count = 0; count < 10000; count++) {
      received = SPI.transfer(n+1);
      if (received != n) {
        Serial.print("MISMATCH: sent ");
        Serial.print(", received ");
        if (failurecount == 10) break;
      } else

Slave code:

#include "pins_arduino.h"

volatile char n = 0;

void setup (void) {
  pinMode(MISO, OUTPUT);
  SPCR |= _BV(SPE);
  SPCR |= _BV(SPIE);

ISR (SPI_STC_vect) {
  n = SPDR;
  SPDR = n;

void loop (void) { }

Sample output:

MISMATCH: sent 1, received 245.
(1290 successful transfers.)
MISMATCH: sent 12, received 5.
MISMATCH: sent 13, received 140.
(1350 successful transfers.)
MISMATCH: sent 84, received 41.
MISMATCH: sent 85, received 212.
(996 successful transfers.)
MISMATCH: sent 58, received 28.
MISMATCH: sent 59, received 186.
(5455 successful transfers.)
MISMATCH: sent 139, received 197.
MISMATCH: sent 140, received 11.
(900 successful transfers.)
(114 successful transfers.)
MISMATCH: sent 131, received 193.

Here is my schematic. I am using bare ATmega328 chips rather than Arduinos, but the situation would be the same if I were using a 5V Arduino Nano and a 3.3V Arduino Nano.

(Except the slave would run at 16MHz instead of 20MHz.)


I also tried with the following level shifter on the MOSI, SCK and SS lines:

active level shifter


To rule out voltage as the possible cause, I ran both ATmega328s at 3.3V and 12MHz. Same problem.

EDIT: I connected two Arduino Nanos together and got different - but still erroneous - behaviour.

The SS pin, despite being set to LOW at the start of the program and never touched again - goes briefly high every so often, corrupting the communication:

SS pin logic analysis

(If I use a different pin for slave select, I get the same behavior.)

Note that this is NOT the issue I experienced with my bare ATmega328 circuit.

  • 3.3 V is small and easy to corrupt in wiring. throw that in with a 5V device and you need some pretty noise resistant wiring
    – Abel
    Feb 13, 2022 at 16:09
  • Can you monitor the SPI lines with a scope? That'd be interesting.
    – Sim Son
    Feb 13, 2022 at 16:34
  • There doesn't seem to be anything wrong with the wiring or the signals themselves. I tested with a logic analyser and a scope. Besides, shouldn't 3.3V at the very least be able to cope with 256Kbit? Feb 13, 2022 at 20:13
  • I'd rather use 1k instead of 4k7 on that level shifter, slowly rising edge will be bottleneck. And also ISR is taking some time too (it could be up to 20 instructions). And also SS is used for synchronization, so it won't be a bad idea to set it high after the transfer and low before it
    – KIIV
    Feb 13, 2022 at 20:38
  • At 20MHz, 20 instructions shouldn't be a significant bottleneck. Feb 13, 2022 at 21:14


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.