SPI.transfer() send multiple bytes, array? (teensy 3.2)

Status
Not open for further replies.

epikao

Active member
Hello,

I need to transfer via SPI many bytes at once and without delay between every byte.

If I do:

Code:
 SPI.transfer(byte1);
 SPI.transfer(byte2);
 SPI.transfer(byte3);
etc..

I have a delay of aprox 500ns between every bytes... with follow SPISettings settingsA(30000000, MSBFIRST, SPI_MODE0) and 96MHz

if I try follow:

Code:
uint8_t rgb[]={5,6,3};

for(x=0; x<=76800; x++)
{
SPI.transfer(rgb, sizeof(rgb));
}

result is as follow: 5,6,3 0,0,0 0,0,0 etc.....

but I need:
5,6,3 5,6,3 5,6,3 5,6,3 etc...

Why the bytes are cleared after the first cycle?
I don't understand that....

thank you very much
 
SPI provides a bidirectional transfer - it writes and read at the same time. The function used is sending a pointer to data and is overwritten with the data read - which is zero's.

try this? :: SPI.transfer(rgb, 0, sizeof(rgb)); // Line #143 >> \hardware\teensy\avr\libraries\SPI\SPI.cpp

Using this 3 param function with a NULL as the second param will have the return data discarded.
 
SPI provides a bidirectional transfer - it writes and read at the same time. The function used is sending a pointer to data and is overwritten with the data read - which is zero's.

try this? :: SPI.transfer(rgb, 0, sizeof(rgb)); // Line #143 >> \hardware\teensy\avr\libraries\SPI\SPI.cpp

Using this 3 param function with a NULL as the second param will have the return data discarded.

thank you that works... but unfortunately the SPI speed is still not such high as expected.
I'm writing to a 240x320 Frame-Buffer from a Display.

I think that the SPI transfer functions still has some delay between every byte or between every third byte.. :-(

Is there another function, or setting to minimize delay between bytes? Respectively a fast way to transfer multiple bytes?

thank you very much
 
If you are using the SPI.transfer(buf, retbuf, cnt)... like transfer, it should keep the SPI running at full speed, without gaps...

Works fine for my 320x240 type output, although you have to be careful on byte orders... Also the speed will obviously depend on what speed you are telling SPI to work at...
Not sure what that is as I don't see any complete code here...
 
You might want to look at the optimized ILI9341 library for inspiration. As long as you use the hardware CS pins for both CS and DC, the code will use DMA to transfer data as fast as possible.

The optimized CS pins for the Teensy 3.2 are (note, pins on the same line represent the same internal hardware resource, so you could use pin 2 as an optimized SPI pin or pin 10, but you can't use them at the same time):
  • Pin 2 or pin 10
  • Pin 6 or pin 9
  • Pin 15/A1
  • Pin 20/A6 or pin 23/A9
  • Pin 21/A7 or pin 22/A8

Further reading:

And note, it is possible to have the processor go too fast and overwhelm the device. I play around with the 128x128 color OLED displays (for the Adafruit uncanny eyes project), and I've found that I can no longer get the 3.2 to work without flickering. In the past I was able to set the Teensy clock speed down so it would work fine, but something has changed in the last year or so, and even if I set the Teensy bus speed to extremely slow, and a low SPI bus speed, it still flickers.

So I now use the Teensy 3.5. I've found I need to set the SPI bus speed to 11Mhz or slower to allow the Adafruit OLED display to work and 18Mhz for the waveshare and newhaven displays. With a SPI bus speed set, I can run the Teensy 3.5 at full speed (120Mh), or even over-clocked. The TFT displays can run fine with a 24Mhz SPI clock speed and the 3.5 running at full 120Mhz.

I use the SPIsettings with SPI transactions to set the SPI bus speed.
 
You might want to look at the optimized ILI9341 library for inspiration. As long as you use the hardware CS pins for both CS and DC, the code will use DMA to transfer data as fast as possible.
...

So I now use the Teensy 3.5. I've found I need to set the SPI bus speed to 11Mhz or slower to allow the Adafruit OLED display to work and 18Mhz for the waveshare and newhaven displays. With a SPI bus speed set, I can run the Teensy 3.5 at full speed (120Mh), or even over-clocked. The TFT displays can run fine with a 24Mhz SPI clock speed and the 3.5 running at full 120Mhz.

I use the SPIsettings with SPI transactions to set the SPI bus speed.

hmm thankyou.
At the moment I try to write from an array with 240*320*3 Bytes x-D ...with SPI.transfer(rgb, 0, sizeof(rgb)); ... but it doesn't works as expected. :-((
I think the spi-function cannot handle such big arrays?? Can you confirm that?

My setting, 96MHz (overclocked),
SPISettings settingsA(30000000, MSBFIRST, SPI_MODE0);
Maximum clock I can measure now in real is aprox 20MHz....

Code:
void color(uint8_t r, uint8_t g, uint8_t b) //red, green, blue Byte value
{
	writecommand(0x2A); //x pixel-position 
	writedata(0x00); //start-position
	writedata(0x00); //start-position

	writecommand(0x2B); //y pixel-positon
	writedata(0x00); //start-position
	writedata(0x00); //start-position

	writecommand(0x2C); //Write to RAM
	
  uint8_t rgb[230399];
  long int x;
  for(x=0; x<76800; x++)
  {
    rgb[x*3+0]=r;
    rgb[x*3+1]=g;
    rgb[x*3+2]=b;
  }   

  digitalWrite(CS0, 0);
  digitalWrite(DC, 1); //Command = 0, Data = 1
  SPI.transfer(rgb, 0, sizeof(rgb));
  digitalWrite(CS0, 1);
}
 
Does adding "__attribute__ ((aligned (4)))" to the array make any difference?

For example:

Code:
uint8_t rgb[230400] __attribute__ ((aligned (4)));

Also consider this array uses nearly all the RAM on Teensy 3.6. If you try this on Teensy 3.2 or 3.5, of course it will fail. It should not even manage to compile.

If you're creating the array with malloc (we can't see that part of your code... so some guesswork here) then you could get a pointer but there simply isn't that much memory.
 
OP Title says (teensy 3.2) - so that can't be in RAM.

Paul - as of TD_1.42 don't T_3.5 and T_3.6 have the same RAM ( less 4 bytes? )?
 
Does adding "__attribute__ ((aligned (4)))" to the array make any difference?

Oh yeah, we're now supporting 256K RAM (less 8 bytes) on Teensy 3.5. :)


what is the max. Array limit for the SPI.transfer function?
I just have try some values, I think the maximum is aprox 60'000? with 65000 it is not working anymore??

Code:
void color(uint8_t r, uint8_t g, uint8_t b)
{
	writecommand(0x2A); //x pixel-position anfahren
	writedata(0x00); //start-position
	writedata(0x00); //start-position

	writecommand(0x2B); //y pixel-positon anfahren
	writedata(0x00); //start-position
	writedata(0x00); //start-position

	writecommand(0x2C); //RAM Daten senden
	
	uint8_t rgb[60000] // OK, but with 65000 NOT OK!!
  long int x;
  for(x=0; x<20000; x++)
  {
    rgb[x*3+0]=r;
    rgb[x*3+1]=g;
    rgb[x*3+2]=b;
  }   

  digitalWrite(CS0, 0);
  digitalWrite(DC, 1); //Command = 0, Data = 1
  SPI.transfer(rgb, 0, sizeof(rgb));
  digitalWrite(CS0, 1);
}

BTW: Regarding __attribute__ ((aligned (4))); I could not find a difference.

See below the max. speed I have with SPI according my settings. How can I setup 120MHz?

spi_speed 30Mhz, 96MHz.jpg
 
Last edited:
The SPI.transfer code is not limiting - it uses 32 bit counters and addressing so it will happily work well beyond available RAM on any Teensy.

First post noted a T_3.2 is being used. It only has 65K of total RAM. The RAM used is noted when the compile completes, and using 100% of RAM - or even almost - can cause failure.
 
The SPI.transfer code is not limiting - it uses 32 bit counters and addressing so it will happily work well beyond available RAM on any Teensy.

First post noted a T_3.2 is being used. It only has 65K of total RAM. The RAM used is noted when the compile completes, and using 100% of RAM - or even almost - can cause failure.

ah ok. understand, compiling results shows only the memory usage of global variables...

To my other question: What can I do to get 30MHz SPI clock? Or how do I set the Systemclock to 120Mhz?
+ is it possible to store the array to static RAM/flash? instead to the dynamic RAM and have DMA?

thanks
 
Last edited:
As mentioned by others, that support a frame buffer on the ILI9341 are Frank's and mine (ili9341_t3n) only support it on T3.6 and at least mine now 3.5
As 320x240*2=153600 bytes and on yours you are using 3 bytes per pixel versus 2 so 230400 bytes which may still fit on T3.6, but I have not tried it.

As for 20mhz, I believe if you set the T3.2 to 120mhz CPU speed, by default the F_BUS will be set to 60MHZ and in SPI fastest mode is F_BUS/2=30mhz
You might be able to go faster and edit kinetisk.h in hardware\teensy\avr\cores\teensy3... and edit where F_CPU == 120000000 and uncomment the line #define line with
setting F_BUS 120000000 (personally I have not tried running the bus at that speed...) So no idea if you might run into issues.

Memory on these CPU only has one address range (unlike the 8 bit AVR boards). So yes you should be able to read from FLASH... However again remember this is still a finite resource (256K)
https://www.pjrc.com/teensy/techspecs.html
 
What can I do to get 30MHz SPI clock?

Use SPI.beginTransaction with SPISettings 30000000 for the clock.

However, the SPI clock can only be an integer division of F_BUS, and the smallest integer is 2. So if F_BUS is 48 MHz (the default with 96 MHz CPU), then 24 MHz is the max SPI speed. SPISettings will automatically use the highest if can that is less than or equal to the clock you request.

Or how do I set the Systemclock to 120Mhz?

Click Tools > CPU Speed and select the 120 MHz option.
 
More overclocking options are available if you edit kinetis.h. The comments in that file explain your options.

NXP rates the MK20 chip on 3.2 for 50 MHz max on F_BUS, and the MK64 & MK66 chips on 3.5 & 3.6 for 60 MHz max F_BUS. Frank and others have reported success with overclocking F_BUS, but going over those ratings is overclocking which comes with all the usual caveats of running faster than the manufacturer's spec.
 
Status
Not open for further replies.
Back
Top