Teensy 3.6 Max SPI transfer speed

Status
Not open for further replies.

bk2001050

Member
Hi Guys,
Currently working on a final year engineering project and need some guidance. My programming skills are limited (in an electrical program). We're interfacing the ADS1298 24 bit high precision bio amplifier to gather EMG data and send it over SPI to the teensy 3.6 main SPI bus. Experiencing some lag issues.

I wanted to ask, what is the max spi transfer rate of the Teensy 3.6 and how does the first argument of the SPI Settings function work i.e how is it determined for a controller/application?

SPISettings settingsA(2000000, MSBFIRST, SPI_MODE1);

Thanks! Any guidance is appreciated!
 
The max speed is 30 MHz.

The speed input to SPISettings is meant to be the maximum speed your hardware can use. The SPI library gives you fastest speed the hardware is capable of using, which is less than or equal to the maximum you specified. The hardware uses integer division of F_BUS, which by default is 60 MHz. The minimum integer is 2, so 30 MHz is the highest speed. The next lower speed will be 20 MHz.

No matter which speed you choose, you will get much better performance if you transfer a block of data, rather than calling SPI.transfer() for each individual byte. The block transfer uses the FIFO to get the next bytes ready, so the gaps between bytes are minimized.

Also use digitalWriteFast if you put the pin number as a constant in your code. It's quicker than normal digitalWrite.
 
Hi Paul, thanks for the prompt reply. The argument is the literal speed. The ADS1298 takes a maximum clock of 20 MHZ, so i changed it to 20000000. I am transferring a block of data at a time (I think), the data acquisition algorithm was written by a previous years team. Basically the ADS outputs 216 bits in the MSB format, 24 bits times 8 channels = 192, plus a 24 bit status header. The following code receives multiple bytes:
uint8_t spiRec(uint8_t* buf, size_t len) {
for (size_t i = 0; i < len; i++) {
buf = SPI.transfer(0XFF);
}
return 0;

All the bytes are stored in a serialBytes[200] array and an encoding function called encodeHex changes the bits to display hex characters.

Currently I am having a weird issue, where Serial.print is printing a bunch of data in chunks instead of incremental chronological values. Please check the attached picture. All the time stamps are identical and the data seems to be printed every second in batches. This is causing me not to get a good plot on the serial plotter. Any suggestions?

Screenshot_2.png
 
just an FYI - As Paul mentioned, the speed of your code:
Code:
uint8_t spiRec(uint8_t* buf, size_t len) {
for (size_t i = 0; i < len; i++) {
    buf[i] = SPI.transfer(0XFF);
}
Will have gaps in time, that is it will not run at a full 20mhz... maybe not important, but just pointing out.

Why this happens. The above code will put out the 0xff and while doing do will wait for this to be transferred out on the MOSI pin and will clock in the response on the MISO pin, and then when this completes the system will detect the response finished and then return it at which time the call returns, and then your next call to transfer happens, which starts it up again. During these transisition times the SPI buss is not doing anything. Sometimes these gaps in time can be as big as the actual transfers...


versus some other ways, which may not be fully standard, like:
Code:
SPI.setTransferWriteFill(0xff);
SPI.transfer(nullptr, buff, len)
;

Which says something like lets try to send and receive len bytes and since the source buffer is NULL we will send 0xff bytes and the return values will be stored in the buff array. This tries to keep the SPI FIFO queue filled and as such shortens the time it takes to receive the data

As for then transferring the data back to PC and timings and chunks. The USB serial code works with chunks of data. Where as it will send packets of data back to the PC. I don't remember the USB packet size for T3.x, could be something like 64 bytes.

but again sometimes it helps to see the actual program to have an idea of what may be going on.
 
just an FYI - As Paul mentioned, the speed of your code:
Code:
uint8_t spiRec(uint8_t* buf, size_t len) {
for (size_t i = 0; i < len; i++) {
    buf[i] = SPI.transfer(0XFF);
}
Will have gaps in time, that is it will not run at a full 20mhz... maybe not important, but just pointing out.

Why this happens. The above code will put out the 0xff and while doing do will wait for this to be transferred out on the MOSI pin and will clock in the response on the MISO pin, and then when this completes the system will detect the response finished and then return it at which time the call returns, and then your next call to transfer happens, which starts it up again. During these transisition times the SPI buss is not doing anything. Sometimes these gaps in time can be as big as the actual transfers...


versus some other ways, which may not be fully standard, like:
Code:
SPI.setTransferWriteFill(0xff);
SPI.transfer(nullptr, buff, len)
;

Which says something like lets try to send and receive len bytes and since the source buffer is NULL we will send 0xff bytes and the return values will be stored in the buff array. This tries to keep the SPI FIFO queue filled and as such shortens the time it takes to receive the data

Wow didn't know it was that inefficient, I am a noob at specific coding like this. I will give those other functions a try, would something like this work:
Code:
uint8_t spiRec(uint8_t* buf, size_t len) {
for (size_t i = 0; i < len; i++) {
    buf[i] = SPI.setTransferWriteFill(0xff);
}

Also, I figured out the USB Serial.println() problem after reading the very well documented link: https://www.pjrc.com/teensy/td_serial.html#rxbuffer

Its as you said, the packet size is 64 bytes, and it sends it all in a full chunk rather than 8 bits at a time. Something like this Serial.write(buffer, length) or Serial.send_now() might work, I will test it out and post back results. Thanks Kurt!
 
Status
Not open for further replies.
Back
Top