SPI on Teensy 4

Status
Not open for further replies.

beckereth

New member
I am working on a project that requires SPI for some external chips and I am running into several issues with SPI on the Teensy 4.

Attempting to diagnose my issues with the example programs and an oscilliscope I am noticing weird behavior. The scope plots below show the CS pin (channel 1, yellow) and SPI clock (channel 2, purple). CS pin falls low but does not remain low for the duration of the transfer. Additionally, as the code belo shows I am only sending 2 bytes of data, but the clock signal shows 24 puleses, indicating 3 bytes of data sent.

Has anyone experienced this before? Is the SPI library not functioning correctly on Teensy 4?


PNG.png

Code:
/*
  Digital Pot Control
  
  This example controls an Analog Devices AD5206 digital potentiometer.
  The AD5206 has 6 potentiometer channels. Each channel's pins are labeled
  A - connect this to voltage
  W - this is the pot's wiper, which changes when you set it
  B - connect this to ground.
 
 The AD5206 is SPI-compatible,and to command it, you send two bytes, 
 one with the channel number (0 - 5) and one with the resistance value for the
 channel (0 - 255).  
 
 The circuit:
  * All A pins  of AD5206 connected to +5V
  * All B pins of AD5206 connected to ground
  * An LED and a 220-ohm resisor in series connected from each W pin to ground
  * CS - to digital pin 10  (SS pin)
  * SDI - to digital pin 11 (MOSI pin)
  * CLK - to digital pin 13 (SCK pin)
 
 created 10 Aug 2010 
 by Tom Igoe
 
 Thanks to Heather Dewey-Hagborg for the original tutorial, 2005
 
*/


// include the SPI library:
#include <SPI.h>


// set pin 10 as the slave select for the digital pot:
const int slaveSelectPin = 10;

void setup() {
  // set the slaveSelectPin as an output:
  pinMode (slaveSelectPin, OUTPUT);
  digitalWrite (slaveSelectPin, HIGH);
  // initialize SPI:
  SPI.begin(); 
}

void loop() {
  // go through the six channels of the digital pot:
  for (int channel = 0; channel < 6; channel++) { 
    // change the resistance on this channel from min to max:
    for (int level = 0; level < 255; level++) {
      digitalPotWrite(channel, level);
      delay(10);
    }
    // wait a second at the top:
    delay(100);
    // change the resistance on this channel from max to min:
    for (int level = 0; level < 255; level++) {
      digitalPotWrite(channel, 255 - level);
      delay(10);
    }
  }

}

void digitalPotWrite(int address, int value) {
  // gain control of the SPI port
  // and configure settings
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
  // take the SS pin low to select the chip:
  digitalWrite(slaveSelectPin,LOW);
  //  send in the address and value via SPI:
  SPI.transfer(address);
  SPI.transfer(value);
  // take the SS pin high to de-select the chip:
  digitalWrite(slaveSelectPin,HIGH);
  // release control of the SPI port
  SPI.endTransaction();
}
 
I am also noticing issues with SPI on Teensy 4. Code written for 3.2 does not run on Teensy 4. Debugging, it seems to just hang on 'SPI.transfer(c);' with no further code execution. Any know issues??
 
@KurtE has gotten it to work across many displays - it has some unique properties.

Just looking at github.com/PaulStoffregen/SPI it was updated yesterday to adjust for pin settings. That will be released in a TD 1.48b1 soon - may install and work locally in {sketchbook}\libraries with TD 1.47.
 
Just out of curiosity have you tried mode0 instead of mode1. Also recently when I had to add a delayNanosecond (200) after going low and before going high. Could try that as well.

Been testing a whole range of spi displays and not had a problem. Unfortunately don't have the hardware to duplicate your problem.
 
Curiously enough, I have resolved my issue by adding the 'SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));' and 'SPI.endTransaction();' methods in their respective locations. I have used the same code for a few years and never used these methods. Are they really needed? If so, does anyone know why? Don't worry I am going to start digging through the code to see for myself as well. I dont have an Oscope, but if I have some time I will put this on my saleae16 and see if the transactions are in fact valid, kinda scary reading over backereth's post.
 
Also something you might try.

I have found that switching SPI_MODES can screw up the first write or two...

So what I have done in a few places, to make things work: For the moment assume only only device.

So in init code I might do something like:

Code:
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
<Make sure your CS pin is not selected something like: digitalWriteFast(CS_PIN, HIGH);
SPI.transfer(0);  // some bogus transfer
SPI.endTransaction();
 
Also something you might try.

I have found that switching SPI_MODES can screw up the first write or two...

So what I have done in a few places, to make things work: For the moment assume only only device.

So in init code I might do something like:

Code:
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
<Make sure your CS pin is not selected something like: digitalWriteFast(CS_PIN, HIGH);
SPI.transfer(0);  // some bogus transfer
SPI.endTransaction();

What do you suppose this does? Or do you do this just for sanity.. I guess I have always relied on default SPISettings and the CS pin to drive the transaction, which is why my code hung. So far everything looks pretty good.

@beckereth, could you do the same test using each SPI mode? I would like to see if they operate as expected.

As to my previous question, I would still like to know why my code didnt work without beginTransaction and does with it. (because it used to on Teensy 3.2) As far as I can tell it diables some interrupts and end Transaction re-enables them.
 
The code I mentioned above, helps by actually sending out a byte in the new configuration. Which for example if the mode change switches the clock signal to being low inactive versus high inactive, it get the clock signal into the state that the device wants. Likewise for leading or trailing edge (although it might work without doing this in that case).
 
I am having the same problem with Teensy 4.0 - it hangs on the first spi.transfer command that is executed.

I am interfacing teensy 4.0 with a quadrature encoder chip (LS7366R) via SPI.
The code is attached below. It was working with a teensy 3.6 but once I changed it to a teensy 4.0, it hangs and I debugged it to the point where it does the first spi.transfer command.
By monitoring the CS line on a scope, I can see that it only activates once (i.e. goes high and pulled low once and stays low).
I am using Pins 11 for MOSI, 12 for MISO and 13 for SCK as per default on 4.0. CS is Pin 10.

The solutions by @KurtE and @thagh05t above did not work.

Software version: arduino 1.88 and teensyduino 1.47
"blink" and "asciiTABLE" tested to work on this particular teensy 4.0.

Code:
#include <SPI.h>

int CS1 = 10;
long EncoderCount1;
char buff[4];

void setup() {
  Serial.begin(115200);
  
  pinMode(CS1, OUTPUT);
  digitalWrite(CS1, HIGH);

  QEC_Configure(); // QEC = Quadrature Encoder Counter
}

void loop() {
  ReadEncoder();
  
  Serial.print("Encoder1 = ");
  Serial.print(EncoderCount1);
  
  Serial.println();
  
  delay(10);
}

void ReadEncoder(void){
  long buff[4];
  digitalWrite(CS1,LOW);
  SPI.transfer(0x60); // Request count
  buff[0] = SPI.transfer(0x00); // most significant byte
  buff[1] = SPI.transfer(0x00);
  buff[2] = SPI.transfer(0x00);
  buff[3] = SPI.transfer(0x00); // least significant byte
  digitalWrite(CS1,HIGH); 
  EncoderCount1 = (long)(buff[0]<<24) + (long)(buff[1]<<16) + (long)(buff[2]<<8) + (long)buff[3];
  }

void QEC_Configure(void)
{
  SPI.begin();
  delay(5);
   
  digitalWrite(CS1,LOW);
  SPI.transfer(0x88); 
  SPI.transfer(0x03);
  digitalWrite(CS1,HIGH);
}
 
@connectivity - Confirmed it dies, will debug.

In the mean time you can get it up and running, if you add a couple lines of code...

Code:
void QEC_Configure(void)
{
  SPI.begin();
  
  delay(5);
  [COLOR="#FF0000"]SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));
  SPI.endTransaction();[/COLOR]
  digitalWrite(CS1, LOW);
  SPI.transfer(0x88);
  SPI.transfer(0x03);
  digitalWrite(CS1, HIGH);
}
 
I updated the T4 support for SPI, so now
SPI.begin internally at the end will call beginTransaction(SPISettings()); endTranaction();
So things like the transfer data size and clock settings and... are properly initialized.

I also put in an implementation for
SPI.setDataMode and SPI.setClockDivider so those I believe are functional now... Still code should migrate to using beginTransaction...

Put in PR: https://github.com/PaulStoffregen/SPI/pull/54

Hopefully @PaulStoffregen will get a chance to pull it in before the next beta release.
 
@connectivity - Confirmed it dies, will debug.

In the mean time you can get it up and running, if you add a couple lines of code...

Code:
void QEC_Configure(void)
{
  SPI.begin();
  
  delay(5);
  [COLOR="#FF0000"]SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));
  SPI.endTransaction();[/COLOR]
  digitalWrite(CS1, LOW);
  SPI.transfer(0x88);
  SPI.transfer(0x03);
  digitalWrite(CS1, HIGH);
}


Thanks KurtE, this works. Previously I replaced the SPI.begin() with SPI.beginTransaction(...) so it didn't work..

cheers
 
Status
Not open for further replies.
Back
Top