raidersnake's ADC multiplexing
Source: SAMD51 ADC0 and ADC1 Multiplexing with DMA - Hardware / Arduino Zero - Arduino Forum
This one's interesting because raidersnake not only had a very similar objective, but also solved the issue of programming the DMAC and using SPI/SD card in the same sketch without locking up the board. However, it came with some errors that stop ADC1 from functioning continuously without some tweaks. Even then, ADC1 does not seem to be giving real results.
As of writing this, a modified version exists in the sandbox_DMA branch as dma_ADC_linked_descriptors
- commit 8d065dcaa65aab43ac917459d1054ec46d87ec81
High Level Overview
TODO
- Utilizes ADC's DMA Sequencing to auto-update inputs (I think).
- Utilizes the ADC Host-Client mode so ADC1 follows ADC0's configuration.
- Utilizes DMAC to gather results into 2 buffers - one per ADC.
-
Channel 0
-
Channel 1
- Channel 2
- Used for DMA Sequencing
- Channel 3
- Used for grabbing ADC0's results
- Circular; This descriptor points to Channel 0's descriptor
- Channel 4
- Used for grabbing ADC1's results
- Circular; This descriptor points to Channel 1's descriptor
-
Globals and Fluff
Code starts with SD setup. Each ADC gets its own buffer to store results. The buffer is then split between each pin.
Then, a 4 element array is initialized with the pins for the ADC multiplexor. These are the input pins to be read from.
Next, a simple descriptor struct (dmacdescriptor
) is declared.
Array pointers are made for the writeback section and descriptor section to reside in SRAM. There's also a declaration for descriptor
, which will be used to make copying descriptors over to the descriptor section easier. There's also a 2 element array of dmacdescriptor
s made in preparation to link the two active ones together.
setup()
The SD/SPI stuff is initialized with the parameters from above. Since the SPI library has its own DMAC setup, the following lines are used to realign the descriptor section and writeback section addresses to those set by the SPI library:
descriptor_section = reinterpret_cast<dmacdescriptor(*)[DMAC_CH_NUM]>(DMAC->BASEADDR.reg); //point array pointer to BASEADDR defined by SD.begin
wrb = reinterpret_cast<dmacdescriptor(*)[DMAC_CH_NUM]>(DMAC->WRBADDR.reg); //point array pointer to WRBADDR defined by SD.begin
The DMAC is then enabled. At the same time, all priority levels are enabled via:
DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);
Referencing the datasheet and the definition for DMAC_CTRL_LVLEN
, the latter term in the bitwise or
operator sets the priority-level-enable bits all to 1
.
ADC DMA Sequencing Setup
DMAC Channel 2's priority is set to 3 (highest). It's trigger source is set to ADC0's DSEQ
trigger. The conditions for this trigger are shown in Section 45.6.3.4 of the SAMD51 datasheet. When this trigger request comes, it triggers a burst transfer.
Next, the empty descriptor
is configured. The next descriptor address is set to itself, creating a circular descriptor., and dstaddr
is . The descriptor is configured according to datasheet Section 45.6.3.4 for DMA Sequencing. The descriptor is then set to be valid.
descriptor
is modified in order to make copying easier:
- descaddr: Set to itself. Circular descriptor
- srcaddr: Set to the end of the
inputCtrl0
array. For some reason, the 4 bytes afterinputCtrl0
are considered relevant as the sequencing control data. - dstaddr: Set to ADC0's DSEQDATA register
- btcnt: Beat count is set to 4 beats.
- btctrl:
- Beat size is set to a word (32 bits)
- The source address is set to be incremented
- Step Selection settings are applied to the source address
- Step Size is set to 1 beat
DMAC ADC0 Setup
DMAC Channel 3 is set to priority level 3. It's trigger source is set to ADC0's RESRDY
interrupt, which triggers when a ADC0 result is ready. Channel 3 is configured to do burst transfers.
descriptor
is modified again in order to make copying easier:
- descaddr: Next descriptor is set to Descriptor 0's address
- srcaddr: Source is set to ADC0's RESULT register
- dstaddr: Destination is set to the halfway point of
adcResults0
- btcnt: Number of beats is 1024 for now; Each pin gets 1024 samples
- btctrl:
- Beat size is set to a half-word (16 bits) which easily fits our 12 bit results
- Set to increment the destination address; will fill up the buffer properly and not just overwrite an element over and over.
- The Descriptor is set as valid
- This channel (3) is set to suspend once the block transfer is completed.
This descriptor is then copied to Channel 3 of the descriptor section.
descriptor
is modified again in order to make copying easier:
- descaddr: Next descriptor is set to Descriptor 3's address - look above
- srcaddr: Source is set to ADC0's RESULT register
- dstaddr: Destination is set to the start of
adcResults0
- btcnt: Number of beats is set to 1024.
- btctrl:
- Beat size is set to a half-word (16 bits) which easily fits our 12 bit results
- Set to increment the destination address; will fill up the buffer properly and not just overwrite an element over and over.
- The Descriptor is set as valid
- This channel (0) is set to suspend once the block transfer is completed.
This descriptor is then copied to Channel 0 of the descriptor section.
DMAC ADC1 Setup
DMAC Channel 4 is set to priority level 3. It's trigger source is set to ADC0's RESRDY
interrupt, which triggers when a ADC1 result is ready. Channel 4 is configured to do burst transfers.
descriptor
is modified again in order to make copying easier:
- descaddr: Next descriptor is set to *Descriptor 1's address
- srcaddr: Source is set to ADC1's RESULT register
- dstaddr: Destination is set to the halfway point of `adcResults1
- btcnt: Number of beats is 1024 for now; Each pin gets 1024 samples
- btctrl:
- Beat size is set to a half-word (16 bits) which easily fits our 12 bit results
- Set to increment the destination address; will fill up the buffer properly and not just overwrite an element over and over.
- The Descriptor is set as valid
- This channel (4) is set to suspend once the block transfer is completed.
This descriptor is then copied to Channel 4 of the descriptor section.
descriptor
is modified again in order to make copying easier:
- descaddr: Next descriptor is set to Descriptor 4's address - look above
- srcaddr: Source is set to ADC1's RESULT register
- dstaddr: Destination is set to the start of
adcResults1
- btcnt: Number of beats is set to 1024.
- btctrl:
- Beat size is set to a half-word (16 bits) which easily fits our 12 bit results
- Set to increment the destination address; will fill up the buffer properly and not just overwrite an element over and over.
- The Descriptor is set as valid
- This channel (1) is set to suspend once the block transfer is completed.
This descriptor is then copied to Channel 1 of the descriptor section.