This is another one of those posts mostly meant for myself.
I've been hitting my head against the wall for four days, trying to get Slave SPI working on a Teensy 4.0, with a Teensy 4.1 as the master.
I started out with the https://github.com/tonton81/SPISlave_T4 library (and https://forum.pjrc.com/threads/66389-SPISlave_T4) , but could not make it work, so I stripped it down to the minimum. After a while (after removing a while-loop from the ISR) I got some response. However, it was not stable and it seemed like it would only receive every second byte.
After a lot more troubleshooting I got it working on every transfer, but only if writing 0 to the TX buffer. Any other values would give different troubles.
In the end, it turned out to most likely be a combination of things - mixing up MOSI and MISO pins as well as wrong impedance settings on the pins. Removing and attaching probes to the pins also made stuff fail.
Anyway, next time I would check the following:
- Make sure CFG1 is set to full duplex (00 or 11)
- Check crossed vs straight connections between MISO and MOSI, matching CFG1
- Connect/disconnect logic probes on MISO/MOSI (works when bytes go missing)
- Make sure master deasserts PCS between frames, I wrote 16 bits without rising the PCS (or rather, rising for a too short time) between blocks of 8 bits when frame size was 8 bit.
- Make sure master speed is > 1MHz
I have yet to find a completely stable solution and I am worried that connecting multiple slaves to the same master may not work as intended.
Learned
Anyway, here are some of the things I learned:
- The minimum SPI speed on the Teensy is 1MHz (without doing special stuff)
- To set SPI in slave mode: set bit 1 in CFG1 to 0 and update Clock gating register for the appropriate LPSPI, for example CCM_CCGR1 |= (3UL << 6); (lpspi4_clk_enable)
- When CFG1 bits 25-24 are 00, MOSI on the master connects to MISO on the slave and vice versa.
- When CFG1 bits 25-24 are 11, MOSI on the master connects to MOSI on the slave (and MISO to MISO).
- When communicating between two Teensy 4.x, the pin impedance must be changed on both slave and master. For example:
IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_01 = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE;
- Even when changing the impedance, I needed to experiment with connecting/disconnecting the logic probes.
- TX buffer empty flag is set immediately after the last TXFIFO byte is shifted out, at the same time an RX interrupt is triggered (if RXWATER is 0).
- The SPI documentation talks about different size descriptions:
- Transfer - when sending data - Transfer complete IR is triggered when all data in TXFIFO has been sent (FIFO is empty)
- Word - Up to 32bits, 0-padded if less than 32 bits received. FIFOs are 16 words long.
- Frame - Frame complete IR is triggered when PCS (chip select) goes high (deasserts). A Frame may be from 8bits long, length must be divisible by 2 and may be longer than 32bits (span multiple words). If word length is reached, the frame is split across multiple words in the RXFIFO.
- TDF and RDF - Transmit and Receive Data flags are set by the system and reset by reading from/writing to the TX and RXFIFOs, not by clearing interrupt flags.
- The SPI slave example sets IER bit 1 which is Transmit Data Interrupt. It will be triggered when the number of bytes in the TXFIFO is less than TXWATER. With TXWATER 0 this will be triggered at the same time a byte is received. I don't understand why this is chosen instead of for example FCF.
- The SPI slave library, and also the more elaborate example at https://github.com/tonton81/SPI_MSTransfer_T4, uses a lot of looping and waiting inside the ISR, which cannot be a good way of doing things...
- With an 8bit frame and a single byte in the TX buffer, the following are set on RX: Frame completed, Word completed, RX data ready, TX data ready. RXFIFO in FSR is incremented. If another byte is received before a new byte is written to TX, we get a TX underrun.
Symptoms
Here are some of the symptoms I experienced:
- Missing or erroneous slave sends when anything but 0 was written to TDR - i suspect that we tried writing and reading from the same pins and that messed up stuff, though not entirely sure WHAT was going on.
- TX buffer underruns even if I wrote to the output buffer every time a byte was received
- Elements in the TX buffer disappeared (even if length was correct) as verified by logic probes.
- When writing the same bytes over and over to TX, the same bytes disappeared every time.
- Swapping master and slave made DIFFERENT bytes disappear.
- Missing RX, some of the received bytes never ended up in the RXFIFO
- All receives worked when I only wrote 0 to TX buffer.
- It looked like I needed to receive 16bits and/or only every second byte was received.
- When unplugging either the MISO or MOSI cable, sends started working perfectly
- Removing probes sometimes made stuff work - but only after powercycling the Teensys.
Test code
My slave test code can be found at
https://github.com/xonik/teensy-spislave-test
Master code is
#include <SPI.h>
SPISettings settings(10000000, MSBFIRST, SPI_MODE0);
void setup() {
IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_01 = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE;
IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_02 = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE;
IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_03 = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE;
IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_00 = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_PKE;
Serial.begin(1000000);
pinMode (10, OUTPUT);
digitalWriteFast(10,HIGH);
Serial.println("SPI Starting");
SPI.begin();
SPI.beginTransaction(settings);
}
void loop() {
digitalWriteFast(1,HIGH);
digitalWriteFast(1,LOW);
delay(500);
digitalWriteFast(10,LOW);
Serial.println(SPI.transfer(0xAA));
digitalWriteFast(10,HIGH);
}
No comments:
Post a Comment