• Not Answered

Using Pointer to help send multiple AD Conversions out Serially

Hey guys,
I am trying to use a DMA and Pointers to help me send A/D values through serial. Right now, I can only get the code to display the values coming in from ADC12MEM0
on my terminal program. The other values being dumped into my RAM are not being transmitted to the terminal. Therefore, I am trying to set up a pointer that will keep track of 
where I am in RAM and send every conversion to the terminal. Here is my code, I tried using the comments to explain what I was thinking

# include <msp430xG46x.h>

int ADCSample;
int *Pointer1;
int ADCSample2;

void main(void)
{
WDTCTL = WDTPW+WDTHOLD; // Stop watchdog

P5DIR |= 0x02;
P5OUT |= 0x02;
// Initialization of ADC12//

P6SEL |= 0x01; // Enable A/D channel A0
ADC12CTL0 &= ~ENC; // Disable Conversions
ADC12CTL0 = ADC12ON + SHS_1 + REFON + REF2_5V + MSC; // turn on ADC12, set samp time
ADC12CTL1 = SHP+CONSEQ_2; // Use sampling timer
ADC12MCTL0 = SREF_1+INCH_0; // Vr+=VeREF+ (external)
//ADC12IFG = 0;


//Timer A
TACCR0 = 1500; // Delay to allow Ref to settle
TACCR1 = 1470;
//TACCTL0 |= CCIE; // Compare-mode interrupt.
TACCTL1 = OUTMOD_7;
TACTL = TASSEL_1 + MC_1 + TACLR; // TACLK = ACLK, Up mode.
//__bis_SR_register(LPM3_bits + GIE); // Wait for delay, Enable interrupts
ADC12CTL0 |= ENC; // Enable conversions

// Initialization of Rs-232//

FLL_CTL0 |= XCAP14PF; // Configure load caps
do
{
IFG1 &= ~OFIFG; // Clear OSCFault flag
_delay_cycles(50000); // Time for flag to set
}
while ((IFG1 & OFIFG)); // OSCFault flag still set?

P2SEL |= 0x30; // P2.4,5 = USCI_A0 RXD/TXD
UCA0CTL1 |= UCSSEL_1; // CLK = ACLK
UCA0BR0 = 0x03; // 32k/9600 - 13.65
UCA0BR1 = 0x00; //
UCA0MCTL = 0x06; // Modulation
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
IE2 |= UCA0TXIE + UCA0RXIE; // enable RXD and TXD interrupt;


// Initialize DMA
DMACTL0 = DMA0TSEL_6; // ADC12IF set
DMACTL1 = DMAONFETCH;
__data16_write_addr((unsigned short) &DMA0SA, (unsigned long) &ADC12MEM0); // Source address
__data16_write_addr((unsigned short) &DMA0DA, (unsigned long) 0x001108); // Destination single address
DMA0SZ = 0x0FFF; // Set DMA Block size ADCSample size
DMA0CTL = DMADT_4 + DMADSTINCR_3 + DMAIE + DMADSTBYTE + DMASRCBYTE; // Repeat single, inc dst, interrupts

ADCSample = ADC12MEM0; //Set integer to value of ADC12MEM0
Pointer1 = &ADCSample; //Set Pointer1 to the address of ADCSample(Not sure where this address, I want it to be at 0x001108)
DMA0CTL |= DMAEN; //Enable DMA
ADC12CTL0 |= ADC12SC; //Start Conversions


//Serial Loop
while(*Pointer1 <= 0x030FF) // Execute loop until loop reaches 0x030FF address
{
ADCSample2 = *Pointer1; //Set integer to value stored in Pointer1
UCA0TXBUF = ADCSample2 >> 8; //Send upper byte from Pointer1 to Serial
while(!(IFG2 & UCA0TXIFG))
{
__delay_cycles(1000);// wait for first transmit
}
UCA0TXBUF = ADCSample2;
*Pointer1 = (*Pointer1 + 1) &0x030FF; //Increment Pointer1 from address 0x001108 to 0x030FF
__bis_SR_register(LPM0_bits + GIE);
}
}



5 Replies

  • Martin Novotny102956
    IE2 |= UCA0TXIE + UCA0RXIE; // enable RXD and TXD interrupt;

    Why do you enable interrupts fo rRX and TX if you want to handle the sending in a busy-waiting loop?

    You didn't post any ISR, so I assume you have none, so the first interrupt 8after GIE has been set) will jump into the void, crashing and resetting the MSP.

    Martin Novotny102956
    ADC12CTL0 |= ADC12SC; //Start Conversions

    Yes, it starts conversions. However, you do not wait for the conversions to complete. So your following code tries to send conversion results that haven't been sampled yet.

    Martin Novotny102956
    while(*Pointer1 <= 0x030FF) // Execute loop until loop reaches 0x030FF address

    No. This while lopps until the INT value where Pointer1 points to, exceeds 0x30FF.
    Since oyu assigned Pointer1 = &ADCSample, and ADCSample is statically filled with, well, the power-on value of ADC12MEM0 some lines before, this loop will run either forever or never

    The DMA probably does the transfer of 4k of words (= 8k data) from ADC12MEM0 to 0x1108 to 0x3106 (overwriting everything that happens to be placed there by the linker), but it would be pure coincidence if this changes *Pointer1 without also messing up Pointer1 itself.

    Martin Novotny102956
    while(!(IFG2 & UCA0TXIFG))
    {
    __delay_cycles(1000);// wait for first transmit
    }

    THis makes no sense. Either you wait for TXIFG set (then you don't need a delay) or you want a delay (then you don't need the while).
    Jus tuse the while, with no delay code.

    Martin Novotny102956
    *Pointer1 = (*Pointer1 + 1) &0x030FF; //Increment Pointer1 from address 0x001108 to 0x030FF

    Definitely not.

    What thsi does is: it takes the value pointed to by Pointer1, increments this value by 1, then does a bit-wise AND with 0x30ff and stores the result back to the memory location Pointer1 points to (whcih still is either ADCSample variable, or maybe anywhere int the addressing range, depending on what damage teh DMA has done.

    The whole construct makes no sense, sorry.
    I guess, you didn't really understand what pointers in C are and how they are used.

  • In reply to Jens-Michael Gross:

    Sorry, I guess I'm not sure why you bothered to even reply to my post when all you did was tell me everything I did wrong and offered no means of assistance. Of course, the code does not do what I was describing and NO I do not have a great understanding of what pointers in C are, but no where in your post did you make any attempt to help me learn or suggest ways to improve my code. Therefore do not bother responding to this reply because I won't be coming back to this forum. Its embarassing that you are to the go to expert on these forums as I posted a problem and instead of posting possible fix or even some good advice you simply told me everything already know which is that my code is obviously flawed.

  • In reply to Martin Novotny102956:

    Jens-Michael Gross is one of more tolerant gurus. You must have caught him at a bad moment.

    I am guessing you are trying to DMA a number of samples from ADC to a buffer and simultaneously sending the buffer to the serial port. I think your code is using unallocated memory as though it was an array. This is not safe as the unallocated memory can move. Better to allocate yourself an array and ask the DMA to fill that array. The questions is not really about pointers but more of buffer arrays and DMA transfer. My background is not with MSP430...here's my "straw man" version of your code. See if others will knock it down.

    # include <msp430xG46x.h>

    #define SAMPLES 256
    volatile int ADCSamples[SAMPLES];

    void main(void)
    {
      int i;
      int x;

      WDTCTL = WDTPW+WDTHOLD; // Stop watchdog

      P5DIR |= 0x02;
      P5OUT |= 0x02;

      // Initialization of ADC12//
      P6SEL     |= 0x01;                                    // Enable A/D channel A0
      ADC12CTL0 &= ~ENC;                                    // Disable Conversions
      ADC12CTL0  = ADC12ON + SHS_1 + REFON + REF2_5V + MSC; // turn on ADC12, set samp time
      ADC12CTL1  = SHP+CONSEQ_2;                            // Use sampling timer
      ADC12MCTL0 = SREF_1+INCH_0;                           // Vr+=VeREF+ (external)
      //ADC12IFG = 0;

      //Timer A
      TACCR0 = 1500; // Delay to allow Ref to settle
      TACCR1 = 1470;
      //TACCTL0 |= CCIE; // Compare-mode interrupt.
      TACCTL1 = OUTMOD_7;
      TACTL = TASSEL_1 + MC_1 + TACLR; // TACLK = ACLK, Up mode.
      //__bis_SR_register(LPM3_bits + GIE); // Wait for delay, Enable interrupts
      ADC12CTL0 |= ENC; // Enable conversions

      // Initialization of Rs-232//
      FLL_CTL0 |= XCAP14PF;   // Configure load caps
      do
      {
        IFG1 &= ~OFIFG;       // Clear OSCFault flag
        _delay_cycles(50000); // Time for flag to set
      }
      while ((IFG1 & OFIFG)); // OSCFault flag still set?

      P2SEL    |= 0x30;                // P2.4,5 = USCI_A0 RXD/TXD
      UCA0CTL1 |= UCSSEL_1;            // CLK = ACLK
      UCA0BR0   = 0x03;                // 32k/9600 - 13.65
      UCA0BR1   = 0x00;                //
      UCA0MCTL  = 0x06;                // Modulation
      UCA0CTL1 &= ~UCSWRST;            // **Initialize USCI state machine**
      IE2      |= UCA0TXIE + UCA0RXIE; // enable RXD and TXD interrupt;


      // Initialize DMA
      // Repeat single src, inc dst, interrupts
      DMACTL0 = DMA0TSEL_6; // ADC12IF set
      DMACTL1 = DMAONFETCH;
      __data16_write_addr((unsigned short) &DMA0SA, (unsigned long) &ADC12MEM0); // Source
      __data16_write_addr((unsigned short) &DMA0DA, (unsigned long) ADCSamples); // Dest
      DMA0SZ  = SAMPLES; // Set DMA Block size ADCSample size BYTES? ITEMS?????
      DMA0CTL = DMADT_4 + DMADSTINCR_3 + DMAIE + DMADSTBYTE + DMASRCBYTE;

      DMA0CTL   |= DMAEN;     //Enable DMA
      ADC12CTL0 |= ADC12SC;   //Start Conversions

      // Need to wait for first sample to complete here. How to?

      //Serial Loop
      //Send samples from DMA buffer to serial port.
      //Assumes that ADC samples faster that the serial port transmits.
      for(i=0; i<SAMPLES; i++)
      {
        x = ADCSamples[i];                   // Get a 16 bit sample
        UCA0TXBUF = x >> 8;                  // Send upper byte to Serial
        while(!(IFG2 & UCA0TXIFG)) continue; // Wait for first transmit
        UCA0TXBUF = x;                       // Send lower byte to Serial
        while(!(IFG2 & UCA0TXIFG)) continue; // Wait for second transmit
        __bis_SR_register(LPM0_bits + GIE);  // What does this do?
      }
    }

    I've put questions marks '?" where I'm not sure what is your intent. Note that the code sends binary values across the serial port. A terminal program such as Hyperterminal wants ASCII characters. Maybe your terminal program has a mode where it will print out binary values in human readable form.

  • In reply to Martin Novotny102956:

    Martin Novotny102956
    Sorry, I guess I'm not sure why you bothered to even reply to my post when all you did was tell me everything I did wrong and offered no means of assistance.

    Well, Telling you where some fundamental errors hide in your code (well, the do not really 'hide') is definitely some sort of assistance.

    It won't help you if I jsut write a workign code version for you. You wouldn't learn anything and come back to the forum with your next non-working code. Telling you where you did wrong (and giving you the advice to take a class about usage of C pointers) will, if accepted by you, increase your knowledge and therefore your ability to do it right by yourself, while at the same time decreasing the probability that you come back for more assistance soon - with the very same mistakes.

    Martin Novotny102956
    I do not have a great understanding of what pointers in C are, but no where in your post did you make any attempt to help me learn or suggest ways to improve my code

    I gave the suggestion to learn about pointers. Once doing so successfully, you'll see by yourself where and why you mixed up pointers, references and values.
    And I gave you a lot of specific information about where you did wrong.

    Martin Novotny102956
    you simply told me everything already know which is that my code is obviously flawed

    Not that (this you obvbiously know or else you wouldn't have pasted here), but why.

    And I never provide complete code. First because I don't have the time to write it and test it (I surely don't want to release untested code), then I don't have the equipment to test the code (there are ~400 MSP derivates, not counting the required external circuitry for each case). And finally, nobody learns walking if he's carried around all the time.
    I may lend a hand while some tries to walk, but if someone hasn't even discovered that he has legs... well, I have given private lessons in the past. Mainly chemistry, physics and math. But for cash. And back then I had the time for doing so.
    Today, I have a fulltime job, and it's not being the 'go to expert'. It's not even for TI. I do this is my spare time. Free of charge.

  • In reply to Norman Wong:

    Norman Wong
    Jens-Michael Gross is one of more tolerant gurus. You must have caught him at a bad moment.

    Thanks :) Well, my time is limited and I try to help people who need my help (or from someone else with deeper MSP knowledge). It's always a 'bad time' when I come over a post that requests 'I have a job to do, can you give the the finished code for free'. I ususally ignore them, or write a short sarcastic answer.
    But then there are cases where I see that someone has already written code (sometimes more than my weary eyes want to read) and asks for advice, and I usually start replying. But if I then discover that there are basic coding errors that are not MSP related, and indicate that fundamental concepts of the used programming language are unknown or at least not understood, I consider this as a waste of my time. Literally bad (= wasted) time. Sure, I  could simply close the browser tab and move on, but since I already started replying, I tend to 'finish' it instead and point at least in a direction that will result in some improvement.

    But now to your code. I didn't check the clock and por tinitialization. However, there are a few things...

    Norman Wong
      //__bis_SR_register(LPM3_bits + GIE); // Wait for delay, Enable interrupts

    This line is intended to put the CPU to sleep until the timer triggers an interrupt and the interupt funciton ends the sleep mode. But since it is ina comment, the necessary settling delay isn't performed. So it shoud be replaced by a busy-waiting version:

    TACCTL0 &= ~CCIFG; clear interrupt flag
    while (!(TACCTL0&CCIFG)); // wait until TAR has counted to TACCR0

    Norman Wong
    IE2      |= UCA0TXIE + UCA0RXIE; // enable RXD and TXD interrupt;

    You shouldn't enable interrupts if you intend to manuall do the transfer in main. And definitely not before you have somehting to send :)
    As soon as you set UCA0TXIE, the transmit ISR is called (and I don't see one).

    Norman Wong
    DMA0SZ  = SAMPLES; // Set DMA Block size ADCSample size BYTES? ITEMS?????

    Transfers (ITEMS). The DMA can do byte and word transfers. It can even do write reads to word writes (clearing the upper byte), or word reads to byte (dropping the upper byte) in case the peripheral requires a word access.

    Norman Wong
      DMA0CTL = DMADT_4 + DMADSTINCR_3 + DMAIE + DMADSTBYTE + DMASRCBYTE;

    I Guess, DMADT_0 is to be used here. Else the transfer is repeated from start after the 256th conversion. (but maybe that's what you want later).
    Also, I don't see a DMA ISR, so DAMIE really shouldn't be set.

    Norman Wong
      // Need to wait for first sample to complete here. How to?


    You can check DMA0SZ. It is decremented after each transfer.

    Norman Wong
        __bis_SR_register(LPM0_bits + GIE);  // What does this do?


    Normally, it puts the CPU to sleep but allows interrupts. So if an interrupt is triggered, the CPU is activated, and once teh ISR returns, it goes back to sleep (unless the ISR has explicitely ended the sleep mode using the bic_SR_regsiter_on_exit(LPM0_bits) intrinsic.
    The LPM bits ar epart of the processor status register, which is saved on ISR entry and restored (together with the LPM bits) on ISR exit. LPM0_bits is a macro that ony contains the CPUOFF bit, disabling MCLK. LPM1_bits would also disable the DCO, but then there would be a delay for switching MCLK on again. And since the DMA transfer also requires MCLK...

    However, there is no ISR at all, so this line is not doing any good.
    It should be replaced by something that checks DMA0SZ:

    while(DMA0SZ>=(SAMPLES-i));