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.

Double circular buffer on MSP430F5438 to store audio data

Hello!,

I´m trying to develop a double circular buffer using DMA to get frames of an extabilished size from ADC converted data.
To reach this goal I wrote this code using a String to ceck how works this main file. I discovered that It works in the right way only one time, and once both buffers are full it stops to write them.

-Is there any way to force the initial address once the last buffer is full?

- How is possible to reduce ISR time?

#include "msp430x54xA.h"
#include "stdint.h"
#define Num_Samples 20
volatile uint8_t buffer0[Num_Samples];
volatile uint8_t buffer1[Num_Samples];
static char Switch=1; //index for Switch/double buffer
static char string1[] = { "Hello World this is an array written to test the duoble circular buffer" };
int volatile audioread;

void Port_init()
{ P10OUT &= ~BIT7; // P10.7 clear
P10DIR |= BIT7; // P10.7 output
P4DIR |= BIT1; // P4.1 output
P4SEL |= BIT1; // P4.1 select
P6SEL = P6SEL | 0x80; //P6.7 --> ADC function
P6DIR = P6DIR & 0x7F;
}
void TimerB_init()
{ //Setup Timer B0
/* Configure TimerB to produce the triggers for the ADC */
TBR = 0;
TBCTL = TBSSEL_1 | MC_1 | TBCLR; // Use ACLK, count up, reset counter and direction
TBCCR0 = 4096; // Init TBCCR0 w/ sample period
TBCCR1 = 4036; // Trigger for ADC12 Sample Conversion
TBCCTL1 = OUTMOD_7; // Reset OUT1 on EQU1, set on EQU0
}
void ADC_init()
{ // Setup ADC12
ADC12CTL0 &= ~ADC12ENC;
ADC12CTL0 = ADC12ON + //Turn on ADC12
ADC12SHT0_1+// 32 cycles per sample (Sampling time)
ADC12MSC + //multiple sample
ADC12REFON; //enable int ref

ADC12CTL1 = ADC12SHP + //pulse mode
ADC12CONSEQ_0 +//single channel single conversion
ADC12SSEL_3 + //clock source SMCLK
ADC12CSTARTADD_0;//select first (in order) register (7)
ADC12CTL2 = ADC12RES_0 + ADC12REFBURST; // 8bit res, 9 clock cycles to convert,Reference buffer on only during sample-and-conversion
ADC12CTL2 &= ~(ADC12DF); //ADC12DF=0 --> unsigned format
ADC12MCTL0 = ADC12SREF_7 +
ADC12EOS +
ADC12INCH_7; //End of Sequence --> Bit7 = 1; ADC12SREF = 0b111; ADC12INCH = 0b0111;
__delay_cycles(75); // 35us delay to allow Ref to settle
// based on default DCO frequency.
// See Datasheet for typical settle
// time.
ADC12CTL0 |= ADC12ENC; // Enable conversions
ADC12CTL0 |= ADC12SC; // Start conversion
audioread=ADC12MEM0;
// Setup DMA0 double buffer
DMACTL0 = DMA0TSEL_6; // ADC12IFGx triggered
// DMA0SA = (__SFR_FARPTR)(unsigned int)&ADC12MEM0;
DMA0SA =(__SFR_FARPTR)(unsigned int)&string1; // Source block address
//DMA0SZ = sizeof Num_Samples; // Block size
DMA0SZ = sizeof string1-1; // Block size
DMA0CTL = DMADT_4 +//Rpt single block
DMASRCINCR_3 +//Source address increment
DMADSTINCR_3 + //Destination address increment
//DMASRCBYTE +
DMALEVEL +
DMASBDB; //Transfer byte to byte
__bis_SR_register(LPM0_bits + GIE); // LPM0 w/ interrupts
__no_operation(); // used for debugging
}
void main(void)
{
WDTCTL = WDTPW+WDTHOLD; // Hold WDT
Port_init();
TimerB_init();
ADC_init(); //store audio on Ram
DMA0CTL |= DMAIE;//Interrupt enable
__bis_SR_register(LPM0_bits + GIE);
__no_operation(); // used for debugging
}

//------------------------------------------------------------------------------
// DMA Interrupt Service Routine \\2 CLOCK MCLK PER TRANSFER after synchronization and 1 of wait
//------------------------------------------------------------------------------
#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR(void)
{
//Depending on Index Change destination address of DMAs
if(Switch==1)
{
DMA0DA = (__SFR_FARPTR)(unsigned int)&buffer0[0];
DMA0CTL |= DMAEN; // Enable
DMA0CTL |= DMAREQ; // Trigger block transfer
//DMA1DA = (__SFR_FARPTR)(unsigned long)&dataBuff2.bufferA5[0];
}
else
{
DMA0DA = (__SFR_FARPTR)(unsigned int)&buffer1[0];
DMA0CTL |= DMAEN; // Enable
DMA0CTL |= DMAREQ; // Trigger block transfer
//DMA1DA = (__SFR_FARPTR)(unsigned long)&dataBuff1.bufferA5[0];
}
//Clear IFGs
__bic_SR_register_on_exit(LPM0_bits);
}
#pragma vector = DMA_VECTOR
__interrupt void DMA_ISR(void)
{
Switch^=1;
__bic_SR_register_on_exit(LPM0_bits);
}

Thank you!

  • You did not drain the buffers, so they will get full sooner or later. After that, you have the problem of data overflow.

    In your case, aside from filling one buffer with DMA of ADC, you need to drain the other buffer, if not empty, with CPU or a different DMA. If you can manage to empty the draining buffer before the filling buffer is full, then you will not have overflow problem.
  • Is there any register to erase a value in DMA using another channel of DMA (for example ch2)? I would like to keep CPU free. Or I need to use:

    DMA2SA =(__SFR_FARPTR)(unsigned int)&string1[]; // Source block address
    DMA2DA =(__SFR_FARPTR)(unsigned int)&string1[0]; // Destination block address
    Using the DMA I would be able to manage parallel process in the same time using DMA and CPU?
  • Sorry, I did not try to understand your code. I was just commenting on the principle of using data buffer.

    Could someone else help to check the code?
  • >Could someone else help to check the code?

    No one likes trying to read unformatted code. Perhaps OP could go back and edit her post to paste the code in using the code [<>] button in the rich-format editor.
  • I´m sorry I didn´t see the button!
  • I solved the problem with the double buffer, I´m able to drain the buffer but when I try to use the Uart I got less sample than expected.
    My idea is Use ADC12 to trigger DMA0 (DMA0 is used to store samples on RAM) than DMA0 triggers DMA1 which transfers samples to UCA3TXBUF.(I would like to use 115200 Baud Rate(1MHz clocksource) to send samples recorded using a 11 050Hz sample frequency. Is it possible? Can I use an higher Sample frequency/Baud rate?
    Thank you for your suggestions!

    Here is my actual code:

    void DMA_init()
    { // Setup DMA0 double buffer
    DMACTL0 = DMA0TSEL_24; // ADC12IFGx triggered
    DMACTL4 = DMARMWDIS; // Read-modify-write disable
    DMA0CTL &= ~DMAIFG;
    DMA0SZ = Num_Samples; // Block size
    DMA0CTL = DMADT_4 +//Rpt single transfer
    DMASRCINCR_0 +//Source address unchanged
    DMADSTINCR_3 + //Destination address increment
    DMASBDB +
    DMAIE +
    DMALEVEL; //Transfer byte to byte
    //DMA1 to UART
    DMACTL0 = DMA1TSEL_30; // DMA0 Trigger
    DMA1CTL = DMADT_6 + DMASRCINCR_3 + DMASBDB + DMALEVEL; //burst block transfer, inc src, enable
    }
    void DMA_transfer()
    {
    if(Switch==1)
    {
    __data16_write_addr((unsigned short) &DMA0SA,(unsigned long) &ADC12MEM0);
    __data16_write_addr((unsigned short) &DMA0DA,(unsigned long) &buffer1);
    }
    else if(Switch==0)
    {
    __data16_write_addr((unsigned short) &DMA0SA,(unsigned long) &ADC12MEM0);
    __data16_write_addr((unsigned short) &DMA0DA,(unsigned long) &buffer0);
    }
    DMA0CTL |= DMAEN; // Enable DMA0
    DMA0CTL |= DMAREQ; // Trigger block transfer
    
    }
    void Uart_transfer()
    {
    DMA1SZ = sizeof (buffer0); // Block size
    if(Switch==1)
    {
    __data16_write_addr((unsigned short) &DMA1SA,(unsigned long) &buffer1);
    __data16_write_addr((unsigned short) &DMA1DA,(unsigned long) &UCA3TXBUF);
    }
    else if(Switch==0)
    {
    __data16_write_addr((unsigned short) &DMA1SA,(unsigned long) &buffer0);
    __data16_write_addr((unsigned short) &DMA1DA,(unsigned long) &UCA3TXBUF);
    }
    DMA1CTL |= DMAEN; // // Enable DMA1
    DMA1CTL |= DMAREQ; // Trigger block transfer
    }
    
    //where these functions are called into the main function using a while loop
    while(1)
    {
    __bis_SR_register(LPM3_bits+GIE);//enter LPM0 waiting interrupt
    if(overflow)
    { overflow=0;
    TA0CCR1 = ADC12_Receive();
    __bis_SR_register(CPUOFF);// ADC_ISR force exit
    ADC_result = ADC12MEM0;
    DMA_transfer();
    Uart_transfer();
    }
    }

  • In general the data rate of the source, the destination, and the data channel have to match each other.

    In your case, the source is from ADC12. If I understand correctly, you want to have a constant sampling rate of 11050Hz. That means 22100 bytes per second (if you do not use CPU to pack 2 samples of 12 bits each into 3 bytes of 8 bits each).

    If you use UART as your data channel to transmit to the data destination, you will need to use baud-rate greater than 221000. The UART in your MSP430 can handle such baud-rate (with 12 or 24 MHz SMCLK).

    You also have to make sure that the data destination at the receiving end of UART can handle the baud-rate continuously.

    You may not need data buffers and may use DMA to transfer data from ADC12 to the UART directly and continuously.

    I did not check, but I think your existing code may cause irregular sampling rate between the DMA block transfers. DMA in Continuous mode will not cause this problem.
  • I agree with your suggestion to use DMA in Continuous mode and I know that this will allow me to get a continuous communication. The question is that I need to do other operations while sending these data (such as send through UART samples from other Sensors) and I would like to avoid mutex while reciving sample data at the data destination.

    Is DMA double buffers useful for this??

    My audio signal is 8bit res 1 channel and I read on the datasheet that the maximum BITCLK clock frequency(equals baud rate in MBaud) is 1 MHz which is the maximum SMCLK frequency that I would be able to use?

    May I have an higher baud rate than 115 200 and 11 050Hz sample frequency?
    Thank you

**Attention** This is a public forum