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.

program for adc conversion via spi

Other Parts Discussed in Thread: MSP430FG4618

hi

i just wanted to know if the below program was right to perform SPI communication between MSP430fg4618 and AD7823ADC (no offence for not using a TI adc...had this in hand)

#include  "msp430fg4618.h"

void main(void)
{
 
  char data;

  WDTCTL = WDTPW+WDTHOLD;                   // Stop watchdog timer
 

                          
  P3SEL |= 0x0A;                                                               // P3.3 for CLK and P3.1 for DATA
  P3DIR |= 0x01;                                                             // P3.0 output FOR /CS
  UCB0CTL0 |= UCMST+UCSYNC+UCMSB;           // MASTER, SYNCHRONOUS, MSB FIRST
  UCB0CTL1 |= UCSSEL_1;                                     // AMCLK
  UCB0BR0 = 0x02;                          
  UCB0BR1 = 0;
  UCB0CTL1 &= ~UCSWRST;                    

  while(1)
  {
    P3OUT &= ~0x01;                         // Enable AD7823, /CS reset
    UCB0TXBUF = 0x00;                       // write Dummy BYTE to start SPI
    while (!(IFG2 & UCB0RXIFG));            // RXBUF ready?

    data = UCB0RXBUF;                      

    P3OUT |= 0x01;                          // Disable AD7823, /CS set


  }
}

 

  • Your ADC is an 8 bit type? The MSP430FG4618 has an internal 12 bit ADC with 12 external and 4 internal channels.

    However, the code looks good at first. The transfer itself should work. However, I doubt that the AD can immediately answer your request. I guess there is either a minimum time beween asserting CS and the first clock pulse of the SPI, so the AD has time to make a conversion. But I don't have the datasheet at hand.

    In your code, the first SPI clock pulse likely comes 2µs after asserting CS (worst case guessed by default MCLK)

    However, you selected ACLK, which defaults to a watch crystal on XT1. If the crystal is not running, no clock pulses are generated at all and no data is received.

     

  • yeah...its 8 bit0726.AD7823.pdf

    frm ure point, there seems to be an error in my program...

    assume i'm operating in mode 2, how do i program the ADC?

    I give a rising edge to the /CS...then a delay of 1.5usec....then falling edge...then delay of 5usec...then the next rising edge....

    so how do i program the timers of msp to give these delays...coz if i take up mode, then TACCR= (5*32768*10^-6) - 1, which gives a negative value....

     

    i have attached the data sheet of the ADC

  • I read quickly throught he AD docs and I'm a bit confused about this mode 2. It's mainly the words used in the description: "when the part is powered up, teh CONVST signal is brought low". It sounds as if the AD is bringing th esignal low to signal that it has powered up. Else I'd expect words liek 'the signal can be pulled low again' or such.

    However, I'll assume this signal is solely controlled by the MSP.

    First, the question is, what kidn of dat ado you newed. By your choice of mode2, I assume, you want samples which coem much slower than the maximum sampling speed of the AD and you want to preserve power in the meantime.

    amj said:
    I give a rising edge to the /CS...then a delay of 1.5usec....then falling edge...then delay of 5usec...then the next rising edge....

    If you want the SD to give the samples as fast as possible, you don't need mode 2. It makes no sense to power down the chip if you power it up again immediately.

    In any case, I can see several different setups.

    Mode1 operation (maximum throughput)

    have CONVST low at startup (e.g. with pulldown resistor, teh MSP is high-impedance at startup)
    after some µs (do your MSP setup in the meantime) pull it high
    Wait at least 1.5µs.
    The AD is now operational.
    Pull down CONVST for at least 20ns, 2µs max, then pull it high again.
    wait at least 5µs (including the high time)
    The conversion result is ready now, you can statr the data transfer
    repeat with pulling down CONVST again for at least 20ns.

    Data transfer may not take longer than the 5µs conversion time. You'll get a new sample every 5µs (=200ksps)

    Mode2 operation (minimum power consumption for low sample rates)

    have CONVST low at startup (e.g. with pulldown resistor, the MSP is high-impedance at startup)
    wait some time (setup your MSP)
    Pull it high for at least 1.5µs, then pull it low again
    wait at least 5µs
    start the data transfer (if you don't wait the 5µs, you'll get the result of the last conversion instead)
    wait as long as you want, the device goes into sleep mode
    eventually repeat with pulling CONVST high again

    Now for the setup of the MSP timing. It depends on what you want.

    Generally, let the timer do most of the timing. That's what the hardware is made for. In this case, use the PWM feature to control CONVST.

    Mode 1: Configure the timer for a fast clock, e.g. 8MHz timer tick. We only need a timespan of few µs.

    Mode 2: Configure the timer for a 500kHz timer tick: configure SMCLK to run at 0.5/1/2/4MHz, the use SMCLK as timer clock source with /1,/2,/4,/8 divider (ID_x), so the timer increments every 2 µs. This allows for longer timings. up to 64ms (=15 Hz minimum samping rate)

    In both cases let the timer run in up mode.

    Now configure the PWM. For mode 1, we only require CCR0. For mode 2, we need CCR0 and CCR1.

    In CONVST is controlled by TA0.0 pin

    Now define the cycle times.

    For mode 1, set CCR0 to a value that is 5µs (e.g. for a timer tick of 128ns = 8MHz timer frequency, use 8*5 = 40) configure CCTL0 to set/reset mode.
    What happens? when the timer counts to the value of CCR0 (=40), TA0.0 gets high (set). One tick later (e.g. 128ns), TimerA rolls over to 0 and resets TA0.0 to low (reset). This triggers the conversion. now 40 ticks (=5 ms) nothing is done, then TAR counts once more to CCR0. 5µs have passed, the conversion is ready, CONVST is pulled high again, then low and so forth.

    In your code, you check fo the CCRIFG bit being set in CCTL0 (busy waiting loop). If it is set, write a dummy byte to the TXBUF to start conversion, then clear the CCRIFG bit manuall. Then wait until RXIFG is set, indicating that the conversion result has been received. Read RXBUF and you've got your sample. Repeat with waiting for CCRIFG.
    This is, however, on a tight schedule. You only have 41 clock cycles per loop. YOu can, however, use a different (larger) value for CCR0. This stretches the time.
    For this mode, no ISRs are used since the ISR latency times will leave you even less time for handling the data.

     

    For mode 2, set CCR0 to a value that is your desired delay between two samples -1 (e.g. for one sample every ms = 1ksps use 499, as one tick is 2µs and 0 counts as a tick).
    CCTL0 needs to be configured for set/reset mode too.
    Now write 3 to CCR1. If you want interrupts, enable the CCIE bitin CCTL1. This callsTIMER0A1 interrupt (NOT Timer0A0 interrupt!) or however this one is named.
    What happens? When TAR counts to 499 with a pace of 1 per 2µs (remember: 500kHz timer frequency), TA0.0 is set. Next timer tick, 2µs later, TAR rolls over to 0, clearing TA0.0 again. This is the 2µs (>1.5µs) high time for poweing up the AD. The conversion starts at this point. 3 ticks (=6µs) later, CCR1 triggers an interrupt. You ISR can write a byte to TXBUF and fetch the result. If the SPI is configured for interrupt usage too, another interupt is triggered when teh byte is received and you can fetch it from RXBUF inside the RX ISR.

    You can choose different values for the tiemr tick and the CCRs, but the minimum timings mus tbe maintaind. And keep in mind that you need enough time to  execute your code and fetch the result between two conversions.

     

    In both cases, once the CCRs are configured, write 0 to TAR (to restart the timings) and set the TA0.0 pin for module usage (PxSEL |= y, where x is th eport and y th epin bit of the TA0.0 pin).

  • i thank God for your patience to write so much...based on that I wrote the code as that follows for Mode 1 operation:

    #include  "msp430fg4618.h"
    #include <in430.h>

    void timerAinit(void)
    {

    TACCTL0= 0x70;                            //set-reset mode, CCIE
    TACCR0 = 39;
    TACTL = 0x0210;                          //SMCLK,Up mode
    }


    void main(void)
    {

    volatile unsigned int i;
     
     WDTCTL = WDTPW+WDTHOLD;
    SCFQCTL = 0xF9;                            //N=121, so f=(121+1)*32768*2=7.99MHz
    SCFI0 = 0x44;                              // D=2, FN_2 set according to DCO frequency range

    FLL_CTL0 = 0x30;                           //DCO outputis divided, Xcap=10pf
    FLL_CTL1 = 0x20;                           //XT2 is OFF, MCLK=SMCLK=DCO


    do
      {
      IFG1 &= ~OFIFG;                          
      for (i = 0x47FF; i > 0; i--);            
      }
      while ((IFG1 & OFIFG));

     P3SEL |= 0x0A;                            // P3.3 for CLK and P3.1 for DATA
     P1DIR |= 0x01;                            // P1.0 output FOR /cs
     UCB0CTL0 |= UCMST+UCSYNC+UCMSB;           // MASTER, SYNCHRONOUS, MSB FIRST
     UCB0CTL1 |= UCSSEL_2;                     // sMCLK
     UCB0BR0 = 0;                          
     UCB0BR1 = 0;
     UCB0CTL1 &= ~UCSWRST;                
     
     P1OUT |= 0x01;                             //pulled /CS high
     _delay_cycles(12);                        //wait 1.5usecs

    while(1)
      {
        P1OUT &= ~0x01;                         // /CS goes low
        _delay_cycles(16);                     // wait 2usecs
        P1OUT |= 0x01;                          // /cs goes high

        timerAinit();                          //5usec delay
     
    __bis_SR_register(GIE);                   

      }
    }

    #pragma vector=TIMERA0_VECTOR
    __interrupt void Timer_B (void)
    {
      char data;
     UCB0TXBUF = 0x00;                       // write Dummy BYTE to start SPI
     TACCTL0 &= ~CCIFG;
     while (!(IFG2 & UCB0RXIFG));            // RXBUF ready?

        data = UCB0RXBUF;  
                                                                   
    }

  • amj said:
    based on that I wrote the code as that follows for Mode 1 operation:

    And how does it work? (I suppose it doesn't work as expected)

    amj said:
    timerAinit();                          //5usec delay

    How do you initalize the timer? And why do you do it in each loop? My suggestion was to use the CCR units to automatize the process of triggering the CS signal. If you do this with delay_cycles, you can as well use delay_cycles for the 5µs delay and remove the timer ISR completelydoing everything in software.

    The advantage of using a timer is that the timeout of the timer as well as the execution of the signal changes is independent of code execution. delay_cycles will execute the given number of cycles, but will not take account for any code execution times you have between the delay calls. Also, interrupt shwich happen during the delay will add to the delay.

    Also, it is a bad idea to do a busy-waiting loop inside an ISR. It will extend the execution time of the main loop by an unknown (maybe unlimited) amount and destroy any timing in the main loop. It will also block any othe rinterrupts unless you enable interrupt nesting. But that's something where you have to exactly knwo how to do it or you're hosed easily.
    It's better to enable interupts for UCRXIFG and write anotehr ISR that is triggered as soon as the data has been received.

**Attention** This is a public forum