Team,
My customer was trying to set up a SW-less DMA ping pong to capture a continuous stream of ADC samples. He wants to avoid SW for this in case the CPU is servicing an interrupt, the buffer switches too late, and he misses a sample. Also would like to avoid nested interrupts.
My thought process was that he could use DMA1 to pull an address for DMA0 to write to, since DMA0 can trigger DMA1. I initialized two addresses in RAM for DMA1 to pull from. After DMA0 completes fills a buffer using ADC12, it could trigger DMA1 to get a new destination address. After DMA0 fills that second buffer, DMA1 would trigger again and reload the first buffer address into DMA0.
I wrote this code (attached) to test this on the F5438A. It will "ping" to the second buffer by loading a new buffer address to DMA0, but not "pong" back to the first address afterwards. DMA1SZ (initially set to 2) stays at 1 and will not load the second address back into DMA0.
The part that is weird is that this ping ponging will work if DMAIE is enabled. Even if the IRQ does nothing, it allows the ping ponging to work. Any thoughts on how to get this to work without the interrupt?
I know the DMA had an errata in the non-A version where it would not allow the DMA to write to the DMA, but it looks to not be an issue on the A-version.
Thank you! I hope this description makes sense. Please call me if needed.
Regards,
Nishant
//****************************************************************************** // MSP430F543xA Demo - DMA0, Single transfer using ADC12 triggered by TimerB // // Description: This software uses TBCCR1 as a sample and convert input into // the A0 of ADC12. ADC12IFG is used to trigger a DMA transfer and DMA // interrupt triggers when DMA transfer is done. TB1 is set as an output and // P1.0 is toggled when DMA ISR is serviced. // ACLK = REFO = 32kHz, MCLK = SMCLK = default DCO 1048576Hz // // MSP430F5438A // ----------------- // /|\| XIN|- // | | | 32kHz // --|RST XOUT|- // | | // | P1.0|-->LED // | P4.1|-->TB1 output // | | // | P6.0|<--A0 // // M. Morales // Texas Instruments Inc. // June 2009 // Built with CCE v3.1 Build 3.2.3.6.4 & IAR Embedded Workbench Version: 4.11B //****************************************************************************** #include "msp430x54xA.h" //unsigned int DMA_DST = 0x3000; void main(void) { WDTCTL = WDTPW+WDTHOLD; // Hold WDT P1OUT &= ~BIT0; // P1.0 clear P1DIR |= BIT0; // P1.0 output P4DIR |= BIT1; // P4.1 output P4SEL |= BIT1; // P4.1 select //Setup Timer B0 TBCCR0 = 0xFFFE; TBCCR1 = 0x8000; TBCCTL1 = OUTMOD_3; // CCR1 set/reset mode TBCTL = TBSSEL_2+MC_1+TBCLR; // SMCLK, Up-Mode // Setup ADC12 ADC12CTL0 = ADC12SHT0_15+ADC12MSC+ADC12ON;// Sampling time, MSC, ADC12 on ADC12CTL1 = ADC12SHS_3+ADC12CONSEQ_2; // Use sampling timer; ADC12MEM0 // Sample-and-hold source = CCI0B = // TBCCR1 output // Repeated-single-channel ADC12MCTL0 = ADC12SREF_0+ADC12INCH_7; // V+=AVcc V-=AVss, A7 channel ADC12CTL0 |= ADC12ENC; // Setup DMA0 __data16_write_addr((unsigned long) 0x4000,(unsigned long) 0x3014); // ... buffer #2 stored at 0x4000 __data16_write_addr((unsigned long) 0x4002,(unsigned long) 0x3000); // ... original buffer #1 stored at 0x4002 DMACTL0 = DMA0TSEL_24; // ADC12IFGx triggered DMACTL4 = DMARMWDIS; // Read-modify-write disable DMA0CTL &= ~DMAIFG; DMA0CTL = DMADT_4+DMAEN+DMADSTINCR_3/*+DMAIE*/; // Rpt single tranfer, inc dst, interrupt? DMA0SZ = 10; // DMA0 size = 10 __data16_write_addr((unsigned short) &DMA0SA,(unsigned long) &ADC12MEM0); // ... from ADC12MEM0 __data16_write_addr((unsigned short) &DMA0DA,(unsigned long) 0x3000); // ... to destination in RAM, buffer #1 DMACTL0 |= DMA1TSEL_30; // ADC12IFGx triggered DMACTL4 = DMARMWDIS; // Read-modify-write disable // DMA0CTL &= ~DMAIFG; DMA1CTL = DMADT_4+DMAEN+DMASRCINCR_3; // Rpt single tranfer, inc src DMA1SZ = 2; // DMA1 size = 2 __data16_write_addr((unsigned short) &DMA1SA,(unsigned long) 0x4000); // ... start at 0x4000 and increment to 0x4002 __data16_write_addr((unsigned short) &DMA1DA,(unsigned long) &DMA0DA); // ... to destination DMA0DA __bis_SR_register(LPM0_bits + GIE); // LPM0 w/ interrupts __no_operation(); // used for debugging } //------------------------------------------------------------------------------ // DMA Interrupt Service Routine //------------------------------------------------------------------------------ #pragma vector=DMA_VECTOR __interrupt void DMA_ISR(void) { switch(__even_in_range(DMAIV,16)) { case 0: break; case 2: break; // DMA0IFG = DMA Channel 0 case 4: // DMA1IFG = DMA Channel 1 P1OUT ^= BIT0; // Toggle P1.0 break; case 6: break; // DMA2IFG = DMA Channel 2 case 8: break; // DMA3IFG = DMA Channel 3 case 10: break; // DMA4IFG = DMA Channel 4 case 12: break; // DMA5IFG = DMA Channel 5 case 14: break; // DMA6IFG = DMA Channel 6 case 16: break; // DMA7IFG = DMA Channel 7 default: break; } }