I'm using an STM32F103VB, and one of the things that I'm doing is doing a set of ADC reads which are then being transferred via DMA to a buffer.
For some reason, when I'm debugging the DMA transfer gets scrambled: channel 0 ends up where channel 1 is supposed to go, channel 1 where channel 2 should be, on up the line until finally the last channel gets written to channel 0.
I have the code set up for the ADC to run in "scan" mode, with the DMA (theoretically!) sucking the data off and putting it into memory.
Furthermore, I have the ADC ISR set up so that on an end of conversion interrupt (which is only supposed to happen at the end of the scan in scan mode) the DMA engine gets reinitialized to point to the base of the memory array it's supposed to write to.
Does anyone have any obvious clue to what I'm doing wrong? Does ST have any good bits of sample code that I've missed?
Thanks in advance.
My liberal friends think I'm a conservative kook.
My conservative friends think I'm a liberal kook.
Why am I not happy that they have found common ground?
Tim Wescott, Communications, Control, Circuits & Software
http://www.wescottdesign.com
Didn't find your answer? Ask the community — no account required.
T
Tim Wescott
Whoops -- I meant to cross-post this to sed, 'cause there may be folks over there with useful information, too.
My liberal friends think I'm a conservative kook.
My conservative friends think I'm a liberal kook.
Why am I not happy that they have found common ground?
Tim Wescott, Communications, Control, Circuits & Software
http://www.wescottdesign.com
L
langwadt
.
e
ave
a suggestion from a quick google
formatting link
z1yLmVUCe5
-Lasse
P
peter_gotkatov
e
ttdesign.com
Do you have the DMA set up to operate in circular mode? If so then it sounds like the DMA is doing one transfer before the first ADC conversion is complete. Try changing your ISR to only set up the DMA but not start the ADC and see if the DMA counter drops by one as soon as you exit the ISR.
Also remember that the ADC has two interrupt sources, one for EOC and one for JEOC so make sure you're using the right one.
If this ONLY happens while debugging through JTAG then it may be that your debugger halts the DMA but not the ADC. Some peripherals like the CAN controller can be selected to either keep running or halt during debug.
T
Tim Wescott
#axzz1yLmVUCe5
That's way too simple -- my problem isn't getting the right numbers out of the ADC. My problem is getting the right numbers out of the ADC, at the right times, and into the right spots in memory fast.
The _only_ way that I can see to do multiple consecutive ADC conversions with that part is to use the ADC scan mode and DMA -- so I pretty much must get it working.
It is very frustrating to halt the processor, restart, and see all the right numbers in all the wrong places. Scary, too, considering that I'm controlling enough power to make things awfully toasty if I screw it up.
My liberal friends think I'm a conservative kook.
My conservative friends think I'm a liberal kook.
Why am I not happy that they have found common ground?
Tim Wescott, Communications, Control, Circuits & Software
http://www.wescottdesign.com
T
Tim Wescott
Initially I had it set up with the DMA in circular mode, and the interrupt coming from the DMA controller on the completion of a set of writes. That gave me this problem as soon as the processor started, so I axed that pretty quick!
Hmm. I'll try that, but it really shouldn't be doing so.
Currently the DMA is set to terminate at the end of a transfer, and to start it up again you need to disable the DMA, re-initialize the length register, then turn it on again.
It does make me think that carefully checking the order in which I turn things back on may bear fruit. However, the way that the whole system is set up (and if I understand ST's documentation), the ADC gets triggered periodically from a timer, converts a bunch of channels which get squirreled away in memory by the DMA, then interrupts.
So if the DMA is getting set up in the interval when the ADC is quiescent, even if it gets screwed up once when the processor starts up, it should still get back on track the next ISR -- or, at least that's how I read things. Yet this is obviously not the case.
I'm only interrupting on EOC.
Good thought, I'll think about implications, and how to test.
My liberal friends think I'm a conservative kook.
My conservative friends think I'm a liberal kook.
Why am I not happy that they have found common ground?
Tim Wescott, Communications, Control, Circuits & Software
http://www.wescottdesign.com
L
langwadt
.
T
m
should have mentioned I was looking at the post by "tony" two thirds down the page, look like scan of two channels with dma
-Lasse
R
Rich Webb
Here's how I've been doing it on an STM32F103RB. ADCStart() is called from the main loop to kick things off. DMADone and ADCActive are extern volatile so that main can keep an eye on progress. Mind the line wrap on some of the comments.
----
// We'll get ADC_NUM_CHANS results in sequence and use DMA to save the // results of the conversions to the output array. No ADC IRQ is needed; // we'll interrupt at the end of the associated DMA cycle.
void ADCInit(void) { RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // give it a clock ADC1->CR2 |= ADC_CR2_ADON; // turn it on RCC->CFGR &= ~RCC_CFGR_ADCPRE; // ADC clock to 12 MHz (pclk2 / 6) RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6; RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6; // stroke twice to ensure it's awake ADC1->CR2 |= ADC_CR2_CAL; // since we're doing a cal cycle while ((ADC1->CR2 & ADC_CR2_CAL) != 0) { ; // wait } // Note that we've already setup PA0-7; PB0,1; PC0-3 for analog input ADC1->CR1 |= ADC_CR1_SCAN; // enable scan mode ADC1->CR2 |= ADC_CR2_EXTSEL; // with SWSTART as the trigger ADC1->CR2 |= ADC_CR2_DMA; // enable DMA at the end of scan ADC1->SQR3 = 0x0A418820; // scan 0, 1, 2, 3, 4, 5, ADC1->SQR2 = 0x16A4A0E6; // 6, 7, 8, 9, 10, 11, ADC1->SQR1 = 0x00D001AC; // 12, 13; total 14 conversions
ADC1->SMPR2 = 0x00B6DB6D; // 55.5 clocks sample time
// Setup the DMA peripheral to handle the storing of the ADC results. RCC->AHBENR |= RCC_AHBENR_DMA1EN; // give it a clock // Tell DMA which peripheral register has the data to store. DMA1_Channel1->CPAR = (unsigned long)&ADC1->DR; // and then point to the destination in memory. DMA1_Channel1->CMAR = (unsigned long)ADCResult; DMA1_Channel1->CNDTR = ADC_NUM_CHANS; // how many to transfer // medium priority, 32 bits, peripheral to memory, increment memory, // not circular, and interrupt when all the transfers are complete. DMA1_Channel1->CCR = 0x00001A82; DMA1_Channel1->CCR |= 0x01; // enable it
NVIC_EnableIRQ(DMA1_Channel1_IRQn); }
void DMA1_Channel1_IRQHandler(void) { DMA1->IFCR = 0x03; // reset channel 1 and global DMA1_Channel1->CCR &= ~0x01; // turn the channel off
// and start the conversions ADC1->CR2 |= ADC_CR2_ADON; }
-- Rich Webb Norfolk, VA
P
peter_gotkatov
As long as ADC_SR_EOC is clear before you set DMA_CCR1_EN then there should be no problem. I don't know if JEOC needs to be cleared as well but it wouldn't hurt.
M
Mark Borgerson
I think the STM32 debug hardware has the option to stop the timer clocks when debugging. If you don't do that, perhaps your timer is triggering an ADC cycle while in the debug mode.
Mark Borgerson
L
langwadt
to
d:
re
ry.
of
to
ks
/
at
s
I'm
.
generally when you are working on stuff that controls with motors and power stopping the cpu is not an option. You have to use some type of realtime debugger to look at variables and such
used to be that you had to make your own with uart or something like that, but now most also have to the option to do it over the jtag or what ever debug interface is used
-Lasse
M
Mark Borgerson
The manual for the STM32F2xx mentions that the option to keep the timer clocks running when the CPU is halted for debugging is useful when using the timers to generate PWM for motor control.
Mark Borgerson
L
langwadt
memory.
depends on how intelligent the timer is of course, but imagine a robot controlled by that motor, stop the cpu at the wrong time at it might just keep going until it hits something HARD
-Lasse
M
Mark Borgerson
to
scrambled:
where
memory.
of
to
folks
conversions
up.
That kind of problem is always present when controlling motors and other mechanical systems. Real-Time debugging is difficult even with the best of hardware. That's one reason I've never been particularly interested in writing code for computer-controlled sawmills!
When I was working on autonomous aerial vehicle code, there were two kinds of debugging----offline PC sims to debug algorithms, and the "record everything and analyze after the crash" for the hardware- associated problems. (The 'crash' was usually not a computer reset type crash. It was an "I was flying and now I'm not" type of crash.)
Mark Borgerson
T
Tim Wescott
stm32/
It looks like he's doing what I'm trying; maybe he didn't debug.
At any rate, I found a horrible kludge that seems to work. At the end of the ISR, I reset the DMA controller leaving room for the ADC channels _plus one_. I'm sure that when I die and I'm trying to convince St. Peter that I belong up instead of down, that this kludge will be raised as a topic of conversation:
DMA.CHAN_1.CCR.bits.EN = 0; // disable DMA DMA.CHAN_1.CMAR = raw_adc_readings; // set address DMA.CHAN_1.CNDTR.bits.NDT = ADC_CHANNELS + 1; // Number of ADC channels + kludge DMA.IFCR.bits.TCF1 = 1; // Clear the transfer complete interrupt DMA.CHAN_1.CCR.bits.EN = 1; // enable DMA
ADC1.SR.bits.EOC = 0; // clear interrupt
My liberal friends think I'm a conservative kook.
My conservative friends think I'm a liberal kook.
Why am I not happy that they have found common ground?
Tim Wescott, Communications, Control, Circuits & Software
http://www.wescottdesign.com
L
langwadt
e
s
t
at
so it is only when you debug? what are you using for debug?
of
I've seen worse but, are you setting the dma up for circular mode? if you do that and have the plus one aren't you pretty sure it will go wrong if you are ever late with the interrupt?
guess you could put a magic number at that extra location and if it ever gets overwritten panic or what ever makes most sense :P
-Lasse
T
Tim Wescott
JTAG, OpenOCD and gdb for debugging.
The DMA is _not_ in circular mode.
I may monitor the extra location -- but I suspect that I'll just find out that it stays zero until after I halt and restart.
Tim Wescott
Control system and signal processing consulting
www.wescottdesign.com
L
langwadt
,
d
e
he
o
ng
I
nd
r
did you have that before?
it should tell you something about whether it is the dma or the adc that gets confused
-Lasse
S
Sacchi
Il 20/06/2012 17:51, Tim Wescott ha scritto:
Maybe you left a dma or adc memory watch opened? Reading a register can trigger some event, and it will be triggered even if you left a dma or adc regs watch opened.
L
T
Tim Wescott
I just got an email on this asking how I resolved it -- so I guess I should revive this dusty old thread and answer my own question.
I can't be entirely sure with the passage of time, but I believe that this is the pertinent bit of code, found in my ADC ISR:
DMA.CHAN_1.CCR.bits.EN = 0; // disable DMA DMA.CHAN_1.CMAR = raw_adc_readings; // set address DMA.CHAN_1.CNDTR.bits.NDT = ADC_CHANNELS + 1; // Number of ADC channels + kludge DMA.CHAN_1.CCR.bits.EN = 1; // enable DMA
This ISR triggers off of the ADC end-of-conversion, _not_ the DMA: I don't know if that's necessary to the fix, or if it just happened to make things work. I'm pretty sure (I certainly hope!) that this end-of-conversion interrupt happens at the end of the sequence -- the reader should check his documentation to make sure.
The pertinent setup (well, some -- there's a bazillion bits to set) is:
ADC1.CR1.bits.EOCIE = 1; // interrupt on conversion end ADC1.CR1.bits.SCAN = 1; // scan ADC1.CR2.bits.DMA = 1; // use DMA ADC1.CR2.bits.ALIGN = 1; // left alignment ADC1.CR2.bits.EXTSEL = 0x04; // Trigger on Timer 3 TRGO event.
Tim Wescott
http://www.wescottdesign.com
---------------------------------------
Posted through http://www.EmbeddedRelated.com
Join the Discussion
Have something to add? Share your thoughts — no account required.
Didn't find your answer?
Ask the community — no account required
Report Content
You are reporting this content to the moderators. They will look at it
ASAP.