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.
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.
// // 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);
// // 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 ); dmaSetCtrlPacket(DMA_CH1, *ADG1_ptr); dmaEnableInterrupt(DMA_CH1, BTC); dmaSetChEnable(DMA_CH1, DMA_HW);
And printing the results in a task with sufficient stack allocation:
// // Inside task // while (1) { adcStartConversion(adcREG1, adcGROUP1); printf("ADC: %u\n", adc1_G1_results[0]); vTaskDelay(xDelay); }
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 Sunil Oak 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:
ADC: 0 ADC: 68347 ADC: 68347 ADC: 68347 ADC: 68348 ADC: 2812 ADC: 2811 ADC: 68347 ADC: 2812 ADC: 2811
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.
void dmaGroupANotification(dmaInterrupt_t inttype, uint32 channel) { sciSendByte(scilinREG, 'D'); return; } void adcNotification(adcBASE_t *adc, uint32 group) { sciSendByte(scilinREG, 'A'); return; }
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:
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:
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); } } /* USER CODE END */ void main(void) { /* USER CODE BEGIN (3) */ gioInit(); hetInit(); esmInit(); sciInit(); sciEnableNotification(scilinREG, SCI_RX_INT); sciReceive(scilinREG, 1, &rx_byte); adcInit(); 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 // adcEnableNotification(adcREG1, adcGROUP1); 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 ADC_SAMPLE_COUNT, // 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 dmaSetCtrlPacket(DMA_CH1, *ADG1_ptr); dmaSetChEnable(DMA_CH1, DMA_HW); // dmaEnableInterrupt(DMA_CH1, BTC); if(xTaskCreate(vUserTask, "UserTask", 512, NULL, tskIDLE_PRIORITY + 1, &xUserTaskHandle) != pdTRUE) { while (1); } _enable_IRQ(); _enable_FIQ(); vTaskStartScheduler(); return; /* USER CODE END */ }
And a "zoom" of the ADC DMA configuration register:
#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
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.
---------------- [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 [20] Ch: 8, ADC: 0 [21] Ch: 8, ADC: 0 [22] Ch: 8, ADC: 0 [23] Ch: 8, ADC: 0 [24] Ch: 8, ADC: 0 [25] Ch: 8, ADC: 0 [26] Ch: 8, ADC: 0 [27] Ch: 8, ADC: 0 [28] Ch: 8, ADC: 0 [29] Ch: 8, ADC: 0 [30] Ch: 8, ADC: 0 [31] Ch: 8, ADC: 0 ----------------
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:
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 Sunil Oak 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 Sunil Oak 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.