Great, we have clocking on our ADC that will be compatible with our sampling rate, now all we need to do is turn it on and let the data flow right? Ah, nope. Where are you going to put all this data and how are you going to deal with it all? Good question. When you are dealing with a massive amount of data that flows in from the ADC, you can have your MCU pre-occupied with the sole task of handling the data but this becomes apparent quickly that your MCU can not do much of anything else. We need to buy time for the MCU to perform additional tasks. That is where DMA comes in. You'll notice that most MCUs that have the capability of large sample rates have a DMA controller attached to the peripheral as well. Let's put it to use and store all of this DATA!
Modules with a DMA controller contains a single word buffer. Why would they only include a single buffer? Because you have something more powerful - DMA.
In the dsPIC33 datasheet in the ADC section, the first mention of DMA comes with bit 12, the DMA Buffer Build Mode bit.
What Scatter-gather mode is comes from the idea that DMA doesn't necessarily have to have a continuous block of data. In this special memory to memory mode data can be collated together. When DMA is in order of conversion mode, 1, then you have a single linear block that has a start and end shape. The default is scatter / gather and has the advantage of assembling the data in an incremental fashion and for most purposes serves us well. So leaving this bit at default will give us this advantage.
Another configuration we need to concern ourselves with is how the DMA will increment the memory addresses when a conversion is done. This can be found in the register ADxCON2:
Well this is all good and such, but what does it mean? This defines a number of locations that will be written in the resulting buffer starting with ADCxBUF0. This can vary from one sample to 16 samples. For example, if the SMPI<3:0> bits = 0000, the conversion results will always be written to ADCxBUF0. In this example, no other buffer locations would be used.
We also need to decide on how much space will be allocated the DMA buffer for storage. This control is found in the ADxCON4 register:
Let's put together a DMA storage of 8 words of buffering and store the conversion in BUF0.
ADC1CON4 |= (0x03 << 2); // Shifts 0b011 or 0x03 into bit position 2 for selecting our storage option
All of the other registers we can leave at the defaults.
Can I flip the Switch?
So far we have have an ADC that is clocked and ready. We have some DMA options selected, are we done? Nope. We still need to select the channel we are going to be sampling and we also need to enable the DMA controller so that it can start working as well.
Let's assume a channel 0 sampling:
AD1CON1 |= (1 << AD12B); // Enable a 12 bit 1 channel ADC operation AD1CON1 |= (1 << ASAM); // Sampling will start again after last conversion is completed AD1CON2 |= (1 << CSCNA); // Scan input for channel 0 AD1CON3 |= 0xA6; // Clock selection of system clock source and clock select bits of 166 ADC1CON4 |= (0x03 << 2); // Shifts 0b011 or 3 into bit position 2 for selecting our storage option // Here's the magic AD1CON1 |= (1 << ADON); // Enable the ADC
If the above registers are set in an ADC initialization call, we would have a 12 bit, single channel ADC, free running conversions one after another, reading at 70kHz, and ready to have it's data transferred via DMA. The last line turns on the ADC but will it work? Not really. We need to setup the DMA and final destination for the data to be analyzed.
We looked a little more into the ADC details and how to prepare ourselves for the massive flow of data. Paying close attention to the datasheet and the description of each of the bits can help you understand how the complex peripherals such as ADCs can be tamed.