Table of Contents
2 DMA to emulate ADC Flexible Scan.
2.1 System overview and flow diagram for Flexscan.
3 SDK implementation.
3.1 ADC configuration.
3.2 LPMTR configuration
3.3 DMA configuration
4 ADC Flex Scan mode with DMA
Appendix A: Requirements
This document describes how to combine ADC, DMA and a timer to implement a ADC Flexible storing data with SDK 2.2 and MCUXpresso IDE. In this configuration ADC measures will be stored into an internal memory buffer with DMA, which imply, no CPU. With this configuration MCU uses less resources, only using ADC and DMA (2 channels) and a timer to trigger conversions. This way, the MCU does not need to read the ADC result register, because the memory management will be done by DMA automatically.
To implement this application we will use SDK libraries, which includes ADC and DMA libraries. The timer used to trigger ADC conversion will be the LPTMR. MCUXpresso IDE will be used as development environment, please check Appendix A to look where you can download SDK libraries and IDE. The project will be created from scratch, regardless of this, MCUXpresso IDE and SDK libraries allows us to create a project with some headers and libraries in the project, if you have problems creating a new project in MCUXpresso please check Reference 4.
In this document we will use the FRDM-K64F which is an ultra-low-cost development platform, but this implementation could be easily migrated to any kinetis family that support second-generation eDMA module (enhanced Direct Memory Access).
This document is an update of the community doc:
which uses old SDK libraries.
Flexible scan implementation needs 2 types of data movement: ADC measures (from peripheral to internal memory) and ADC mux change (from internal memory to peripheral). Second one is needed to change ADC input channel and have the flexible implementation, first one save ADC measures in our internal memory.
DMA peripheral (for this MCU) includes up to 16 channels, for this application we will only use 2 channels, one for ADC measures and one for ADC muxing channel. It is important to remark that we will use the linking feature in these channels to implement the ADC changing channel automatically, later in this document it will be explain this feature.
As already mentioned, this implementation will use one DMA channel to transfer data from ADC measures (ADC0_RA register in this case) to a memory buffer (named here as g_ADC0_resultBuffer), so follow figure shows this movement:
As you can see, ADC0_COCO (conversion complete) will be the one that request a transfer from ADC0_RA to resultBuffer. Once this transfer has completed, we need to change ADC channel, so when DMA channel 1 transfer finishes, it will indicate us to trigger the other data movement, in this case from internal buffer g_ADC_mux (that save our adc channels) to the ADC0_SC1A register that sets the ADC channel, so the following diagram shows this.
To do this, this application used a DMA feature that link DMA channels, channel-to-channel linking. So, when one DMA channel finished, the linked DMA channel will be triggered, in this case when DMA channel 1 has completed to move data from ADC measure register to internal memory then DMA channel 0 need to be triggered to change the ADC channel, so next conversion ADC would have selected other ADC channel.
Once that the ADC channels is changed (and the HW trigger happens), the COCO flag from ADC will trigger other DMA request in channel 0 and the process will start again. This flow process can be repeated as many channels as we have and as many samples as we want, for this example code we will measure 3 channels and have 4 samples, so the result buffer size is 3 × 4 = 12 (the real buffer size is 16, to demonstrate that only 12 data field parts are written).
The ADC works in hardware trigger mode, with the LPTMR timer working as the trigger source. This mean that even though DMA channel 0 changed the ADC input channel, conversion will not start until HW trigger (LPTMR) start the conversion, this mean that the flow of the program can be as follow.
In the example code provided, when DMA channel 0 has changed 3 times the ADC channel, DMA channel 1 major loop will happen, and when the sample X is the last sample (sample 4) of the last channel in muxbuffer, then Major loop in DMA channel 0 happens and the application ends (or it could start again). This mean that the application here can be shown as:
Implementation of this code can be dived in three parts; LPTMR, ADC and DMA configuration.
ADC configuration is a normal ADC config, with 12 single ended, Asynchronous clock source and Vref as reference voltage. Hardware trigger and DMA support are enable to trigger conversions with LPTMR and to be able to trigger a DMA request when COCO interrupt happen. In ADC channel configuration, it is enable Conversion completed interrupt and it is loaded the value of g_ADC_mux to channel number, this will be the first channel that LPTMR will trigger.
LPMTR is a common configuration with LPTMR as time counter. Please notice that prescaler could be used and select a prescaler clock. To set LPTMR period it is used the macro USEC_TO_COUNT, which just take the value in microseconds and it calculate the number of ticks to count, using the source clock. Also at the end, LTPMR is configured to be used as HW trigger for ADC conversions in the SIM register.
For DMAMUX configuration it is enable Channel 0 and 1 and it is selected the source trigger, DMA for channel 0 (it will be triggered with linking feature) and ADC COCO flag for channel 1 (trigger when the ADC conversion complete).
For DMA configuration, it is needed to create 2 tcd configuration, there are set in transferConfig_chx. First one defined is for DMA channel 1 (data from ADC measure to internal memory), and the second is for DMA channel 0 (data from internal memory to ADC_SC1 register to change ADC channel). Please noticed that these definitions are in bytes to transfer, so some of them are sizeof(uint16_t). Here is also the setup to link channel 1 to channel 0.
Finally, it is added the adjustment for TCD, so when DMA major loop finished in both, it will point to the start of the source and destination. Noticed that this is added just for internal memory address, this is because for the case of a peripheral (ADC in this case), pointer to address doesn’t change.
Then DMA channel 1 and LPTMR are started.
When DMA channels were initialized, their callbacks were also defined;
This flags are just used for this implementation and as reference, they can be removed if needed. After Major loop has finished, SDK implementation disable DMA transfers automatically, so noticed that the callback_1 for DMA channel 1 has a line commented, that if uncommented, this application will act as a ADC that load measurements in an internal Ring buffer Indefinitely.
With a basic Print implementation of ADC results we can show the functionality of this example project.
There are obtained the following results,
Go to Build an SDK, select your device and click on Specify Additional Configuration Settings, verify that you have selected KDS as toolchain and then Go to SDK Builder. Click in Download Now, accept Term and conditions and download SDK packet. Please check: Generating a downloadable MCUXpresso SDK v.2 package