The goal of this example is to read all ADC inputs of Kinetis KL25 in a row without the need of using CPU core for switching channels and pins and reading individual values.
The FRDM-KL25 board features Kinetis MKL25Z128VLK4 microcontroller. This MCU contains a 16-bit AD converter with 16 inputs.
In Processor Expert there is available ADC_LDD component which can be used for measuring values on these pins. However, there is a limitation that some of the input pins (e.g. ADC0_SE4a and ADC0_SE4b) are muxed to same channel and ADC_LDD doesn’t allow to measure such two pins at once without additional mux-switching code. The MCU also does not provide an option of scanning through the ADC channels and the channels need to be switched by the user. To resolve the goal of measuring all inputs in a row the Peripheral Initialization components and DMA (Direct Memory Access) peripheral can be used.
Note: The archive with the example project is attached to this article.
The DMA in this example is used for controlling all the channel switching, pin mux selection and reading the results for series of measurement into a memory buffer.
Results are written to serial console (virtual serial port) provided by the FRDM board.
The Direct Memory Access (DMA) channels are configured for writing and reading ADC registers the following way:
The data for the DMA channel 1 a 2 are prepared in the ChannelsCfg and ChannelsCfg2 arrays prepared in memory and the DMA operates in the following cycle:
In the beginning, the DMA channel 1 transfer is started using software trigger. This selects the pin (a/b).
Then, DMA channel 2 is immediately executed because of enabled DMA channel linking. This configures the channel and starts the conversion.
After the conversion is complete, the result is read by DMA channel 0 and stored to results array. Channel linking executes the Channel 1 transfer and the cycle continues.
After all needed channels are measured (DMA byte counter reaches 0), the DMA Interrupt is invoked so the user code is notified.
See the following figure describing the process:
The application uses generated driver modules from the following Processor Expert components:
This component redirects printf command output to FRDM USB virtual serial port which is connected to UART0 pins PTA1/UART0_RX and PTA2/UART0_TX.
The serial device, speed and pins are configured in inherited Serial_LDD component.
The Init_ADC provides ADC initialization code with all channels enabled and set to single-ended.
The clock can be selected according to any valid value, according to the user needs.The same with HW average settings.
Compare functionality will not be used in this demo.
Pins configuration - all pins available on the board are enabled:
Interrupts, DMA and Triggering - Interrupts are disabled, DMA request is enabled. Triggering is disabled, as it’s not used in this demo project, However, the application could be extended to use it.
The Init_DMA provides provides initialization code for the DMA.
Clock gate and DMA multiplexor are enabled:
Property values:
The channels/pins to be measured are specified in the ChannelsCfg and ChannelsCfg2 arrays.
These arrays contains a list of pins to be measured, the order can be changed according to the user needs, the channels can even be measured multiple times.
The special value 0x1F stops the conversion.
// configuration array for channels - channel numbers. Should ends with 0x1F which stops conversion
// seconcd onfiguration array coreesponding to channels selecting A/B pins
// For example: 0 + PIN_A corresponds to the pin ADC0_SE0, 5 + PIN_5 selects the pin ADC0_SE5b
// You can use these arrays to reorder the measurement as you need
const uint8_t ChannelsCfg [ADC_CHANNELS_COUNT + 1] = { 0, 4, 3, 7, 4, 23, 8, 9, 11, 12, 13, 14, 15, 5, 6, 7, 0x1F };
const uint8_t ChannelsCfg2[ADC_CHANNELS_COUNT + 1] = {PIN_A, PIN_A, PIN_A, PIN_A, PIN_B, PIN_A, PIN_A, PIN_A, PIN_A, PIN_A, PIN_A, PIN_A, PIN_A, PIN_B, PIN_B, PIN_B, 0 };
In the main loop, the application first re-initializes the DMA values and strarts the sequence by software triggerring the DMA channel 1.
// loop
while (TRUE) {
// clear flag
Measured = FALSE;
// reset DMA0 destination pointer to beginning of the buffer
DMA_DAR0 = (uint32_t) &MeasuredValues;
// reset DMA1 source pointer (MUX switching writes)
DMA_SAR1 = (uint32_t) &ChannelsCfg2;
// reset DMA2 source pointer (channel switching and conversion start writes)
DMA_SAR2 = (uint32_t) &ChannelsCfg;
// number of total bytes to be transfered from the ADC result register A
DMA_DSR_BCR0 = ADC_CHANNELS_COUNT * 2;
// set number of total bytes to be transfered to the ADC0_CFG2
DMA_DSR_BCR1 = ADC_CHANNELS_COUNT + 1;
// set number of total bytes to be transfered to the ADC0_SC1A.
DMA_DSR_BCR2 = ADC_CHANNELS_COUNT + 1;
// start first DMA1 transfer (selects mux, then fires channel 2 to select channel which starts the conversion)
DMA_DCR1 |= DMA_DCR_START_MASK;
// wait till it's all measured
while (!Measured) {}
// print all measured values to console
for (i=0; i<ADC_CHANNELS_COUNT; i++) {
printf ("%7u", (uint16_t) MeasuredValues[i]);
}
printf ("\n");
// reset the counter
TU1_ResetCounter(TU1_DeviceData);
// wait for some time to slow down output
while (TU1_GetCounterValue(TU1_DeviceData) < 50000) {}
}
The project can be run usual way.
One question : which is the maximum sampling frequency that this system can reach?
Hi Petr_H
I have been trying to build a recording system with one ADC channel. I have to perform Analog to Digital Conversion and then send my data to the computer (using serial communication e.g.). Firstly I used the ADC component that is available in the processor expert that is working well, however when I send this data to the computer using the serial communication (printf and scanf function) I don't get the expected data.
I am trying now using DMA in order to improve mcu(KL26Z)-computer communication. I tried to adapt your code but I was not successful. Could you please give me some hints?
Thank you in advance!
See this thread : https://community.freescale.com/message/462300?et=watches.email.thread#462300
I also try to use DMA with K70 with some problems with DMA+PDB+ADC.
Thanks for sharing! This is the most lightweight ADC driver i've seen for the Kinetis KLZ25. If anyone is using this they are probably interested in speed. The Low Device Driver (LLD) in PE (Processor Expert) are simply too bloated to achieve max speed. I also couldn't get their DMA option to work without error-ing out. However, yours worked!
With your code I modified it to 8bit sample on 3 channels w/ clk speed of 16mhz. All speed features enabled. However, I could only effectively get ~100ksps(~10us for all 3 channels) when taking into account reloading the DMA. I used the done interrupt to reload the DMA. Ideally i'd like to get 200kbs+ . Any idea what I could do to speed it up? I didn't see a huge performance increase when going from 16bit 12mhz to 8bit 16mhz which seemed odd. Max processor optimizations are turned on to help a bit.
Hi NXP,
I can't download your sample code, it has been removed. Could you upload it again?
in addition, can I follow this ways on MKL17 MCU to measuring 19ch ADC?
Thanks,
Leo
Hi,
It seems that the link somehow stopped working, probably during the community migration.
Luckily I found it archived and uploaded it again, it's now available here: KL25_MultiADC_DMA.zip
best regards
Petr Hradsky