Skip to main content
KiptonM
Senior III
February 17, 2022
Question

ADC with Circular DMA works almost as expected. It is off by a 16-bit word. Can it be fixed?

  • February 17, 2022
  • 12 replies
  • 2223 views

I am reading 4 values, AN0, AN1, Temp, and the Reference. (But reference may go away.)

I am over sampling 16x. and eventually will be starting a sequence every 100 us.

I wanted two buffers, so I could work on one, while filling the other.

I thought a circular buffer would work. I make it 8 16-bit words and have a half-full buffer interrupt go when the first 4 words are done, and have the buffer-full interrupt go when the second 4 words are finished.

It does work that way.

The only issue is that it starts on second memory location not the first.

When I prefill the array with zeros, the array looks like this after the first transfer.

<invalid data> <data 0> <data 1> <data 2> <data 3> 0 0 0

And after the second DMA transfer it looks like this

<data 7> <data 0> <data 1> <data 2> <data 3> <data 4> <data 5> <data 6>

And after the third DMA Transfer it looks like this:

<data 7> <data 8> <data 9> <data 10> <data 11> <data 4> <data 5> <data 6>

Is there a way to fix this so the first data transfer starts on the first memory location? Or what am I doing wrong?

This is a STM32G031

And I am using HAL_ADC_Start_DMA() to start the DMA.

Here is what the ADC registers look like after the first transfer.

ADC_ISR: 200A

ADC_IER: 0010

ADC_CR: 10000001

ADC_CFGR1: 00000003

ADC_CFGR2: 2000000D

ADC_SMPR: 00000336

ADC_AWD1TR: 0FFF0000

ADC_AWD2TR: 0FFF0000

ADC_AWD3TR: 0FFF0000

ADC_CHSELR: 00003003

ADC_DR: 00005DAF

ADC_AWD2CR: 00000000

ADC_AWD3CR: 00000000

ADC_CALFACT: 00000035

ADC_COMMON_CCR: 00C00000

I think I can make it work the way it is, but it would be easier if it were aligned right.

This topic has been closed for replies.

12 replies

waclawek.jan
Super User
February 17, 2022

Walk through the code up to the point where given DMA channel is enabled (yes, inside Cube, it's open source, you can debug it as your own code - in fact, at this moment, it already *is* your own code and your reponsibility).

At that point, the "regular" ADC conversions should not have been started yet, check this. Now look at the ADC registers - is there any status flag set which would trigger a DMA transfer? (I don't use the 'G0 and the ADC modules vary from family to family so you have to understand how DMA is triggered in 'G0 on your own).

Note, that debugging may be intrusive and status flags may be cleared upon reading status or data registers. Again, read the manual for the details.

JW

KiptonM
KiptonMAuthor
Senior III
February 17, 2022

Actually that was a good idea. Unfortunately I have not found the problem.

What I did find was that the first word which I labeled <invalid data> was already in the ADC_DR register before I called the HAL_ADC_Start_DMA().

So I tried to read it before I called HAL_ADC_Start_DMA(). It did not make a difference. It still wrote it.

I will keep looking.

KiptonM
KiptonMAuthor
Senior III
February 17, 2022

So this time I caught it. Just after you enable the DMA and before you start the ADC the DMA moves the value in the ADC_DR to the first position in the DMA output.

Now I need to find out why the DMA thinks there was a trigger. More reading. Unfortunately I do not completely trust STM32 documentation because of all of the errors I have found in the past.

KiptonM
KiptonMAuthor
Senior III
February 17, 2022

Just before I went into the HAL_ADC_Start_DMA(). i printed the status of all the DMA registers out to a serial port and my terminal program. It had the DMA_ISR (DMA1->ISR) register = 0x00000000. But when I looked at the SFR debugger window it was 0x7000 which corresponds to 3 of the 4 interrupt flags for channel 5 which is used for the ADC.

When I tried to clear it with DMA1->IFCR = 0x00007000; according to the debugger window it did not clear. So now I do not know what is right. The debugger or my printing of the value.

sprintf(buf,"DMA_ISR: %08lX\r\n", DMA1->ISR);

KiptonM
KiptonMAuthor
Senior III
February 17, 2022

I have wasted too much time on this.

I fixed it by moving the data in the ISR.

void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
	//HAL_GPIO_WritePin();
	ADC_DMA_Buffer[0].word[0] = ADC_buffer.word[1];
	ADC_DMA_Buffer[0].word[1] = ADC_buffer.word[2];
	ADC_DMA_Buffer[0].word[2] = ADC_buffer.word[3];
	ADC_DMA_Buffer[0].word[3] = ADC_buffer.word[4];
	task_list.ADCa = 1;
	__NOP();
}
 
 
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
	ADC_DMA_Buffer[1].word[0] = ADC_buffer.word[5];
	ADC_DMA_Buffer[1].word[1] = ADC_buffer.word[6];
	ADC_DMA_Buffer[1].word[2] = ADC_buffer.word[7];
	ADC_DMA_Buffer[1].word[3] = ADC_buffer.word[0];
	task_list.ADCb = 1;
	ADC1->ISR |= ADC_ISR_EOC;
	__NOP();
}

When I am debugging if I put a breakpoint at the __NOP() in either routine the 4th transfer does not work. But if I put the breakpoint on the first line of the ISR the whole routine works fine.

I cannot explain it.

Piranha
Principal III
February 17, 2022

> I looked at the SFR debugger window it was 0x7000 which corresponds to 3 of the 4 interrupt flags for channel 5 which is used for the ADC.

If I assume that you mean the ISR register, then 0x7000 are 3 bits of a channel 4.

KiptonM
KiptonMAuthor
Senior III
February 18, 2022

I had to check if I read it wrong or if I typed it wrong. It is the difference between 0x7000 and 0x70000.

Yes you are correct. It is 0x7000 which is for the UART, not the ADC.

Thank you for that catch.

So there are no interrupts pending when I start the DMA. So I still do not understand why it pulls the existing data from the DR register at startup and does not wait for the first ADC request.

waclawek.jan
Super User
February 18, 2022

@Piranha​ ,

ST notoriously mixes 0-based and 1-based indexing. Channels in single-port DMA in 'G0 are indexed 1-based, but they are controlled by the DMAMUX which are indexed 0-based, i.e. DMA channel 4 is controlled by DMAMUX channel 5.

A random IDE and random "libraries" may add more confusion to this, if they wish so.

JW

https://community.st.com/s/article/how-to-use-dmamux-on-the-stm32-microcontrollers

KiptonM
KiptonMAuthor
Senior III
February 18, 2022

Documentation is the worst part of the STM32 parts. I find mistakes all the time. They also make it difficult to find the documentation, It should be very easy.

Even the datasheets are hard to find from the website. You find a family, then it gives you a list of say 7 datasheets with a number that has no correspondence to the part number. So you have to download and open the datasheet to see which part in the family the datasheet you downloaded is for.

Maybe it is a French or Italian thing... :grinning_face:

Piranha
Principal III
March 19, 2022

ST's web page, forum and basically everything related to software is terrible, but not in a way you describe.

The images of subseries and part numbers are clickable. Navigate to the page of specific part and the Documentation tab there will show the documents only relevant to that part. Or search the part number with Google and it will give the direct link instantly:

https://www.google.com/search?q=STM32G031J4

waclawek.jan
Super User
February 18, 2022

@KiptonM​ ,

I can't explain what you see based on your description, but using debugger may be misleading: besides potentially clearing flags as I said above, also note that when program execution is stopped in debugger, peripherals may still keep running.

JW

KiptonM
KiptonMAuthor
Senior III
February 18, 2022

Yes you are right on that. Debuggers have a lot of problems with peripherals and interrupts. It does not matter which chip you are working with. That is why I get fast writes out to a serial port as the first thing in any processor I bring up. My Linux computer can read 2,000,000 baud with a FDTI 3.3V logic serial to USB (TTL-232R-3V3-WE). With DMA transfers it helps a lot in debugging real time.