Measuring all ADC inputs on FRDM-KL25 using DMA

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Measuring all ADC inputs on FRDM-KL25 using DMA

Measuring all ADC inputs on FRDM-KL25 using DMA

Introduction

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.

Project Description

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:

  • DMA channel 0 reads converted results (ADC0_RA register)
  • DMA channel 1 changes the ADC pin group selection multiplexer (ADC0_CFG2 register) using values from memory array ChannelsCfg
  • DMA channel 2 selects the ADC channel and starts conversion (ADC0_SC1A register) using values from memory array ChannelsCfg2


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:

block diagra.png

Component Configuration

The application uses generated driver modules from the following Processor Expert components:

ConsoleIO properties setup

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.

1.png

2 serial_ldd.png


Init_ADC properties setup

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.

          3 init_adc 1.png

Pins configuration - all pins available on the board are enabled:

          3 init_adc pins.png


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.

          3 init_adc zz_interrupts.png


Init_DMA properties setup

The Init_DMA provides provides initialization code for the DMA.

Clock gate and DMA multiplexor are enabled:

          dma common.png


DMA Channel 0

  • 16-bit results are transferred from ADC0_RA register (see proprerty Data source / Address).
  • Transfer mode is Cycle-steal, which means that only one transaction is done per each external request.
  • The destination address initial value is not filled in the inspector because it's filled repeatedly in the application code.
  • Channel linking is set to trigger channel 1 after each transfer
  • DMA mux settings for the Channel 0 are enabled and ADC0_DMA_Request is selected, which is the signal from ADC when the conversion ends.
  • "DMA transfer done" interrupt for this channel is enabled. The ADCint ISR function will be called.
  • External request (request from ADC) is Enabled to start the transfer. Byte count will also be changed before every sequence

Property values:

          dma ch0 setup.png

         

DMA Channel 1

  • DMA channel 1 changes the ADC pin group selection multiplexer (ADC0_CFG2 register) using values from memory array ChannelsCfg.
  • Please note that the source address initial value is not filled, will be set in the application code along with the Byte count value.
  • There is no HW trigger for this channel, it's set to be triggered by SW only (and by linking mechanism, which will be used).
  • The linking from this channel is set to trigger CH2 after the transfer.
  • No interrupt is enabled for this channel


               dma ch1 setup.png


DMA Channel 2

  • DMA channel 2 selects the ADC channel and starts conversion (ADC0_SC1A register) using values from memory array ChannelsCfg2.
  • No channel is linked after the transfer ends - No link.
  • No external channel request is selected, this channel transfer is triggered by linking from CH2.

               dma ch2 setup.png

TimerUnit_LDD

  • It's used in the application code to provide delay to slow down console output.
  • The TPM0 counter is used with the period of approx. 350ms.
  • No interurpt is used.
  • Auto initialization is enabled.

               TimerUnit.png

Code

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) {}

}


Running the project

The project can be run usual way.

  • import the project into CodeWarrior for MCUs V10.5.
  • Build the project
  • Connect the FRDM-KL25 board
  • Start debuging and run the code
  • Run terminal application or use the Terminal view in eclpise. Set it to use the virtual serial port created for the board. The parameters should be set to 38400,no parity, 8 bits ,1 stopbit.
Attachments
Comments

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 

100% helpful (1/1)
Version history
Last update:
‎09-27-2013 03:32 AM
Updated by: