@@ -235,155 +235,146 @@ void SPIClass::transfer(void *buf, size_t count)
235
235
}
236
236
}
237
237
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
+
238
265
// Pointer to SPIClass object, one per DMA channel.
239
266
static SPIClass *spiPtr[DMAC_CH_NUM] = { 0 }; // Legit inits list to NULL
240
267
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
+ }
247
304
}
248
305
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;
251
313
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 (
257
321
(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
260
324
DMA_BEAT_SIZE_BYTE, // Bytes/hwords/words
261
325
false , // Don't increment source address
262
326
true ); // Increment dest address
263
327
readChannel.setTrigger (getDMAC_ID_RX ());
264
328
readChannel.setAction (DMA_TRIGGER_ACTON_BEAT);
329
+ readChannel.setCallback (dmaCallback_read, DMA_CALLBACK_TRANSFER_DONE);
265
330
spiPtr[readChannel.getChannel ()] = this ;
266
- // Since all RX transfers involve a TX, a
267
- // separate callback here is not necessary.
268
331
}
269
332
}
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
+ }
270
342
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
280
350
(void *)getDataRegister (), // Dest (SPI data register)
281
- 0 , // Count (set later)
351
+ count , // Count
282
352
DMA_BEAT_SIZE_BYTE, // Bytes/hwords/words
283
353
true , // Increment source address
284
354
false ); // Don't increment dest address
285
355
writeChannel.setTrigger (getDMAC_ID_TX ());
286
356
writeChannel.setAction (DMA_TRIGGER_ACTON_BEAT);
287
- writeChannel.setCallback (dmaCallback );
357
+ writeChannel.setCallback (dmaCallback_write, DMA_CALLBACK_TRANSFER_DONE );
288
358
spiPtr[writeChannel.getChannel ()] = this ;
289
359
}
290
360
}
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
381
369
}
382
- }
383
370
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 ();
387
378
}
388
379
389
380
void SPIClass::attachInterrupt () {
0 commit comments