Hi All,

I am trying to down-clock my Teensy 4.1 to lower power consumption, and am experiencing a bizzare issue with SPI.

I am connecting an MPU-6500 IMU breakout board to the 4.1's SPI1 peripheral, and using a software-defined CS pin. The SPI lines are running at 1MHz, and are laid out on a PCB. Using PlatformIO on Linux x86_64, If I define the board's CPU Frequency to be 150 MHz, SPI1 works perfectly fine, sending data back and forth between the two devices. However, if I set the clock speed to 24 MHz (Which I grabbed from Arduino's "Tools > CPU Speed"), the board attempts to send one byte and appears to hang. Here is a proof of concept (copy-pastable in Teensyduino, for reproducibility):

Code:
#include <SPI.h>

#define SPI_CS 3
#define SPI_SCK 27

void setup() {
  // put your setup code here, to run once:

  SPI1.begin();
  SPI1.setSCK(SPI_SCK);

  pinMode(SPI_CS, OUTPUT);
  digitalWrite(SPI_CS, HIGH);
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(5000);

  SPI1.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE3));
  digitalWrite(SPI_CS, LOW);

  SPI1.transfer(0xaa);
  SPI1.transfer(0x77);

  delayMicroseconds(1);

  digitalWrite(SPI_CS, HIGH);
  SPI1.endTransaction();
}
Here, I continuously send two garbage bytes over SPI1, then delay 5 seconds.

With the CPU Speed set to 150 MHz, I can see the two bytes send properly over the bus:
Click image for larger version. 

Name:	issue_150_mhz.jpg 
Views:	7 
Size:	61.8 KB 
ID:	28488

However if I set it to 24 MHz and re-upload the code, The two bytes don't seem to send.
Click image for larger version. 

Name:	issue_24_mhz.jpg 
Views:	7 
Size:	45.1 KB 
ID:	28489

Since I am manually setting the CS pin, I believe there is something going on in those two SPI1.transfer functions. To confirm this, I set up a UART to mark where in the program it is:

Code:
#include <SPI.h>

#define SPI_CS 3
#define SPI_SCK 27

void setup() {
  // put your setup code here, to run once:

  SPI1.begin();
  SPI1.setSCK(SPI_SCK);

  Serial3.begin(115200);

  pinMode(SPI_CS, OUTPUT);
  digitalWrite(SPI_CS, HIGH);

  Serial3.printf("Hello\n");
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(5000);

  Serial3.printf("a");
  SPI1.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE3));
  digitalWrite(SPI_CS, LOW);

  Serial3.printf("b");
  SPI1.transfer(0xaa);
  Serial3.printf("c");
  SPI1.transfer(0x77);
  Serial3.printf("d");

  delayMicroseconds(1);

  digitalWrite(SPI_CS, HIGH);
  SPI1.endTransaction();
}
From here, I moved the Proof of Concept code to PlatformIO (as it is a bit simpler to modify the SPI Library source code there), adding printf statements to the SPI Library to mark where it was getting stuck:

Code:
/* SPI.h */
// Write to the SPI bus (MOSI pin) and also receive (MISO pin)
	uint8_t transfer(uint8_t data) {
		// TODO: check for space in fifo?
		Serial3.printf("1");
		port().TDR = data;
		Serial3.printf("2");
		while (1) {
			uint32_t fifo = (port().FSR >> 16) & 0x1F;
			Serial3.printf("%d ", fifo);
			if (fifo > 0) return port().RDR;
		}
		//port().SR = SPI_SR_TCF;
		//port().PUSHR = data;
		//while (!(port().SR & SPI_SR_TCF)) ; // wait
		//return port().POPR;
	}
Of course, Serial is horrifically slow, but we do get the same result if we re-upload using 24 MHz:
Click image for larger version. 

Name:	issue_with_serial_closeup.jpg 
Views:	6 
Size:	59.2 KB 
ID:	28490

And I receive on my Logic Analyzer's serial monitor:

Code:
ab120 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...
Which implies that we are beginning the transaction properly, entering the first SPI1.transfer(), sending the first byte, and in the while loop waiting for the SPI's FIFO to have the returned byte, but it never actually receives it as the FIFO is always empty (at least the last part is what I'm assuming).

Did I misconfigure the SPI for use at such a low clock speed, or is something else at play here?