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.
Hi,
I have troubles using DMA with ADC12 on a MSP430F5522 microcontroller.
I want my ADC12 to sample channel 4 repeatedly, and I want DMA to transfer the converted data to a global-defined 64-sample array. After 64 transfers I want the process to stop, and therefore I am using DMA ISR to disable ADC conversions. This process must be repeated at fixed time intervals.
I have the following problems:
1) if I try to enable and disable the DMA every time I have to perform my process, the DMA transfers data only the first time, and after that it remains "sleeping" ... I mean, from the second time on it does not do any transfer (even if the configuration of all registers is exactly the same as before starting the first time)
2) if I leave the DMA always enabled, the DMA is always working, not only the first time! ...But, when I try to disable ADC conversions after having collected my 64 samples (that is in the DMA Interrupt routine) I find that one random value is overwritten by DMA to my array while I am accessing the ADC registers. These value is not coming from the ADC12MEM0 register, which may contain the result of the "excess" conversion initiated by ADC while I was disabling it, but is apparently meaningless.
My code follows.
Please help me!! Thank you in advance,
Elena
I have configured my peripherals as follows:
/* ADC */
/* disable ADC conversions */
ADC12CTL0 &= ~(ADC12ENC);
/* Turn on ADC, set multiple sample and conversion, sample time = 8*ADCCLK */
ADC12CTL0 = ADC12ON + ADC12MSC + ADC12SHT0_1;
/* Use sampling timer (pulse sample mode), sample-and-hold source is ADC12SC*/
/* repeated single channel mode, ADC source clock is ACLK, divider = /6 */
/* ADCCLK freq is 2.083 MHz, sample time is 3.84us, conv. time is 6.24us */
ADC12CTL1 = ADC12SHP + ADC12CONSEQ_2 + ADC12SSEL_1 + ADC12DIV_5;
/* 12-bit resolution, temperature sensor off */
ADC12CTL2 = ADC12RES1 + ADC12TCOFF;
/* ref+ = AVcc, ref- = AVss, sampling channel A4 */
ADC12MCTL0 = ADC12SREF_0 + ADC12INCH_4;
/* Enable conversions (to be done after ADC configuration) */
ADC12CTL0 |= ADC12ENC;
/* DMA */
/* DMA channel 0 triggered by ADC12IFG */
DMACTL0 = DMA0TSEL_24;
/* Read-modify-write disable */
DMACTL4 = DMARMWDIS;
/* clear DMA interrupt flag */
DMA0CTL &= ~DMAIFG;
/* repeated single tranfer mode (DMAEN always enabled), dest incrementing, */
/* source fixed, word-to-word, edge-sensitive trigger, DMA enabled, */
/* DMA interrupt enbled, DMAIFG flag set when the DMAxSZ reg counts to zero */
DMA0CTL = DMADT_4 + DMADSTINCR_3 + DMASRCINCR_0 + DMASWDW + DMAEN + DMAIE;
/* DMA0 size is the number of transfers (each triggered by its own trigger) */
/* before disabling DMA (DMAEN bit = 0) and setting the DMA interrupt flag */
DMA0SZ = AF_SAMPLE_NUM;
/* Source address */
__data16_write_addr((unsigned short) &DMA0SA,(unsigned long) &ADC12MEM0);
/* Destination address */
__data16_write_addr((unsigned short) &DMA0DA,(unsigned long) &gui_AF_Samples);
The source code performing the main task is:
void AF_Sampling (void)
{
/* clear DMA start */
DMA0CTL &= ~DMAREQ;
/* reset the flag for end of AF sampling */
gb_Sampling_End = FALSE;
/* clear ADC regs */
ADC12CTL0 &= ~ADC12ENC;
ADC12CTL1 &= ~ADC12BUSY;
/* start DMA transfers */
DMA0CTL |= DMAREQ;
/* Enable conversions (to be done after ADC configuration) */
ADC12CTL0 |= ADC12ENC;
/* Start sequence - software trigger */
ADC12CTL0 |= ADC12SC;
/* wait for end of DMA transfers */
while (gb_Sampling_End != TRUE);
/* clear ADC enable conversion bit */
ADC12CTL0 &= ~ADC12ENC;
/* clear ADC interrupt flag (one sampling and conversion " in excess" !!!) */
ADC12IFG = 0;
trash = ADC12MEM0; /* "excess" conversion */
/* clear DMA start */
DMA0CTL &= ~DMAREQ;
/* clear DMA interrupt flag */
DMA0CTL &= ~DMAIFG;
}
The DMA ISR is the following
#pragma vector=DMA_VECTOR
__interrupt void DMA_ISR(void)
{
/* disable GIE to avoid interrupt nesting */
__bic_SR_register(GIE);
/* clear DMA start */
DMA0CTL &= ~DMAREQ;
/* disable ADC conversions */
/* Resetting ADC12ENC during repeat-single-channel operation */
/* stops the converter at the end of the current conversion */
ADC12CTL0 &= ~ADC12ENC;
/* clear ADC interrupt flag (one sampling and conversion exceeding) */
ADC12IFG = 0;
/* set the flag for end of AF sampling */
gb_Sampling_End = TRUE;
/* clear DMA interrupt flag */
DMA0CTL &= ~DMAIFG;
}
If you disable the DMA in the DMA ISR, ADC12IFG is set by the ongoing conversion while the DMA is inactive.
If you now re-enable DMA, ADC12IFG is already set, so no rising edge will trigger the DMA.
DMA triggers are edge sensitive. The only level-sesitive trigger is the external DMA signal.
So when re-enabling the DMA you must also clear ADC12IFG manually, so the next conversion result will trigger a DMA:
If you leave the DMA on and disable the ADC, the current conversion will be finished (and a DMA will be triggered). If oyu nto at athe same time reset CONSEQx to 0. Which will immediately stop with unpredicatble conversion result.
However, in the code you posted, you do both, stopping the DMA and stopping the ADC (so it is neithe rthe code for case one nor for case 2). And you clear the IFG bits. So it should work.
btw: it is not necessary to clear GIE at the beginning of an ISR. GIE is always cleared when an ISR is entered. If it weren't, the ISR would immediately interrupt itself, sicne the IFG bit is still set. Since GIE is part of the status register, and the status register is saved to stack on ISR entry, it is implicitely set again when the ISR exits.
elenab said:yes, but it actually does not work....
elenab said:/* clear ADC enable conversion bit */
ADC12CTL0 &= ~ADC12ENC;
/* clear ADC interrupt flag (one sampling and conversion " in excess" !!!) */
ADC12IFG = 0;
This part won't work. Clearing ADC12ENC does end the conversion after it is complete. When you try to clear ADC12IFG in teh next instruction the conversion most likely isn't complete yet and the IFG bit hasn't been set a tall (and if it had been set, it would immediately trigger the DMA and never appear to the main code)
you'll have to 1) disable the DMA, 2) then stop the conversion, 3) then wait for the last conversion to complete (IFG bit set), 4) then clear it.
If you clear it before it has been set, it won't work. And if you try to clear it before the DMA has been disabled, it also won't work.
Yes, you are right.
In fact my first idea was to enable DMA every time I needed to perform sampling and transfer, and to disable it at the end.
But here comes the problem I have describedat point 1):
if I try to enable and disable the DMA every time I have to perform my process, the DMA transfers data only the first time, and after that it remains "sleeping" ... I mean, from the second time on it does not do any transfer (even if the configuration of all registers is exactly the same as before starting the first time)
I think there may be a HW problem... I mean, when I disable DMA clearing the DMAEN bit, maybe this information remain somehow "latched"... and when I try to re-enable DMA again by setting DMAEN, it actually remains disabled and does not perform any transfer...
Yes, because the DMA is triggered by the setting of the IFG bit, not by its presence. That means IFG mut be 0 and becomes 1. If the IFG bit is set the moment you enable the DMA, it won't trigger. After enabling the DMA, youu'll have to clear the IFG bit that is remaining from the last conversion. The next conversion result will then trigger the DMA.elenab said:if I try to enable and disable the DMA every time I have to perform my process, the DMA transfers data only the first time, and after that it remains "sleeping"
As I said, level-trigger is only allowed for the external DMA signal. For all internal DMA triggers, only edge triggering works reliable (even if you can set the DMALEVEL bit). And this means that the IFG bit must first be clear in order to trigger the DMA when it is set again.
Ok,
I have modified my SW.
Now the things that I do when using DMA are:
This process shall be repeated every time I need a new set of 64 samples.
ADC is configured in repeated single channel mode (ADC12CONSEQ = 2).
DMA is configured in repeated single transfer mode (DMADT_4), triggered by ADC12IFG (DMA0TSEL_24), edge-sensitive trigger (DMALEVEL = 0).
I put a breakpoint at the first instruction of the DMA ISR and another one at the end of the main flow function. When I run my SW, I observe that it never enters the DMA ISR and it never overpasses the instruction where I wait for the end of my 64-sample collection (i.e., the above point 5.). At this point I halt the SW manually and find the following registers status:
ADC12CTL0 = 0x0193, which means ADC12SC = 1, ADC12ENC = 1
ADC12CTL1 = 0x02AD, which means ADC12BUSY = 1
ADC12IFG = 0x0001, which means ADC12IFG0 = 1, though I verified that it was 0 before starting conversions (i.e., at point 3.)
DMA0CTL = 0x4C14, which means DMAIE = 1, DMAIFG = 0, DMAEN = 1
DMA0SZ = 0x0040, which means that no data have been transferred yet.
It seems to me that the DMA is not correctly triggered by the ADC12IFG. This is what I called “sleeping” DMA in my previous replies...
Following some code extracts:
Initialization:
/* DMA */
/* DMA trigger is ADC12IFG */
DMACTL0 = DMA0TSEL_24;
/* Read-modify-write disable */
DMACTL4 = DMARMWDIS;
/* clear DMA interrupt flag */
DMA0CTL &= ~DMAIFG;
/* repeated single tranfer mode, dest incrementing, */
/* source fixed, word-to-word, edge-sensitive trigger, DMA enabled, */
/* DMA interrupt enbled, DMAIFG flag set when the DMAxSZ reg counts to zero */
DMA0CTL = DMADT_4 + DMADSTINCR_3 + DMASRCINCR_0 + DMASWDW + DMAIE;
/* DMA0 size is the number of transfers before setting the DMA interrupt flag */
DMA0SZ = 64;
/* Source address */
__data16_write_addr((unsigned short) &DMA0SA,(unsigned long) &ADC12MEM0);
/* Destination address */
__data16_write_addr((unsigned short) &DMA0DA,(unsigned long) &gi_AF_Samples);
/* ADC */
/* disable ADC conversions */
ADC12CTL0 &= ~(ADC12ENC);
/* Turn on ADC, set multiple sample and conversion, sample time = 8*ADCCLK */
ADC12CTL0 = ADC12ON + ADC12MSC + ADC12SHT0_1;
/* Use sampling timer (pulse sample mode), sample-and-hold source is ADC12SC*/
/* repeat single channel mode, ADC source clock is ACLK, divider = /6 */
/* ADCCLK freq is 2.083 MHz, sample time is 3.84us, conv. time is 6.24us */
ADC12CTL1 = ADC12SHP + ADC12CONSEQ_2 + ADC12SSEL_1 + ADC12DIV_5;
/* 12-bit resolution, temperature sensor off */
ADC12CTL2 = ADC12RES1 + ADC12TCOFF;
/* ref+ = AVcc, ref- = AVss, sampling channel A4 */
ADC12MCTL0 = ADC12SREF_0 + ADC12INCH_4;
/* Enable conversions (to be done after ADC configuration) */
ADC12CTL0 |= ADC12ENC;
The source code performing the main task is:
void AF_Sampling (void)
{
/* reset the flag for end of AF sampling */
gb_Sampling_End = FALSE;
/* enable DMA */
DMA0CTL |= DMAEN;
/* clear ADC IFG */
ADC12IFG = 0;
/* Enable conversions (to be done after ADC configuration) */
ADC12CTL0 |= ADC12ENC;
/* Start sequence - software trigger */
ADC12CTL0 |= ADC12SC;
/* wait for end of DMA transfers */
while (gb_Sampling_End != TRUE);
}
The DMA interrupt service routine is:
#pragma vector=DMA_VECTOR
__interrupt void DMA_ISR(void)
{
/* disable DMA */
DMA0CTL &= ~DMAEN;
/* disable Adc conversions */
ADC12CTL0 &= ~ADC12ENC;
/* wait for ADC IFG */
while (ADC12IFG ==0);
/* clear ADC IFG */
ADC12IFG = 0;
/* set the flag for end of AF sampling */
gb_Sampling_End = TRUE;
/* clear DMA IFG */
DMA0CTL &= ~DMAIFG;
}
Thank you,
Elena
elenab said:while (gb_Sampling_End != TRUE);
Somewhere before this line (maybe in main before calling the function containing the ADC/DMA code) GIE must have been set. Or else no interrupts will be executed ever. And the waiting loop waits forever.
It is not the best idea to have a busy waiting loop inside an ISR. Currently, i tdoes nto do any harm, but as soon as the project grows, something like that may eat up the processor performance for nothing while other parts of the code or other ISRs are eagerly waiting to be executed.elenab said:while (ADC12IFG ==0);
Yes, the GIE bit is set at the beginning of the main flow (I did not report it) and in fact all the other interrupts in my program work correctly. So this cannot be the point...
(As for your suggestion about the waiting loop inside an ISR, I know you are right, and I am using it only temporarily... at least to see my code working! Then I will use a different solution since the code I am writing is safety critical and I cannot have any potential infinite loop in it).
Please let me know if you have any other idea about my problem. Thank you!
Elena
unfortunately, I do not know all the other interrupts in your program, so I cannot see the point :)elenab said:in fact all the other interrupts in my program work correctly. So this cannot be the point...
Maybe the problem is somewhere else where you do not expect it, adn wher eI cannot detect it because you didn't post the offending code.
However, if the program is really that large, I doubt I'll find the time to look through it.
If you can provide a minimum program that exhibits the problem but does not contain any other code, I'll look into it.
Or maybe by stipping out the apparently unrelated code, maybe you'll detect the problem by yourself.
That the otehr code does what it shall do does nto mean that it doesn't interefere with the code that doesn't work.
In case anyone finds this post while searching, this behaviour appears to be caused by the same issue detailed here:
http://e2e.ti.com/support/microcontrollers/msp430/f/166/p/401588/1429066#1429066
**Attention** This is a public forum