This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS570LS1224: ADC with DMA (spna227)

Part Number: TMS570LS1224
Other Parts Discussed in Thread: HALCOGEN

Tool/software:

Hi (Jagadish),

I have been following the span227 to implement ADC with DMA. I need to get 20 samples from a specific (single) channel and store them to the RAM. To make things simpler, I started with triggering a DMA request over a single sample.

Fullscreen
1
2
3
4
5
6
7
8
9
//
// Inside void main()
//
adcInit();
adcREG1->ADG1CHNSELMODECTRL = 0x0A; // enable enhanced channel selection mode for group1
adcREG1->ADG1MAXCOUNT = 1; // 1 conversion total
adcREG1->ADG1CURRCOUNT = 0; // clear group1 current_count counter
adcREG1->G1DMACR = 0x09; // DMA request on group1 conversion end
adcEnableNotification(adcREG1, adcGROUP1);
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//
// Inside void main()
//
dmaEnable();
dmaReqAssign(1, 10);
dmaConfigCtrlPacket( ADG1_ptr, // control packet for group1
(uint32)(&adcREG1->GxBUF[1]), // group1 results FIFO
(uint32)(&adc1_G1_results), // group1 results in CPU data RAM
1, // number of frames to be transferred
1, // number of elements per frame
0, // next channel to be chained, 0 = disable chaining
2, // read element size (32 bits)
2, // write element size (32 bits)
1, // transfer type = 1 ==> block transfer
0, // read addressing mode = 0 ==> fixed
1, // write addressing mode = 1 ==> post-incremented
1, // autoinit enabled
0, // read address element index offset = 0
0, // write address element index offset = 0
0, // read address frame index offset = 0
0 // write address frame index offset = 0
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

And printing the results in a task with sufficient stack allocation:

Fullscreen
1
2
3
4
5
6
7
8
9
10
//
// Inside task
//
while (1)
{
adcStartConversion(adcREG1, adcGROUP1);
printf("ADC: %u\n", adc1_G1_results[0]);
vTaskDelay(xDelay);
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

After playing with different number of elements per frame (1, 5, 20), I was able see with the debugger that the DMA engine transfers the requested count into the destination RAM buffer. I do not use continuous conversion - after reading the response from  in the following thread: e2e.ti.com/.../tms570lc4357-dma-with-adc-in-continuous-mode

Unfortunately, I get really weird results from the phototransistor on the Launchpad kit, channel 6, without changing the illumination level. I know it prints the whole uint32 including the flags and channel ID, but still quite large difference in the readings (in the context of a single constant channel). It looks as follows:

Fullscreen
1
2
3
4
5
6
7
8
9
10
ADC: 0
ADC: 68347
ADC: 68347
ADC: 68347
ADC: 68348
ADC: 2812
ADC: 2811
ADC: 68347
ADC: 2812
ADC: 2811
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

I am also struggling to understand the following:

1. Neither the ADC, nor the DMA interrupt notifications get triggered. I tried not only with the HALCoGen functions (as in the above snippets), but also by assigning the proper bit field values in the INT Enable registers.

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
void dmaGroupANotification(dmaInterrupt_t inttype, uint32 channel)
{
sciSendByte(scilinREG, 'D');
return;
}
void adcNotification(adcBASE_t *adc, uint32 group)
{
sciSendByte(scilinREG, 'A');
return;
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

My GIO and SCI interrupts (FIQ/IRQ) work properly (in FreeRTOS environment).

2. What is the relation between the ADC storage objects mentioned in the API and the TRM:

  • (API)   GxBUF[...] - array of (8 * (uint32_t)) = (8 * 4Bytes) = 32Bytes per group
  • (TRM) ADC FIFO RAM - "Conversion results are stored in a 64-word memory (SRAM)." That means 64 * 4 = 256 bytes in total and ~85Bytes per group
  • (TRM) "ADBNDEND contains a 3-bit field called BNDEND that configures the total memory available. The ADC
    module can support up to 1024 buffers. The device supports a maximum of 64 buffers for both the ADC
    modules."

3. Based on the above, what should be taken into consideration for the DMA transfer? How should the ADC FIFO be configured for optimal DMA transfer efficiency?

I hope my post is not that overwhelming, but I literally spent about a week on research and I am going nowhere.

Any other up-to-date example code will be much appreciated as well...

Thanks for the understanding and kind regards,

Varban

  • Hi Varban,

    My suggestion would be trying to use below highlighted method in ADC DMA.

    You need 20 samples each time right, so configure block size as twenty, so in this way the DMA will get ADC trigger after 20 conversions. Once DMA gets the trigger from ADC, we should configure DMA in such way that its number of elements as 1 and number of frames as 20, once we did this then DMA will transfer all these 20 converted results to a RAM from ADC memory. Once the DMA completes transfer of 20 converted result then it will trigger you a block transfer complete interrupt.

    Once you get DMA block transfer interrupt then you can perform required operations on ADC data in the RAM which is shifted by DMA.

    I don't have any direct example for this but try to implement on your end and if you get any difficulty in implementation i will try to assist you further.

    --
    Thanks & regards,
    Jagadish.

  • EDITED

    Hi Jagadish,

    Thanks for the directions and sorry for my delayed response.

    So, that is how I understood the suggested approach. Based on the below HALCoGen configuration:

    I wrote the following:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    xTaskHandle xUserTaskHandle;
    void vUserTask(void *pvParameters)
    {
    const TickType_t xDelay = 500 / portTICK_PERIOD_MS;
    uint16_t n = 0;
    while (1)
    {
    adcStartConversion(adcREG1, adcGROUP1);
    for (n = 0; n < ADC_SAMPLE_COUNT; n++)
    {
    printf("[%u] Ch: %u, ADC: %u\n", n,
    (uint32)((adc1_G1_results[n] >> 16U) & 0x1FU), // Channel ID
    (uint16)(adc1_G1_results[n] & 0xFFFU)); // Value
    }
    printf("----------------\n");
    vTaskDelay(xDelay);
    }
    }
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    And a "zoom" of the ADC DMA configuration register:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #define ADC_SAMPLE_COUNT 32U // Equal to the configured FIFO size in HALCoGen
    ...
    adcREG1->G1DMACR = (1U << 0U) | // G1_DMA_EN - ADC module generates a DMA transfer when the ADC has written to the Group1 memory. G1_BLK_XFER bit must be cleared to ‘0’ for this DMA request to be generated.
    (1U << 2U) | // G1_BLK_XFER - ADC module generates a DMA request when the ADC has written G1_BLOCKS number of buffers into the Group1 memory.
    (1U << 3U) | // DMA_G1_END - ADC module generates a DMA request when the ADC has completed the conversions for all channels selected for conversion in the group1.
    (ADC_SAMPLE_COUNT << 16U); // G1_BLOCKS - One DMA request is generated if the G1_BLK_XFER is set to ‘1’ and the specified number of Group1 conversion results have been accumulated
    // ADC_SAMPLE_COUNT = 32U, equal to the configured FIFO size inside HALCoGen
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    Although I ended up with DMA transfers, I still have the following concerns and observations.

    1. Buffer content

    Edited after my initial mistake with the hard-coded index 0 in the loop (sorry for the confusion).

    The result is the following - only one value is sampled, all the others in the buffer are 0s.

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    ----------------
    [0] Ch: 6, ADC: 420
    [1] Ch: 8, ADC: 0
    [2] Ch: 8, ADC: 0
    [3] Ch: 8, ADC: 0
    [4] Ch: 8, ADC: 0
    [5] Ch: 8, ADC: 0
    [6] Ch: 8, ADC: 0
    [7] Ch: 8, ADC: 0
    [8] Ch: 8, ADC: 0
    [9] Ch: 8, ADC: 0
    [10] Ch: 8, ADC: 0
    [11] Ch: 8, ADC: 0
    [12] Ch: 8, ADC: 0
    [13] Ch: 8, ADC: 0
    [14] Ch: 8, ADC: 0
    [15] Ch: 8, ADC: 0
    [16] Ch: 8, ADC: 0
    [17] Ch: 8, ADC: 0
    [18] Ch: 8, ADC: 0
    [19] Ch: 8, ADC: 0
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    I also tried with adcResetFiFo() before and after adcStartConversion() - the behaviour (the buffer content) is absolutely the same.

    2. The value of G1_BLOCKS

    I noticed that adcStartConversion() changes the G1_BLOCKS from 32U to 31U. Then, the register "automatically" recovers to 32U:

    Is this normal?

    3. The G1_DMA_EN bit

    I do not observe any difference in the program behaviour when the G1_DMA_EN bit is 0 or 1. What should be the recommended configuration for this scenario?

    Thanks and regards,

    Varban 

  • Hi,

    I eventually managed to run the DMA by activating all the channels in adcREG1, adcGROUP1 and setting the FIFO size equal to the number of the channels (in my case - 24).

    Then, after calling adcStartConversion() in non-continuous mode, the ADC gets all the channel readings in the FIFO in one shot and fires a single DMA to the RAM - that transfer all the 24 uint32_t values.

    It seems like that what I was initially looking for (reading a single channel N times, filling the queue and triggering a DMA upon FIFO full event) is not possible by design, correct me if I'm wrong.

    Thanks and regards,

    Varban

  • Hi Varban,

    Can you please refer below example configurations and code:

    ADC_with_DMA_RM46.zip

    In above project i was able to successfully be transferred the 20 samples of channel-6 to the other RAM buffer using DMA.

     

    I think the main difference from your project is that, here i enabled the continuous conversion mode instead of single conversion mode.

    I enabled this because if we didn't enable this continuous conversion mode, whenever we gave start conversion the channel-6 data always storing at single location of the ADC RAM memory.

    I think this is expected only i mean single shot mode will only do the conversion one time for all the enabled channels and will store the data in the ADC RAM. If we enable 6th channel, then only its data is storing every time at the first location of the RAM.

    --

    Thanks & regards,
    Jagadish.

  • Hi Jagadish,

    Thanks for your response!

    My observations in continuous mode were exactly the same.

    However, is it still the case that DMA is not recommended to be used with continuous mode, as stated in this thread: e2e.ti.com/.../tms570lc4357-dma-with-adc-in-continuous-mode?

    Best regards,

    Varban

  • Hi Varban,

    However, is it still the case that DMA is not recommended to be used with continuous mode, as stated in this thread: e2e.ti.com/.../tms570lc4357-dma-with-adc-in-continuous-mode?

    I think this is true.

    Even i if you verify my converted result also as you can see for last 4 conversions EMPTY flag got set:

    As they said, there seems like some timing issues between continuous mode with DMA.

    And also, as we select single channel for this conversion mode right, so the conversions will happen very quickly.

    As you can see the conversion time for one channel is just 1.6us, so that means we will get samples at every 1.6uS. I think we don't need the samples with such low interval.

    Maybe this is the reason they are preferring to use either timer or PWM for samples generation.

    --
    Thanks & regards,
    Jagadish.

  • As you can see the conversion time for one channel is just 1.6us, so that means we will get samples at every 1.6uS. I think we don't need the samples with such low interval.

    And from what I have observed - in case of DMA, one may need some state machine based on interrupts (to indicate the new transfer completion), and the interrupts (even with very simple and fast handling) may choke the RTOS flow.

  • And from what I have observed - in case of DMA, one may need some state machine based on interrupts (to indicate the new transfer completion), and the interrupts (even with very simple and fast handling) may choke the RTOS flow.

    Maybe you are correct, however i never tested DMA in RTOS environment.