[4.1] Simple SPI problems - wrong value occasionally sent from MOSI

Status
Not open for further replies.

Nick_D_L

New member
Hi all,

I'm working on a project where I will log measurements from a ADS8353-Q1, a two channel ADC (datasheet) onto the built in SD card with SDIO.

The ADC's SPI protocol starts with sending 16 bits , which programs the register (p. 28). Then the ADC sends 16 bits from its two MISO lines, which are the voltages measured by the ADC. I have two current SPI issues that I would be very grateful for help with.

-Occasionally the Teensy sends out the wrong value according to my AD2. I want to avoid this happening because I don't want to write to a reserved register.

- I don't know how to handle the fact this ADC has two MISO lines. I originally planned to use two SPI busses on the Teensy with one acting as a slave, but apparently the Teensy cannot be a SPI slave. If it can't be done I will simply have to use the 1 MISO mode and accept the lower throughput.

On the first problem - attached is my code, a waveform screenshot from my AD2's logic analyzer, and my wiring.
-Teensy is getting a stable 5V from my laptop, so noise isn't an issue.
- I am using pin 37 for select, 13 for clock and 11 for MOSI. For implementing MISO, I am planning on using pins 12 and 39, but can use any.
-Using only the default SPI.h library with the Arduino IDE. Have not gotten into using any of the SDIO stuff yet.
- Right now the teensy is connected directly to the o-scope, so I do not suspect there is any noise.


IMG_0571.jpg

Code:
//  Current Sensing - Channel A. AINP_A recieves VDiff, while AINM_A recieves VREF from the current sense board
//  Voltage Sensing - Channel B. AINP_B recieves VMeasured, a 0 to 5V signal. AINM_B recieves a 2.5V referance voltage.

//MSB FIRST


#include <SPI.h>
const int slaveSelectPin = 37;
const word readMOSI = 0;
const word CFR_REGISTER = 33408; //1000001010000000 //See page 28 of datasheet.


void setup() {
  // put your setup code here, to run once:
pinMode(slaveSelectPin, OUTPUT); //Slave select as output
digitalWrite(slaveSelectPin, HIGH); //Set as high to create a falling edge to begin transfer.


SPI.begin(); //Begin SPI

}

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

void adc_Transmit() {
SPI.beginTransaction(SPISettings(20000000, MSBFIRST, SPI_MODE3));
digitalWrite(slaveSelectPin,LOW);
SPI.transfer16(CFR_REGISTER);
SPI.transfer16(readMOSI);
digitalWrite(slaveSelectPin,HIGH);
SPI.endTransaction();
}

Image1.jpg


Image2.jpg
The intended transmission is 1000001010000000, with the other values being:

1100001111000000
0100000101000000
1100000101000000

So it seems to be the result of a slight delay on the MOSI, but I'm not sure how to solve it.
 

Attachments

  • LL1.jpg
    LL1.jpg
    27.8 KB · Views: 73
  • LL2.jpg
    LL2.jpg
    26 KB · Views: 74
Firstly that chip is mode 0 SPI, not mode 3, and have you tried a lower SPI clock rate? Those long wires may be struggling at full speed.
 
As for SPI using two SDO pins of the chip.

There may be possibilities of it on a T4.1, I am not sure how well it may or may not work.

HOWEVER it will not work with the pins you mentioned and it will not work with current SPI library.

That is you mentioned two pins: 12 and 39. They are both MISO pins, but they are not on the same object.
That is pin 12 is the MISO pin for the SPI object

And pin 39 is one of the two pins that can be MISO on SPI1 object, the other is pin 1 (default). Note you can not use both 1 and 39 for this either as they are actually the same signal, that the underlying system (IOMUXC) allows to be enabled on one of those two pins.

So then the question is, what might work? Warning I have never tried it and have no idea if/how it would work. BUT the underlying system (LPSPI) does support multiple pins to be used for transfer, I believe it supports 1 bit 2 bit or 4 bit per clock to be transferred out over specific pins (MISO is the low order bit...)

But I believe in most cases of 2 or 4 you are only transferring one direction or the other. That is you need to muck the the RXMSK and TXMSK and WIDTH fields of the TCR register... I have never done so.

So in multiple signal mode I believe:

SOUT(MOSI) is bit 0
SIN(MOSI) is bit 1
PCS(2) is bit 2 (in quad mode)
PCS3 is bit 3 (in Quad mode)

What does this mean, unfortunately none of our SPI objects export all of these signals. The closest we have is the main SPI object:
Note the reference card does not give you all of this information about CS pins. for example on the SPI object
It just shows that there are 3 CS pins(10, 36, 37), in this case all three of these are different CS signals as I show in the PCS above in this case:
10 -> PCS(0), 36->PCS(2), 37 -> PCS(1) and there is no PCS(3) exposed for this object.

MOSI(11), MISO(12), PCS-2 (36)

On our other two SPI objects all of the exposed on SPI1 there is pins 1 and 38 these are both PCS(0) - only one can be used per program as hardware PCS
Likewise SPI2: only has pin 44 again PCS(0)...

More details in the Reference Manual LPSPI chapter.

But my suggestion is probably to punt and just use normal SPI.
 
Forgot to mention, there may be another alternative, depending on your needs.

That is the T4 and 4.1 have another subsystem FlexIO which you can setup code to probably do this pretty well.
But again it would require a bit of work to get setup and working.

I did some earlier playing around with FlexIO to create additional Serial port objects and a simple SPI object.

My experiments are up on github: https://github.com/KurtE/FlexIO_t4
And it would take a bit of time to look through and in my case remember what I was doing.

But my guess is, it would not be hard to add a second MISO pin, it would probably need another shifter. There may be issues if you wish to do all of this with DMA as only dma on first two Flex objects and only 2 channels per object ... so some limitations on this... Also API wise obviously simple transfer of
rx_val = MYFLXSPI.transfer(tx_val)
would only give you one value, but could modify to other api...
 
- Right now the teensy is connected directly to the o-scope

What equipment are you using to make these measurements? Those images look more like a logic analyzer than any oscilloscope I've ever seen!

I loaded your first image into an editor, so I could compare the 2 data waveforms side-by-side (or above-below) to each other.

sc.png

Looking at these 2 waveforms, it seems like Teensy transmitted the exact same data pattern. One time your scope/software decoded it as 33408, and the other time as 49472. But looking at the data waveform, it's very clearly the exact same 16 bits. The clock rising edges are also pretty clearly inside the middle area of each bit.

My best guess is everything actually is working, but your equipment or the software which is creating this analysis just isn't able to reliably decode 20 Mbit/sec. Notice how the clock waveform does look slightly different in the 2 cases, as if a different duty cycle? Looks like the data acquisition rate isn't a lot faster than the SPI clock, perhaps only 50 MHz? This is probably a case where your SPI protocol decode is needs to see a certain number of samples of the clock in a particular state before it can find the transition and decode the data, and maybe it works when the acquisition sample rate just happened to line up with 1 more of its cycles detecting Teensy SPI clock low, but can't decode when the relative phase between the SPI clock and acquisition rate lines up in such a way that 1 extra acquisition cycle sees the SPI clock high rather than low.
 
I tried running your code here on a Teensy 4.1. Here's what my scope sees:

file.png

The 82 80 result is very consistent.

Admittedly, I connected this quickly on a breadboard with a few inches of wire on every signal and its ground, so I turned on bandwidth limit in my scope. Fortunately it's still just able to work for 20 MHz SPI. I know my scope is able to decode SPI up to about 50 MHz (but not any higher - I have tried)... but higher bandwidth requires a lot more work to get the signals probed well enough. I am willing to do that is needed, but I believe this quick & dirty (bw limited) measurement is good enough for this case. My oscilloscope is consistently seeing the number your code sends. It never decodes to the other values you saw.

Anyway, I'm pretty sure everything is working properly here, and you're simply running into a limitation of the gear you've using to make these measurements.
 
Another guess... maybe the software analysis is configured incorrectly. Maybe it's looking at the data waveform during the falling edge rather than at the rising edge?

Together with (assuming) only 3 samples acquired for every SPI clock cycle, when the phases line up one way you get Teensy's SPI clock (which is very close to 50% duty cycle) received as 33% duty cycle, and 66% when the phase lines up the other way.

Taking these 2 things together, the 66% duty cycle case decodes correctly, even though it's looking at the data during the wrong edge. But the 33% case gives errors, because the falling edge is seen earlier relative to the data.
 
What equipment are you using to make these measurements? Those images look more like a logic analyzer than any oscilloscope I've ever seen!

I loaded your first image into an editor, so I could compare the 2 data waveforms side-by-side (or above-below) to each other.

View attachment 21188

Looking at these 2 waveforms, it seems like Teensy transmitted the exact same data pattern. One time your scope/software decoded it as 33408, and the other time as 49472. But looking at the data waveform, it's very clearly the exact same 16 bits. The clock rising edges are also pretty clearly inside the middle area of each bit.

My best guess is everything actually is working, but your equipment or the software which is creating this analysis just isn't able to reliably decode 20 Mbit/sec. Notice how the clock waveform does look slightly different in the 2 cases, as if a different duty cycle? Looks like the data acquisition rate isn't a lot faster than the SPI clock, perhaps only 50 MHz? This is probably a case where your SPI protocol decode is needs to see a certain number of samples of the clock in a particular state before it can find the transition and decode the data, and maybe it works when the acquisition sample rate just happened to line up with 1 more of its cycles detecting Teensy SPI clock low, but can't decode when the relative phase between the SPI clock and acquisition rate lines up in such a way that 1 extra acquisition cycle sees the SPI clock high rather than low.

Solved!

Its the analog discovery 2, which has a logic analyzer function that I was using. Its max speed is 100MHz, however I had set it to the wrong SPI mode so it was occasionally reading out the data wrong. I've set it up again to actually read the same SPI mode that I use, and its working flawlessly. Thanks!
 
I'm pretty unfamiliar with the different SPI modes, but are you sure it isn't mode 1? On a second look it definitely isn't mode 3. The timing diagram from the datasheet I've attached below:p22.png

Clock starts out low, so CPOL = 0. But it looks like the data is sent out on a falling edge, which would indicate CPHA = 1. This is the first time I've tried to decipher one of these diagrams, so I could be very wrong.
 
Forgot to mention, there may be another alternative, depending on your needs.

That is the T4 and 4.1 have another subsystem FlexIO which you can setup code to probably do this pretty well.
But again it would require a bit of work to get setup and working.

I did some earlier playing around with FlexIO to create additional Serial port objects and a simple SPI object.

My experiments are up on github: https://github.com/KurtE/FlexIO_t4
And it would take a bit of time to look through and in my case remember what I was doing.

But my guess is, it would not be hard to add a second MISO pin, it would probably need another shifter. There may be issues if you wish to do all of this with DMA as only dma on first two Flex objects and only 2 channels per object ... so some limitations on this... Also API wise obviously simple transfer of
rx_val = MYFLXSPI.transfer(tx_val)
would only give you one value, but could modify to other api...

Flex looks like it could do this. Once I get the ADC working with normal SPI I'll come back to this and see what I can do. Thanks!
 
I'm pretty unfamiliar with the different SPI modes, but are you sure it isn't mode 1? On a second look it definitely isn't mode 3. The timing diagram from the datasheet I've attached below:View attachment 21190

Clock starts out low, so CPOL = 0. But it looks like the data is sent out on a falling edge, which would indicate CPHA = 1. This is the first time I've tried to decipher one of these diagrams, so I could be very wrong.

Yes, I forgot this is a DAC! clocked on rising edge, sampled on falling edge, clock polarity idle-LOW, which is mode 1, not 0
as I thought, but not 3 either.

Getting the wrong mode can lead to very iffy timing as data is clocked out on the wrong edge.

A scope is much better than a logic analyser for this level of timing debugging, as the quality of signals and
rise and fall times is visible too.
 
@Nick_D_L, sorry to bump this thread... I was just looking into using that part, did you end up using the single SDO mode? Curious if you have made any wrapper classes for this device that you might be interested in sharing?
 
Status
Not open for further replies.
Back
Top