@@ -235,155 +235,146 @@ void SPIClass::transfer(void *buf, size_t count)
235235 }
236236}
237237
238+ // Non-DMA transfer function.
239+ // this was removed from the dma function and made its own
240+ void SPIClass::transfer (void * txbuf, void * rxbuf, size_t count)
241+ {
242+ uint8_t *txbuf8 = (uint8_t *)txbuf,
243+ *rxbuf8 = (uint8_t *)rxbuf;
244+ if (rxbuf8) {
245+ if (txbuf8) {
246+ // Writing and reading simultaneously
247+ while (count--) {
248+ *rxbuf8++ = _p_sercom->transferDataSPI (*txbuf8++);
249+ }
250+ } else {
251+ // Reading only
252+ while (count--) {
253+ *rxbuf8++ = _p_sercom->transferDataSPI (0xFF );
254+ }
255+ }
256+ } else if (txbuf) {
257+ // Writing only
258+ while (count--) {
259+ (void )_p_sercom->transferDataSPI (*txbuf8++);
260+ }
261+ }
262+ }
263+
264+
238265// Pointer to SPIClass object, one per DMA channel.
239266static SPIClass *spiPtr[DMAC_CH_NUM] = { 0 }; // Legit inits list to NULL
240267
241- void SPIClass::dmaCallback (Adafruit_ZeroDMA *dma) {
242- // dmaCallback() receives an Adafruit_ZeroDMA object. From this we can get
243- // a channel number (0 to DMAC_CH_NUM-1, always unique per ZeroDMA object),
244- // then locate the originating SPIClass object using array lookup, setting
245- // the dma_busy element 'false' to indicate end of transfer.
246- spiPtr[dma->getChannel ()]->dma_busy = false ;
268+ // dma callback when the read part is completed
269+ void SPIClass::dmaCallback_read (Adafruit_ZeroDMA *dma)
270+ {
271+ // dmaCallback() receives an Adafruit_ZeroDMA object. From this we can get
272+ // a channel number (0 to DMAC_CH_NUM-1, always unique per ZeroDMA object),
273+ // then locate the originating SPIClass object using array lookup, setting
274+ uint8_t channel = dma->getChannel ();
275+
276+ // flag this part of the dma done
277+ spiPtr[channel]->dma_read_done = true ;
278+
279+ // read and write dmas are both done
280+ if (spiPtr[channel]->dma_read_done && spiPtr[channel]->dma_write_done )
281+ {
282+ // call the callback function the user specified
283+ spiPtr[channel]->userDmaCallback ();
284+ }
285+ }
286+
287+ // dma callback when the write part is completed
288+ void SPIClass::dmaCallback_write (Adafruit_ZeroDMA *dma)
289+ {
290+ // dmaCallback() receives an Adafruit_ZeroDMA object. From this we can get
291+ // a channel number (0 to DMAC_CH_NUM-1, always unique per ZeroDMA object),
292+ // then locate the originating SPIClass object using array lookup, setting
293+ uint8_t channel = dma->getChannel ();
294+
295+ // flag this part of the dma done
296+ spiPtr[channel]->dma_write_done = true ;
297+
298+ // read and write dmas are both done
299+ if (spiPtr[channel]->dma_read_done && spiPtr[channel]->dma_write_done )
300+ {
301+ // call the callback function the user specified
302+ spiPtr[channel]->userDmaCallback ();
303+ }
247304}
248305
249- void SPIClass::transfer (const void * txbuf, void * rxbuf, size_t count,
250- bool block) {
306+ // dma transfer function for spi
307+ // this function does not block, and dma will transfer in the background
308+ // the callback parameter should be passed in by the user, it is called when the dma is done
309+ void SPIClass::transfer (void * txbuf, void * rxbuf, size_t count, void (*functionToCallWhenComplete)(void ) )
310+ {
311+ // save this function to call when the dma is done
312+ userDmaCallback = functionToCallWhenComplete;
251313
252- // If receiving data and the RX DMA channel is not yet allocated...
253- if (rxbuf && (readChannel.getChannel () >= DMAC_CH_NUM)) {
254- if (readChannel.allocate () == DMA_STATUS_OK) {
255- readDescriptor =
256- readChannel.addDescriptor (
314+ // ******************************
315+ // If the RX DMA channel is not yet allocated...
316+ if (readChannel.getChannel () >= DMAC_CH_NUM)
317+ {
318+ if (readChannel.allocate () == DMA_STATUS_OK)
319+ {
320+ readDescriptor = readChannel.addDescriptor (
257321 (void *)getDataRegister (), // Source address (SPI data reg)
258- NULL , // Dest address (set later)
259- 0 , // Count (set later)
322+ rxbuf , // Dest address
323+ count , // Count
260324 DMA_BEAT_SIZE_BYTE, // Bytes/hwords/words
261325 false , // Don't increment source address
262326 true ); // Increment dest address
263327 readChannel.setTrigger (getDMAC_ID_RX ());
264328 readChannel.setAction (DMA_TRIGGER_ACTON_BEAT);
329+ readChannel.setCallback (dmaCallback_read, DMA_CALLBACK_TRANSFER_DONE);
265330 spiPtr[readChannel.getChannel ()] = this ;
266- // Since all RX transfers involve a TX, a
267- // separate callback here is not necessary.
268331 }
269332 }
333+ else
334+ {
335+ // update to use the currently passed buffers
336+ readChannel.changeDescriptor (
337+ readDescriptor,
338+ (void *)getDataRegister (), // Source address (SPI data reg)
339+ rxbuf, // Dest address
340+ count); // Count
341+ }
270342
271- // Unlike the rxbuf check above, where a RX DMA channel is allocated
272- // only if receiving data (and channel not previously alloc'd), the
273- // TX DMA channel is always needed, because even RX-only SPI requires
274- // writing dummy bytes to the peripheral.
275- if (writeChannel.getChannel () >= DMAC_CH_NUM) {
276- if (writeChannel.allocate () == DMA_STATUS_OK) {
277- writeDescriptor =
278- writeChannel.addDescriptor (
279- NULL , // Source address (set later)
343+ // If the TX DMA channel is not yet allocated...
344+ if (writeChannel.getChannel () >= DMAC_CH_NUM)
345+ {
346+ if (writeChannel.allocate () == DMA_STATUS_OK)
347+ {
348+ writeDescriptor = writeChannel.addDescriptor (
349+ txbuf, // Source address
280350 (void *)getDataRegister (), // Dest (SPI data register)
281- 0 , // Count (set later)
351+ count , // Count
282352 DMA_BEAT_SIZE_BYTE, // Bytes/hwords/words
283353 true , // Increment source address
284354 false ); // Don't increment dest address
285355 writeChannel.setTrigger (getDMAC_ID_TX ());
286356 writeChannel.setAction (DMA_TRIGGER_ACTON_BEAT);
287- writeChannel.setCallback (dmaCallback );
357+ writeChannel.setCallback (dmaCallback_write, DMA_CALLBACK_TRANSFER_DONE );
288358 spiPtr[writeChannel.getChannel ()] = this ;
289359 }
290360 }
291-
292- if (writeDescriptor && (readDescriptor || !rxbuf)) {
293- static const uint8_t dum = 0xFF ; // Dummy byte for read-only xfers
294-
295- // Initialize read descriptor dest address to rxbuf
296- if (rxbuf) readDescriptor->DSTADDR .reg = (uint32_t )rxbuf;
297-
298- // If reading only, set up writeDescriptor to issue dummy bytes
299- // (set SRCADDR to &dum and SRCINC to 0). Otherwise, set SRCADDR
300- // to txbuf and SRCINC to 1. Only needed once at start.
301- if (rxbuf && !txbuf) {
302- writeDescriptor->SRCADDR .reg = (uint32_t )&dum;
303- writeDescriptor->BTCTRL .bit .SRCINC = 0 ;
304- } else {
305- writeDescriptor->SRCADDR .reg = (uint32_t )txbuf;
306- writeDescriptor->BTCTRL .bit .SRCINC = 1 ;
307- }
308-
309- while (count > 0 ) {
310- // Maximum bytes per DMA descriptor is 65,535 (NOT 65,536).
311- // We could set up a descriptor chain, but that gets more
312- // complex. For now, instead, break up long transfers into
313- // chunks of 65,535 bytes max...these transfers are all
314- // blocking, regardless of the "block" argument, except
315- // for the last one which will observe the background request.
316- // The fractional part is done first, so for any "partially
317- // blocking" transfers like these at least it's the largest
318- // single-descriptor transfer possible that occurs in the
319- // background, rather than the tail end.
320- int bytesThisPass;
321- bool blockThisPass;
322- if (count > 65535 ) { // Too big for 1 descriptor
323- blockThisPass = true ;
324- bytesThisPass = count % 65535 ; // Fractional part
325- if (!bytesThisPass) bytesThisPass = 65535 ;
326- } else {
327- blockThisPass = block;
328- bytesThisPass = count;
329- }
330-
331- // Issue 'bytesThisPass' bytes...
332- if (rxbuf) {
333- // Reading, or reading + writing.
334- // Set up read descriptor.
335- // Src address doesn't change, only dest & count.
336- // DMA needs address set to END of buffer, so
337- // increment the address now, before the transfer.
338- readDescriptor->DSTADDR .reg += bytesThisPass;
339- readDescriptor->BTCNT .reg = bytesThisPass;
340- // Start the RX job BEFORE the TX job!
341- // That's the whole secret sauce to the two-channel transfer.
342- // Nothing will actually happen until the write channel job
343- // is also started.
344- readChannel.startJob ();
345- }
346- if (txbuf) {
347- // DMA needs address set to END of buffer, so
348- // increment the address now, before the transfer.
349- writeDescriptor->SRCADDR .reg += bytesThisPass;
350- }
351- writeDescriptor->BTCNT .reg = bytesThisPass;
352- dma_busy = true ;
353- writeChannel.startJob ();
354- count -= bytesThisPass;
355- if (blockThisPass) {
356- while (dma_busy);
357- }
358- }
359- } else {
360- // Non-DMA fallback.
361- uint8_t *txbuf8 = (uint8_t *)txbuf,
362- *rxbuf8 = (uint8_t *)rxbuf;
363- if (rxbuf8) {
364- if (txbuf8) {
365- // Writing and reading simultaneously
366- while (count--) {
367- *rxbuf8++ = _p_sercom->transferDataSPI (*txbuf8++);
368- }
369- } else {
370- // Reading only
371- while (count--) {
372- *rxbuf8++ = _p_sercom->transferDataSPI (0xFF );
373- }
374- }
375- } else if (txbuf) {
376- // Writing only
377- while (count--) {
378- (void )_p_sercom->transferDataSPI (*txbuf8++);
379- }
380- }
361+ else
362+ {
363+ // update to use the currently passed buffers
364+ writeChannel.changeDescriptor (
365+ writeDescriptor,
366+ txbuf, // Source address
367+ (void *)getDataRegister (), // Dest (SPI data register)
368+ count); // Count
381369 }
382- }
383370
384- // Waits for a prior in-background DMA transfer to complete.
385- void SPIClass::waitForTransfer (void ) {
386- while (dma_busy);
371+ // ******************************
372+ // clear the flags
373+ // fire the dma transactions
374+ dma_read_done = false ;
375+ dma_write_done = false ;
376+ readChannel.startJob ();
377+ writeChannel.startJob ();
387378}
388379
389380void SPIClass::attachInterrupt () {
0 commit comments