Scatter/Gather emulation and a small problem
Dear Christoph,
again, thank you very much for your library. I am starting to implement it into other libraries and, yes, it is a huge step forward.The broken scatter/gather feature on the chip sucks, but with a very simple fix on your architecture, we can compensate for that. I added another boolean field called "m_bDontDeselect" to the DmaSpi0::Transfer class. The ISR queries this field upon a completed transfer and, if it is set, it doesn't deactivate the CS line. The next transfer can then continue the work of the previous one, effectively concatenating any number of registered transfer objects into a single SPI transaction. So, given an additional constructor for the Transfer object
Code:
Transfer(const uint8_t* pSource = nullptr,
const uint16_t& size = 0,
volatile uint8_t* pDest = nullptr,
const uint8_t& fill = 0,
AbstractChipSelect* cb = nullptr,
boolean dontDeselect = false)
I can now send a single command byte followed by a whole buffer passed from an upper layer, without needing to copy around the buffer:
Code:
void Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) {
uint8_t cmd = ENC28J60_WRITE_BUF_MEM;
DmaSpi0::Transfer trx0(&cmd,1,nullptr,0,chipSelect,true);
DmaSpi0::Transfer trx(data,len,nullptr,0,chipSelect);
DMASPI0.registerTransfer(trx0); DMASPI0.registerTransfer(trx);
}
While this works great, another feature gives me some headache. I took a look at your pause() and resume() methods, that, for some reason, I need to switch between polled and DMA mode. The problem is that when I switch to polled mode and back, the DMA ISR is triggered ONE BYTE BEFORE the end of the transfer. Take a look at the following code:
Code:
while (1) {
uint8_t data[]={0xf0, 0xa5, 0x03};
DmaSpi0::Transfer trx(data,3,nullptr,0,&cs);
DMASPI0.registerTransfer(trx); // transfer 3 bytes through DmaSpi
while (trx.busy()); // wait until complete
DMASPI0.pause(); // switch to polled mode
SPDR=0x0; // transfer a single byte with Paul's avr emulation, don't care about CS
while(!(SPSR&(1<<SPIF))); // wait for transfer to complete
DMASPI0.resume(); // back to DMA mode
DMASPI0.registerTransfer(trx); // 3 bytes through DmaSpi
while (trx.busy()); // wait until complete
}
Please take a look at the attached scope picture, that shows CS and CLK. The first transfer (3 bytes through DmaSpi) correctly lowers and raises CS before and after the transfer. The second transfer sends out a single byte, but doesn't care about CS. However, after that, all following DmaSpi transfers raise the CS pin during the still ongoing transfer.
Actually, the DMA ISR gets called after the SECOND LAST BYTE. This seems to be independent from the number of bytes sent before through DmaSpi or through polling. Any number of bytes written directly to the (emulated or not) SPI registers lead to a premature ISR trigger ONE BYTE before the end of the transfer.
Do you have an explanation or solution for this?
Regards
Jorg