How can we achieve maximum SPI speed without using DMA?
If we change (in SPI.h, line 57-58)
// Default speed set to 4MHz, SPI mode set to MODE 0 and Bit order set to MSB first.
SPISettings() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); }
to
// Default speed set to 12MHz, SPI mode set to MODE 0 and Bit order set to MSB first.
SPISettings() { init_AlwaysInline(12000000, MSBFIRST, SPI_MODE0); }
and then at the beginning of the code write
SPI.setClockDivider(4);
are we reaching the max speed?
Or does the setClockDivider function do the same as the changing the 4MHz default to 12MHz?
The beginTransaction code inside SPI will automatically use Arduino Zero's fastest 12 MHz. The advantage of this new approach is your code will automatically use 16.8 MHz when compiled on Arduino Due, since that's Due's best speed not more than 20 MHz. If compiled on a Teensy 3.2, it'll automatically be 16 MHz. On regular AVR boards, it'll use 8 MHz (or whatever speed it possible... this also proper scales if your AVR board has a different F_CPU speed).
The point is you embed the number 20000000, representing the actual maximum speed your other chip can use. As more types of high speed boards appear in the coming years, your code will automatically adapt to use their fastest speeds which don't exceed the 20 MHz rating.
The other important feature of this approach is you do NOT configure the speed in setup().
Configuring speed just once results in code that can't be combined with other SPI-based code and libraries. Even if you only intend to use this 1 SPI chip in this particular project, when you later build another project with this chip and other SPI parts, or if you ever share this code with other people who try to use it with other SPI chips, the beginTransaction() functions guarantee each SPI chip always gets proper settings when it needs to use the SPI bus.
SPI is meant to be a bus for multiple chips, and Arduino is meant to be a platform for developing and sharing code within a large community. The old configure-only-once approach resulted in tons of code that wasn't portable between different chips and couldn't be shared and combined with other code.
It's time to leave the old, obsolete idea of configuring SPI settings just once in the past. Use the new functions. They're much better.
Finally I added the following #ifndefs in the Adafruit_SSD1306::ssd1306_data() function to remove the AVR TWBR register definition for the ARM processors:
I have been able to make the library work for the zero, what in trying to do is to improve the speed of the display.
At the moment I use the setClockDivider(4) function. I was just wondering if changing the settings as well will increase the speed again or if it is just a different way to change the clock
speed.
I would love to implement DMA but it seems to be a bit difficult...
If you are willing to bypass the layers of slow C++ stuff, you can get nearly 12Mb/S without using DMA for write-only operations.
This code writes 128x128x3 bytes to an LCD in ~33mS . . . 11.9Mb/S out of a theoretical 12Mb/S
The trick is to wait for the REG_SERCOM4_SPI_INTFLAG DRE bit, which gives you an entire 600nS while the previous byte clocks out to do your data and loop overhead.
It took some digging to find that the Adafruit M0 Adalogger is using SERCOM4 and that DRE is INTFLAG.0x01, but once you know those two bits, it works quite easily.
// 128x128x3*8 bits / 33mS = 11.9Mbits/S
void Fill_LCD(uint8_t R, uint8_t G, uint8_t B)
{
register int
i;
Set_LCD_for_write_at_X_Y(0, 0);
//Fill display with a given RGB value
SET_RS;
// Select the LCD controller
CLR_CS;
for (i = 0; i < (128 * 128); i++)
{
//There is a 1-byte buffer in front of the
//the SPI transmit shift register. If that
//register is empty, we can write.
//Speedup: write directly to the register
while(0==(REG_SERCOM4_SPI_INTFLAG&0x1)) //DRE
{
//wait;
}
REG_SERCOM4_SPI_DATA=B;
while(0==(REG_SERCOM4_SPI_INTFLAG&0x1)) //DRE
{
//wait;
}
REG_SERCOM4_SPI_DATA=G;
while(0==(REG_SERCOM4_SPI_INTFLAG&0x1)) //DRE
{
//wait;
}
REG_SERCOM4_SPI_DATA=R;
}
SET_CS;
}