2024-06-02 08:15 AM - edited 2024-06-02 08:18 AM
Hello,
I have a problem on a STM32H753 which I do not understand.
I want to sample some data from ADC with a given time period.
So I use the TIM2_TRGO as Event generator for the ADC3 (via EXTSEL)
ADC3 is configured in 12 Bit single conversion, in the sequence register there is only one entry, the length ist 1.
TIM2 is configured in a way that it generates an Event each 4ms
For Data transfer I use the DMA1_Stream0
The corresponding DMAMUX channel is set to 115 accordingly.
On the ADC input Pin I provide a sinus signal.
For debugging reasons I initialise my RAM array with 1.
When the transfer is complete, I see in the Data Array that there are missing some data.
One result for example:
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2252, 2283, 2258, 2178, 2049, 1880, 1680, 1463, 1245, 1048, 924, 825, 768, 730, 734, 775, 830, 935, 1052, 1250, 1468, 1685, 1883, 2054, 2182, 2260, 2283, 2250, 2165, 2031, 1855, 1654, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
The data not equal to 1 are correct ADC values which I expect.
The result differs each run. Here for example the result of another record:
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2631, 2827, 2971, 3043, 3049, 2971, 2828, 2626, 2384, 2097, 1810, 1531, 1273, 1059, 888, 795, 767, 802, 914, 1097, 1332, 1585, 1869, 2159, 2428, 2660, 2855, 2980, 3042, 3025, 2948, 2803, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2963, 3036, 3041, 2970, 2833, 2635, 2392, 2118, 1840, 1560, 1297, 1071, 902, 797, 760, 802, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 807, 912, 1090, 1315, 1578, 1869, 2168, 2431, 2659, 2843, 2991, 3031, 3028, 2953, 2806, 2589, 2351, 2073, 1779, 1497, 1245, 1033, 876, 789, 768, 815, 943, 1119, 1350, 1622, 1906, 2192, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 820, 942, 1129, 1359, 1626, 1915, 2207, 2471, 2708, 2861, 2989, 3040, 3020, 2935, 2770, 2549, 9, 9, 9, 9
Here is the code I use:
//Config ADC
ADC3->SMPR1 |= ADC_SMPR1_SMP1_1;
ADC3->PCSEL |= ADC_PCSEL_PCSEL_1; //Select input channel
ADC3->CFGR = (ADC3->CFGR &~ADC_CFGR_RES_Msk) | ADC_CFGR_RES_1; //set 12 Bit resolution
ADC3->CFGR = (ADC3->CFGR &~ADC_CFGR_EXTSEL_Msk) | (11 << ADC_CFGR_EXTSEL_Pos); //use adc_ext_trg11 as trigger (TIM2_TRGO)
ADC3->CFGR = (ADC3->CFGR &~ADC_CFGR_EXTEN_Msk) | ADC_CFGR_EXTEN_0; //enable external trigger on rising edge
ADC3->CFGR = (ADC3->CFGR &~ADC_CFGR_DMNGT_Msk) | ADC_CFGR_DMNGT_0; //DMA One Shot Mode selected
ADC3->SQR1 = 0; //clear complete sampling sequence (automatically set sequencelength = 1)
ADC3->SQR1 |= (1 << ADC_SQR1_SQ1_Pos); //set channel to be converted
ADC3->CR |= ADC_CR_ADSTART;
for(int i = 0; i < traceObj->length; i++)
{
traceObj->data[i] = 0;
}
//Config DMA
__HAL_RCC_DMA1_CLK_ENABLE();
DMA1->LIFCR |= DMA_LIFCR_CTCIF0; //Clear Transfer complete Interrupt
DMA1->LIFCR |= DMA_LIFCR_CHTIF0; //Clear Half Transfer Interrupt
DMA1_Stream0->PAR = (uint32_t) &ADC3->DR; //Peripheral Address
DMA1_Stream0->M0AR = (uint32_t) traceObj->data; //MemoryAddress
DMA1_Stream0->CR |= DMA_SxCR_MINC; //memory increment
DMA1_Stream0->CR &= ~DMA_SxCR_DIR_Msk; //transfer direction: peripheral -> memory
DMA1_Stream0->NDTR = traceObj->length;
DMA1_Stream0->CR &= ~DMA_SxCR_CIRC; //disable circular mode
DMA1_Stream0->CR |= DMA_SxCR_PSIZE_0; //Peripheral Size: 16 Bit
DMA1_Stream0->CR |= DMA_SxCR_MSIZE_0; //Memory size: 16 Bit
DMA1_Stream0->CR |= DMA_SxCR_PL_1; //High priority
DMA1_Stream0->CR |= DMA_SxCR_EN; //enable DMA channel
//Config DMAMUX
DMAMUX1_Channel0->CCR |= 115; //select ADC3 as input for DMA1 Channel 0
//Config Timer
__HAL_RCC_TIM2_CLK_ENABLE();
TIM2->CR1 &= ~TIM_CR1_CEN; //Disable Timer
TIM2->PSC = 200;
TIM2->ARR = 4000; //je nach Frequenz...
TIM2->CR2 |= TIM_CR2_MMS_1; //update mode --> update event is selected as trigger Output
__disable_irq();
TIM2->CR1 |= TIM_CR1_CEN; //Enable Timer
while(DMA1_Stream0->NDTR != 0);
__enable_irq();
What can be the reason that the DMA decrement its NDTR correctly, but sometimes/often transfers no data to the RAM?
Maybe someone here has an idea what the problem could be?
Solved! Go to Solution.
2024-06-22 03:47 AM
Hi,
ST support was able to help me.
You are right. Its a problem with the D-Cache. The D-Cache does not recognize that the DMA has changed the RAM content. If you try to read the data, you only get the D-Cache Date. So you have to invalidate the D-Cache before.
Adding the line
SCB_CleanInvalidateDCache();
has solved my problem.
I am using the M7 for the first time and was not yet familiar with this mechanism with the D-Cache.
2024-06-06 07:19 AM
Maybe some additional info:
I also tryed it with ADC1 and ADC2, but still have the same problem.
traceObj->data is placed in SRAM3, beginning from address 0x30040000.
So in this case all used peripherals are in D2 Domain (DMA1, ADC1/2 [AHB1], SRAM3)
I really need help. I dont understand what is going wrong here.
2024-06-21 09:34 AM - edited 2024-06-21 09:42 AM
Here are some ideas you may want to try:
1. You seem like a bare-metal purist but, if CubeMX supports your intended usage, perhaps it would help to generate a project and see if you get the same kind of skips. There's a good chance you can find an official CubeIDE example of DMA+ADC for the H7x series which you can get running fairly quickly on your board.
2. does changing clock frequency up or down have any marked effect on the result?
3. Does changing the sample rate (from 4ms to say 50ms) have any effect?
4. Have you independently verified the timer's firing rate (perhaps you got the calculation wrong?). Can you use the same settings to toggle a pin with output compare? or implement an interrupt handler that captures the tick delta between firings?
5. I'm not familiar with the H7xxx series, but on more modest chips you can adjust the acquisition time in cycles (and you need to adjust it for faster clock rates). If this exists on your chip, have you tried playing with it?
6. Are you checking for any kind of error experienced by the ADC (I'm assuming there's some kind of interrupt/register bit for that)?
2024-06-21 01:11 PM
2024-06-21 02:34 PM - edited 2024-06-21 02:35 PM
@waclawek.jan wrote:
Good Catch.
From README of example ADC_DMA_Transfer for H743 (probably applies to H753 too)
@Note If the application is using the DTCM/ITCM memories (@0x20000000/ 0x0000000: not cacheable and only accessible
by the Cortex M7 and the MDMA), no need for cache maintenance when the Cortex M7 and the MDMA access these RAMs.
If the application needs to use DMA(or other masters) based access or requires more RAM, then the user has to:
- Use a non TCM SRAM. (example : D1 AXI-SRAM @ 0x24000000)
- Add a cache maintenance mechanism to ensure the cache coherence between CPU and other masters(DMAs,DMA2D,LTDC,MDMA).
- The addresses and the size of cacheable buffers (shared between CPU and other masters)
must be properly defined to be aligned to L1-CACHE line size (32 bytes).
2024-06-22 03:47 AM
Hi,
ST support was able to help me.
You are right. Its a problem with the D-Cache. The D-Cache does not recognize that the DMA has changed the RAM content. If you try to read the data, you only get the D-Cache Date. So you have to invalidate the D-Cache before.
Adding the line
SCB_CleanInvalidateDCache();
has solved my problem.
I am using the M7 for the first time and was not yet familiar with this mechanism with the D-Cache.
2024-06-22 09:13 AM - edited 2024-06-22 09:13 AM
You still should read @Piranha's post I gave link to. And, of course, the Cortex-M7 Programming Manual. And the H7 memory/architectural appnote.
JW