ADC DMA

cancel
Showing results for 
Search instead for 
Did you mean: 

ADC DMA

741 Views
pascalschröer
Contributor V

Hi at all,

At the moment, I'm writing the last things of my application for my bachelor thesis. For this project

I need to measure every 200us the internal adc. Then I store the measured values into a ringbuffer

and try to send them out to the UART. Well, this application works! But without the use of DMA.

In this project I have got a FTM timer which generates every 200us an interrupt request. Now I

start the adc and wait for the adc complete interrupt. If this interrupt fires, I write the data into the

ringbuffer and send them to the UART.

I think this is a typical application for dma use or? So I have tried to implement it, but it doesen't

work. I have tried to implement some code examples of the community but it doesn't work too.

Can anyone explain what to do and in which order? or does anyone has got an example code for

this specific case?! I use a MK10DX128VLH7 cpu.

Thanks a lot!

Pascal

0 Kudos
5 Replies

175 Views
mjbcswitzerland
Specialist V

Pascal

The uTasker supports ADC circular buffer operation based on DMA. It is documented in http://www.utasker.com/docs/uTasker/uTaskerADC.pdf

It also supports sending the same buffer (after a delay) to the DAC using DMA. Optionally it will also store the samples in a file on an SD card connected via SPI [when no SDHC] (WAV file for audio).

You know where the code is ;-)

It works on all K parts (the next release also on all KL parts - apart from the KL02 which doesn't support DMA...)

Regards

Mark

0 Kudos

175 Views
pascalschröer
Contributor V

Thanks for your answer Mark, but I haven't got understood the whole code of your uTasker project.

So I tried to write a very simple example with the help of your code and the datasheet. This example

should only wait for a the adc complete flag and then write the adc data to my ringbuffer... But if I

debug the program, I get some errors in the DMA_ES register and the dma doesn't work

(TCDn_DADDR is inconsistent with TCDn_ATTR[DSIZE])

Do you knwo what I can do now?

Thanks

Pascal

void ADC_init(void)

    {

    ADC0_SC1A = ADC_SC1_ADCH(0x13);
  
   ADC0_CFG1 |= ADC_CFG1_MODE(0x03);   
   ADC0_CFG1 |= ADC_CFG1_ADIV(0x03);
  
   ADC0_SC1A = ADC_SC1_ADCH(0x13);//1A
  
  
   ADC0_SC3 |= ADC_SC3_ADCO_MASK;
   //ADC0_SC2 |= ADC_SC2_DMAEN_MASK;
  

    }

void DMA_init(void)

{

    SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;

    SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;

    /*Start the sequence*/

    DMA_ERQ |= DMA_ERQ_ERQ0_MASK;    

    /* Set the Source Address*/

    DMA_TCD0_SADDR = (uint32_t)(&ADC0_RA); //UART2_D 0x4006C007

   

    /* Destination address */

    DMA_TCD0_DADDR = (uint32_t)(&UART_RINGBUFFER);

   

    /* Source offset*/

    DMA_TCD0_SOFF = 0; // No offset

   

    /* Destination offset*/

    DMA_TCD0_DOFF = 0; //No offset

   

    DMA_TCD0_ATTR = 0x00

                  | DMA_ATTR_SSIZE(4)

                  | DMA_ATTR_DSIZE(4);   

   

    /* Transfer size */

    DMA_TCD0_NBYTES_MLNO = 4; //4 byte

   

    /* No adjustment to    source address */

    DMA_TCD0_SLAST = 0;

   

    /* No link channel, transactions */

    DMA_TCD0_CITER_ELINKNO = 1; //1 major loop

   

    /* Adjustment to destination address */

    DMA_TCD0_DLASTSGA = 0;

   

    /* No link channel, transactions */

    DMA_TCD0_BITER_ELINKNO = 1; //1 major loop

    DMA_TCD0_CSR = 0;

    DMAMUX_CHCFG0 = 0x00;

   

    DMAMUX_CHCFG0 = 0x00

                  |DMAMUX_CHCFG_ENBL_MASK

                  |DMAMUX_CHCFG_SOURCE(40); //Enable channel 0, Request souurce = ADC0

}

0 Kudos

175 Views
mjbcswitzerland
Specialist V

Pascal

I recommed using short word transfers (unless you have a reason for long word transfer).

When using the eDMA you have to respect its rules:

- all source and destination addresses MUST be aligned (if using 32 bits the last 2 bits of the address MUST bei 00) - check UART_RINGBUFFER address.

- also the transfer count MUST match with the transfer width. If using 32 bit width the total size must be a multiple of this value (4, 8, 12, 16, 20 etc.) If not there will be an error and an abort.

Always check what the error register is telling you - it tells you the reason and whether it was due to the destination or the source. Then you should be able to see why and correct it.

Regards

Mark

0 Kudos

175 Views
pascalschröer
Contributor V

Hi Mark,

I have found a little problem but now it works! I can store some data in my ringbuffer. Now I would like

to connect the adc0 and the UART1 dma. So I tried to do it this way:

void DMA_init(void)

{

    SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;

    SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;

   

    /*Start the sequence*/

    DMA_ERQ |= DMA_ERQ_ERQ0_MASK;    

    /* Set the Source Address*/

    DMA_TCD0_SADDR = (uint32_t)(&ADC0_RA);

   

    //DMA_TCD0_DADDR = (uint32_t)(&UART_RINGBUFFER);            //This case works!

    DMA_TCD0_DADDR = (uint32_t)(&UART1_D);

   

    DMA_TCD0_SOFF = 0; // No offset

   

    DMA_TCD0_DOFF = 2; //No offset

    DMA_TCD0_ATTR = 0x00

                  | DMA_ATTR_SSIZE(1)

                  | DMA_ATTR_DSIZE(1);   

   

    /* Transfer size */

    DMA_TCD0_NBYTES_MLNO = 2; //4 byte

   

    /* No adjustment to    source address */

    DMA_TCD0_SLAST = 0;

   

    DMA_TCD0_CITER_ELINKYES = 0x8A01; //link to the UART1 transmit channel

    //DMA_TCD0_CITER_ELINKNO = 1; //1 major loop

    DMA_TCD0_BITER_ELINKYES = 0x8A01; //link to the UART1 transmit channel

    //DMA_TCD0_BITER_ELINKNO = 1; //1 major loop

   

    DMA_TCD0_DLASTSGA = 0;

    DMA_TCD0_CSR = 0;

    DMAMUX_CHCFG0 = 0x00;

   

    DMAMUX_CHCFG0 = 0x00

                  |DMAMUX_CHCFG_ENBL_MASK

                  |DMAMUX_CHCFG_SOURCE(40); //Request souurce = ADC0

}

The problem is, that the UART1_D register doesn't change. So there isn't a UART

transfere. Do I have to enable something or is there maybe a fault in the ELINKER register?

Because if I enable the disabled comments in the code I can store the adc values in

my ringbuffer without any problems.

Thanks

Pascal

0 Kudos

175 Views
mjbcswitzerland
Specialist V

Pascal

I don't know that copying the ADC value directly to the UART is the best method.

The ADC is best read as a short word since it will have 12..16 byte resolution but a write to the UART data register should be a single byte write. There should also be no destination increment after the write. It is not possible to read a short word and write just one byte (the source and destination should match).

It may be that the UART data register write is working the first time; the UART must be configured correctly in advance to operate. Also you can't read the UART_D register to see whether the write took place because this reads the input (received) byte and not the byte that was written there (it would only read the written byte when the output is connected to the input (loop-back)). This means it is best to watch the data sent by the UART.

You will also need to have the UART set to a speed that allows its data to be sent faster than the ADC is being read - if not the UART output will overrun and there will be data loss.

It may be that you can transfer 16 bits from the ADC with two byte reads and two byte writes; the source would need to increment between the two byte reads to read the low and high register bytes but the destination remain stable in order to write two bytes to the UART_D (minor loop does two transfers and major loop just 1). Since the UART output is buffered it should be able to do this as long as the UART transmission is completed by the next DMA cycle (ADC sampling rate).

Generally it may be more flexible to use the circular buffer to receive ADC sampes to. Then set up a second DMA channel to transfer this data to the UART. This woudl also allow the data in the circular buffer to be processed before sending to the UART (eg. using the DMA half-buffer interrupt).

Regards

Mark