I've been using a 256x64 display in one of my designs for some years now and it's been great, but I'm limited to 20MHz SPI and that makes a full screen write a touch over 4ms for 9-bit.
It's 9-bit because I don't use a D/C pin, I run the display in 9-bit mode with the MSB low for C, high for D (I'm in SPI_MODE3, so that might be topsy-turvy for folks using MODE1-MODE2).
Anyway, I want to transition to DMA SPI display writes to not have code blocking for 4ms every time I call a display write (50Hz / 20ms intervals, so 20% of the time my code is blocked by these display writes!).
I have two roadblocks in this quest.
The first is that with the following code, even running the unmodified SPI.transfer function for DMA hangs:
For reference, reverting the last line to my custom SPI.transfer9((void *)ptr, 0, balance, false); works fine. And I know that using the normal 8-bit transfer function won't actually work on my display, but I'm trying it anyway as a sanity check to see if DMA is working before I continue to try and port DMA to a 9-bit function. At the moment this hangs and the Teensy resets after 10 secs or so.
So the 2nd roadblock is making a 9-bit DMA mode. I need to clarify that I don't need to read 9-bit from memory, I just need to write 9-bit to the device, and that ninth bit does not need to change during the write. I never have a need to send a display command via DMA. So the way I've done 9-bit in my SPI.transfer9 function is thus:
But my digging through that 3500 page manual suggests DMA doesn't have a native way of doing anything other than 8/16/32 bit. So my thinking is to bit-bang a bit before sending each 8-bit frame. Possibly requiring adjustments to the DMA pacing. So I'm getting thoroughly out of my depth in the SPI.cpp and DMAChannel.h files, and I started trying to look at bit-banging methods but it seemed ridiculous inserting those into the SPI and DMA libraries when most of the code is setting up pins and timers, both of which should already be exposed in these libraries (_ccr appears to be the private var in SPI.cpp responsible for timing?)
Does anyone have any clues re getting ordinary DMA SPI happening, and then how I might make bit-banging happen within DMA SPI ?
It's 9-bit because I don't use a D/C pin, I run the display in 9-bit mode with the MSB low for C, high for D (I'm in SPI_MODE3, so that might be topsy-turvy for folks using MODE1-MODE2).
Anyway, I want to transition to DMA SPI display writes to not have code blocking for 4ms every time I call a display write (50Hz / 20ms intervals, so 20% of the time my code is blocked by these display writes!).
I have two roadblocks in this quest.
The first is that with the following code, even running the unmodified SPI.transfer function for DMA hangs:
Code:
EventResponder SPI_DMA_Handler;
volatile bool dmaBusy = false;
SPISettings dmaSettings(20000000, MSBFIRST, SPI_MODE3);
...
void SPI_DMA_ready() {
//end screen update
SPI.endTransaction();
digitalWriteFast(oledCS,HIGH);
dmaBusy = false;
}
...
void setup() {
SPI_DMA_Handler.attachImmediate(&SPI_DMA_ready);
}
...
ptr = buff + rowBegin * 128;
balance = (rowEnd + 1 - rowBegin) * 128;
// Send txBuffer to display using SPI DMA
dmaBusy = true;
digitalWriteFast(oledCS,LOW);
SPI.beginTransaction(dmaSettings);
SPI.transfer((void *)ptr, 0, balance, SPI_DMA_Handler);
So the 2nd roadblock is making a 9-bit DMA mode. I need to clarify that I don't need to read 9-bit from memory, I just need to write 9-bit to the device, and that ninth bit does not need to change during the write. I never have a need to send a display command via DMA. So the way I've done 9-bit in my SPI.transfer9 function is thus:
Code:
uint32_t tcr = port().TCR;
port().TCR = (tcr & 0xfffff000) | LPSPI_TCR_FRAMESZ(8); // turn on 9 bit mode
...
while (count > 0) {
uint16_t tempBig = p_write? *p_write++ : _transferWriteFill;
if (!cmd) tempBig += 0x100;
port().TDR = tempBig;
count--;
Does anyone have any clues re getting ordinary DMA SPI happening, and then how I might make bit-banging happen within DMA SPI ?