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.

PLZ Handling ADC sampling rate & UART transmit speed for waveform

I'd like to make a PPG&GSR signal waveform mornitoring system by MSP430&Android.

My system is simply ADC12(12bit) -> NumToString(for 1signal(2chanel)12Char=12byte=96bit) -> UART(8bit, 119200bps) -> Bluetooth(8bit, 1~3MB/s) -> Android

But I just get 50~60sample per second(I need 200~300sample per second). I guess my problem is in Timer or Main Clock or UART.

 

My question is...

1. When I changed Timer clock register(TA1CCR0), nothing changed(I still got same sample per second)

2. My freind uses NumToBinary algorithm and get around 250 sample per sec, I know it is more effiecient than my way. But I don't know why even I chaned up UART speed it doesnt works. I messured this. UART speed is 119200bps=14.4kB/s and 1 signal(12char=12byte). So, 14400/12=1200 sample per sec. am I wrong?

 

I don't know exactly know what is the problem and looking forward to your help

thanks indeed.

 

I attach my main code

----------------------------------

 

void main(void) {

    

    WDTCTL = WDTPW + WDTHOLD; // Stop watch dog timer

    

   //Initialize LCD

   halLcdInit();

   halLcdBackLightInit();

   halLcdSetBackLight(0);

   halLcdSetContrast(100);

   halLcdClearScreen();

    

   __bis_SR_register(SCG0);                  // Disable the FLL control loop

   UCSCTL0 = 0x0000;                         // Set lowest possible DCOx, MODx

   UCSCTL1 = DCORSEL_7;                      // Select DCO range 16MHz operation

   UCSCTL2 = FLLD_1 + 249;                   // Set DCO Multiplier for 8MHz

                                    // (N + 1) * FLLRef = Fdco

                                             // (249 + 1) * 32768 = 8MHz

                                             // Set FLL Div = fDCOCLK/2

   __bic_SR_register(SCG0);                  // Enable the FLL control loop

    

   //Initialize ADC12_A

   P7SEL |= 0x50;  // Enable A/D channel Port7.4, 7.6 (A12, A14)

   ADC12CTL0 = ADC12ON+ADC12MSC+ADC12SHT1_0;// Turn on ADC12, set sampling time , ADC12CLK Cycle 4

   ADC12CTL1 = ADC12SHP+ADC12CONSEQ_3+ADC12DIV_0; // Use sampling timer, Repeat-sequence-of-channels

   //ADC12CTL2 = ADC12RES_3;

   ADC12MCTL9 = ADC12INCH_12;// ref+=AVcc, channel = A12, ADC_12A Memory Control 9

   ADC12MCTL11 = ADC12INCH_14+ADC12EOS;// ref+=AVcc, channel = A14, ADC_12A Memory Control 11

   ADC12IE = 0xF080; //Enable ADC12IFG7, ADC12IFG15

   ADC12CTL0 |= ADC12ENC; // Enable conversions

        

   //Initialize UART

   P3SEL = 0x30; //Port3.4, 3.5

   UCA0CTL1 |= UCSWRST; // **Put state machine in reset**

   UCA0CTL1 |= UCSSEL_2; // CLK = SMCLK 8Mhz

   UCA0BR0 = 4; // 8MHz/9600=69.44 (see User's Guide)

   UCA0BR1 = 0x00; // 32.3.13 Typical Baud Rates and Errors

   UCA0MCTL = UCBRS_5+UCBRF_3+UCOS16; // Modulation UCBRSx=5, UCBRFx=3, Oversamping ON

   UCA0CTL1 &= ~UCSWRST;

   UCA1IE |= UCRXIE;

    

   //Initialize Timer

   TA1CCTL0 = CCIE; // CCR0 interrupt enabled

   TA1CCR0 = 8333; // 8MHz/8333ms divide =

   TA1CTL = TASSEL_2 + MC_1; // SMCLK 1MHz, Continuous mode, clear TAR

   __enable_interrupt();

    

   while(1);

}

    

// Timer A0 interrupt service routine

#pragma vector=TIMER1_A0_VECTOR

__interrupt void TIMER1_A(void)

{

   while(1){

      ADC12CTL0 |= ADC12SC; // Start conversion

      __bis_SR_register(LPM0_bits+GIE);

   }

}

    

// ADC12 interrupt service routine

#pragma vector=ADC12_VECTOR

__interrupt void ADC12_ISR(void){

   readADC();

}

    

void readADC(){

    

   sum_data[0] = (unsigned int)ADC12MEM9;   // Move sum_data[0], IFG is cleared p.7.4

   sum_data[1] = (unsigned int)ADC12MEM11;   // Move sum_data[1], IFG is cleared p.7.5

    

  numtostring(sum_data[0],sum_data[1]);

    

}

    

void numtostring(long num, long num2){

   char str[12] = {' '};

   int tmp;

   int i = 0;

   int j,k,l;

    

   while(num!=0){

      for(j=i; j>0; j--){

         *(str+j) = *(str+j-1);

      }

    

      tmp = num%10;

      if(tmp==0)

         *str='0';

      else if(tmp==1)

         *str='1';

      else if(tmp==2)

         *str='2';

      else if(tmp==3)

         *str='3';

      else if(tmp==4)

         *str='4';

      else if(tmp==5)

         *str='5';

      else if(tmp==6)

         *str='6';

      else if(tmp==7)

         *str='7';

      else if(tmp==8)

         *str='8';

      else if(tmp==9)

         *str='9';

      num = num / 10;

      i++;

   }

      str[i++]='A';

      str[i++]='/';

    

   k=i;

   halLcdPrintLineCol("PPG", 3, 1, OVERWRITE_TEXT);

   halLcdPrintLineCol(str, 3, 7, OVERWRITE_TEXT);

    

   while(num2!=0){

      for(j=i; j>k; j--){

         *(str+j) = *(str+j-1);

      }

      tmp = num2%10;

      if(tmp==0)

         *(str+k)='0';

      else if(tmp==1)

         *(str+k)='1';

      else if(tmp==2)

         *(str+k)='2';

      else if(tmp==3)

         *(str+k)='3';

      else if(tmp==4)

         *(str+k)='4';

      else if(tmp==5)

         *(str+k)='5';

      else if(tmp==6)

         *(str+k)='6';

      else if(tmp==7)

         *(str+k)='7';

      else if(tmp==8)

         *(str+k)='8';

      else if(tmp==9)

         *(str+k)='9';

      num2 = num2 / 10;

      i++;

   }

   str[i++]='B';

   str[i++]='/';

    

   halLcdPrintLineCol("GSR", 5, 1, OVERWRITE_TEXT);

   halLcdPrintLineCol(str+k, 5, 7, OVERWRITE_TEXT);

   for(j=0;j<i; j++){

      while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?

      UCA0TXBUF = str[j];

   }

}

  • Junwoo Yoo said:
       TA1CTL = TASSEL_2 + MC_1; // SMCLK 1MHz, Continuous mode, clear TAR

    MC_1 is up mode, but that's okay, I guess.
    In continuous mode, the timer runs from 0 to 65535. Changes in CCR0 change the moment at which CCR0-IRQ is triggered, but not the frequency. In Up mode, however, the timer counts from 0 to CCR0, so changing CCR0 changes the trigger frequency.

    Junwoo Yoo said:
       TA1CCR0 = 8333; // 8MHz/8333ms divide =

    It's actually /8334 then, as '0' is a count too. However, this should result in 1.04ms interrupt frequency.

    Junwoo Yoo said:
    #pragma vector=TIMER1_A0_VECTOR
    __interrupt void TIMER1_A(void)
    {
       while(1){
          ADC12CTL0 |= ADC12SC; // Start conversion
          __bis_SR_register(LPM0_bits+GIE);
       }
    }

    First, you can trigger the ADC directly by a timer output (use the proper OUTMOD setting). Check your device datasheet to see which timer output can be used to directly trigger the ADC. You don't need an ISR then at all.

    Then, you enter LPM inside an ISR. That's a death sin. It means your ISR will not exit, but will be interrupted by another ISR then. Worst case, this will fill your stack so it overflows, overwriting your variables first, then flushing into the void, crashing the CPU.

    If you want the system to return to LPM where it was before, you can simply exit the ISR, This will restore the previous LPM state.
    If you want to change the LPM state after the ISR exits (!) Use the __bis_SR_register_on_exit() intrinsic (or __bic...) instead (there are multiple different aliases of this intrinsic, check your compiler documentation.

    Another thing: in sequence or repeated modes, ADC12SC needs to be cleared before it can be set again. The trigger is edge-sensitive. Only in single conversion mode, ADC12SC clears when the conversion is done.

    However, you're setting ADC12SHP and MSC bits. This means, once started, the ADC will run the sequence over and over again with fastest speed, until you clear the ENC bit. This is why your timer has no effect at all, and changes on CCR0 won't change anything. Clear the MSC bit if you want to trigger each single conversion of the sequence separately. Or use sequence mode (instead of repeated sequence mode). In this case, your timer ISR can trigger the conversion of a new sequence by clearing and then setting the ADC12SC bit.

    Junwoo Yoo said:
    __enable_interrupt();
       while(1);

    Why don't you enter LPM right here and leave it this way?

    while(1) __bis_SR_register(LPM0_bits | GIE);

    Junwoo Yoo said:
    __interrupt void ADC12_ISR(void){
       readADC();
    }

    That's a bad idea. Calling functions from inside an ISR should be avoided.
    The function call forces the compiler to assume that the parameter passing registers are clobbered during the function call (standard calling convention). So it has to save them on stack. Simply move the function content directly into the ISR instead of making it a function. Otherwise you're wasting time and space. And ISRs should be as fast as possible.

    Also, your function called inside the ISR does so some really time-consuming things, like LCD output. This is misplaced in an ISR execution. The ISR should simply collect the data and then wake main, telling it (implicitly because of the wake, or explicitly by a global flag) that new data needs to be processed and sent.

    ISRs are no threads. ISRs are fast-in, fast out handlers for events. They shouldn't do more than immediately necessary to handle the event. Everything else should be done in main.

    So teh ADC ISR copies the data from ADC12MEM, clearing the IFG bits, then calls __bic_SR_register_on_exit(LPM0_bits).
    In main, you do

    while(1) {
      __bis_SR_register(LPM0_bits | GIE);
      _no_operation(); // don't put a breakpoint here right after entering LPM
      // new data has been arrived
      // process and display it
      // then loop back into LPM
    }
     

  • Jens-Michael Gross said:

    First, you can trigger the ADC directly by a timer output (use the proper OUTMOD setting). Check your device datasheet to see which timer output can be used to directly trigger the ADC. You don't need an ISR then at all.

    1. Acording to datasheets about OUTMOD..

    Could you let me know which is for my system? I don't understand this register...

    -------------------------------------

    17.2.5.1 Output Modes

    The output modes are defined by the OUTMOD bits and are described in Table 17-2. The OUTn signal is

    changed with the rising edge of the timer clock for all modes except mode 0. Output modes 2, 3, 6, and 7

    are not useful for output unit 0 because EQUn = EQU0.

    Table 17-2. Output Modes

    OUTMODx Mode Description

    000 Output The output signal OUTn is defined by the OUT bit. The OUTn signal updates immediately when OUT is updated.

    001 Set The output is set when the timer counts to the TAxCCRn value. It remains set until a reset of the timer, or until another output mode is selected and affects the output.

    010 Toggle/Reset The output is toggled when the timer counts to the TAxCCRn value. It is reset when the timer counts to the TAxCCR0 value.

    011 Set/Reset The output is set when the timer counts to the TAxCCRn value. It is reset when the timer counts to the TAxCCR0 value.

    100 Toggle The output is toggled when the timer counts to the TAxCCRn value. The output period is double the timer period.

    101 Reset The output is reset when the timer counts to the TAxCCRn value. It remains reset until another output mode is selected and affects the output.

    110 Toggle/Set The output is toggled when the timer counts to the TAxCCRn value. It is set when the timer counts to the TAxCCR0 value.

    111 Reset/Set The output is reset when the timer counts to the TAxCCRn value. It is set when the timer counts to the TAxCCR0 value

    The output modes are defined by the OUTMOD bits and are described in Table 17-2. The OUTn signal is

    changed with the rising edge of the timer clock for all modes except mode 0. Output modes 2, 3, 6, and 7

    are not useful for output unit 0 because EQUn = EQU0.

    Table 17-2. Output Modes

    OUTMODx Mode Description

    000 Output The output signal OUTn is defined by the OUT bit. The OUTn signal updates immediately when OUT is updated.

    001 Set The output is set when the timer counts to the TAxCCRn value. It remains set until a reset of the timer, or until another output mode is selected and affects the output.

    010 Toggle/Reset The output is toggled when the timer counts to the TAxCCRn value. It is reset when the timer counts to the TAxCCR0 value.

    011 Set/Reset The output is set when the timer counts to the TAxCCRn value. It is reset when the timer counts to the TAxCCR0 value.

    100 Toggle The output is toggled when the timer counts to the TAxCCRn value. The output period is double the timer period.

    101 Reset The output is reset when the timer counts to the TAxCCRn value. It remains reset until another output mode is selected and affects the output.

    110 Toggle/Set The output is toggled when the timer counts to the TAxCCRn value. It is set when the timer counts to the TAxCCR0 value.

    111 Reset/Set The output is reset when the timer counts to the TAxCCRn value. It is set when the timer counts to the TAxCCR0 value

     

    ----------------

     

    Jens-Michael Gross said:

    __interrupt void ADC12_ISR(void){    readADC(); }

    That's a bad idea. Calling functions from inside an ISR should be avoided. The function call forces the compiler to assume that the parameter passing registers are clobbered during the function call (standard calling convention). So it has to save them on stack. Simply move the function content directly into the ISR instead of making it a function. Otherwise you're wasting time and space. And ISRs should be as fast as possible.

    [/quote]

     

    2. I understand what you mean but when I tried to put function in main(while) it doesn't work..

    This is modified source code..

    --------------------

     

    void main(void) {

        

    WDTCTL = WDTPW + WDTHOLD; // Stop watch dog timer

        

    //Initialize LCD

    halLcdInit();

    halLcdBackLightInit();

    halLcdSetBackLight(0);

    halLcdSetContrast(100);

    halLcdClearScreen();

        

    __bis_SR_register(SCG0); // Disable the FLL control loop

    UCSCTL0 = 0x0000; // Set lowest possible DCOx, MODx

    UCSCTL1 = DCORSEL_7; // Select DCO range 16MHz operation

    UCSCTL2 = FLLD_1 + 249;           // Set DCO Multiplier for 8MHz

    // (N + 1) * FLLRef = Fdco

                                              // (249 + 1) * 32768 = 8MHz

                                              // Set FLL Div = fDCOCLK/2

    __bic_SR_register(SCG0);              // Enable the FLL control loop

        

    // Initialize LED

    P1DIR |= 0x01; // LED, Set P1.0 to output direction

        

    //Initialize ADC12_A

    P7SEL |= 0x50; // Enable A/D channel Port7.4, 7.6 (A12, A14)

    ADC12CTL0 = ADC12ON+ADC12MSC+ADC12SHT1_0; // Turn on ADC12, set sampling time , ADC12CLK Cycle 4

    ADC12CTL1 = ADC12SHP+ADC12CONSEQ_1+ADC12DIV_0; // Use sampling timer, -sequence-of-channels

    ADC12CTL2 = ADC12RES_2;

    ADC12IE = BIT9+BITB; // Enable ADC12IFG9, ADC12IFG11

    ADC12MCTL9 = ADC12INCH_12;// ref+=AVcc, channel = A12, ADC_12A Memory Control 9

    ADC12MCTL11 = ADC12INCH_14+ADC12EOS; // ref+=AVcc, channel = A14, ADC_12A Memory Control 11

    ADC12CTL0 |= ADC12ENC; // Enable conversions

        

        

    //Initialize UART

    P3SEL = 0x30; // Port3.4, 3.5

    UCA0CTL1 |= UCSWRST; // **Put state machine in reset**

    UCA0CTL1 |= UCSSEL_2; // CLK = SMCLK 8Mhz

    UCA0BR0 = 4; // 8MHz/115200=69.44 (see User's Guide)

    UCA0BR1 = 0x00; // 32.3.13 Typical Baud Rates and Errors

    UCA0MCTL = UCBRS_5+UCBRF_3+UCOS16; // Modulation UCBRSx=5, UCBRFx=3, Oversamping ON

    UCA0CTL1 &= ~UCSWRST;

    UCA1IE |= UCRXIE;

        

    //Initialize Timer

    TA1CCTL0 = CCIE + OUTMOD_4; // CCR0 interrupt enabled

    TA1CCR0 = 3999; // 8MHz/8333ms divide =

    TA1CTL = TASSEL_2 + MC_1; // SMCLK 1MHz, Up mode, clear TAR

    // __enable_interrupt();

        

    while(1)

    {

      __bis_SR_register(LPM0_bits | GIE);

      _no_operation(); // don't put a breakpoint here right after entering LPM

      // new data has been arrived

      // process and display it

      // then loop back into LPM

    }

    }

        

    // Timer A0 interrupt service routine

    #pragma vector=TIMER1_A0_VECTOR

    __interrupt void TIMER1_A(void)

    {

       while(1){

          ADC12CTL0 |= ADC12SC; // Start conversion

          __bis_SR_register(LPM0_bits+GIE);

       }

    }

        

    // ADC12 interrupt service routine

    #pragma vector=ADC12_VECTOR

    __interrupt void ADC12_ISR(void){

    sum_data[0] = (unsigned int)ADC12MEM9; // Move sum_data[0], IFG is cleared p.7.4

    sum_data[1] = (unsigned int)ADC12MEM11; // Move sum_data[1], IFG is cleared p.7.5

    numtostring(sum_data[0],sum_data[1]);

    __bic_SR_register_on_exit(LPM0_bits);

    }

        

        

    void numtostring(long num, long num2){

    char str[12] = {' '};

    int tmp;

    int i = 0;

    int j, k;

        

    while(num!=0){

    for(j=i; j>0; j--){

    *(str+j) = *(str+j-1);

    }

        

    tmp = num%10;

    if(tmp==0)

    *str='0';

    else if(tmp==1)

    *str='1';

    else if(tmp==2)

    *str='2';

    else if(tmp==3)

    *str='3';

    else if(tmp==4)

    *str='4';

    else if(tmp==5)

    *str='5';

    else if(tmp==6)

    *str='6';

    else if(tmp==7)

    *str='7';

    else if(tmp==8)

    *str='8';

    else if(tmp==9)

    *str='9';

    num = num / 10;

    i++;

    }

    str[i++]='A';

    str[i++]='/';

        

    k=i;

    halLcdPrintLineCol("PPG", 3, 1, OVERWRITE_TEXT);

    halLcdPrintLineCol(str, 3, 7, OVERWRITE_TEXT);

        

    while(num2!=0){

    for(j=i; j>k; j--){

    *(str+j) = *(str+j-1);

    }

    tmp = num2%10;

    if(tmp==0)

    *(str+k)='0';

    else if(tmp==1)

    *(str+k)='1';

    else if(tmp==2)

    *(str+k)='2';

    else if(tmp==3)

    *(str+k)='3';

    else if(tmp==4)

    *(str+k)='4';

    else if(tmp==5)

    *(str+k)='5';

    else if(tmp==6)

    *(str+k)='6';

    else if(tmp==7)

    *(str+k)='7';

    else if(tmp==8)

    *(str+k)='8';

    else if(tmp==9)

    *(str+k)='9';

    num2 = num2 / 10;

    i++;

    }

    str[i++]='B';

    str[i++]='/';

        

    halLcdPrintLineCol("GSR", 5, 1, OVERWRITE_TEXT);

    halLcdPrintLineCol(str+k, 5, 7, OVERWRITE_TEXT);

    for(j=0;j<12; j++){

    while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?

    UCA0TXBUF = str[j];

    }

    }

     

     

  • Jens-Michael Gross said:

     

    First, you can trigger the ADC directly by a timer output (use the proper OUTMOD setting). Check your device datasheet to see which timer output can be used to directly trigger the ADC. You don't need an ISR then at all.

    Then, you enter LPM inside an ISR. That's a death sin. It means your ISR will not exit, but will be interrupted by another ISR then. Worst case, this will fill your stack so it overflows, overwriting your variables first, then flushing into the void, crashing the CPU.

    If you want the system to return to LPM where it was before, you can simply exit the ISR, This will restore the previous LPM state. If you want to change the LPM state after the ISR exits (!) Use the __bis_SR_register_on_exit() intrinsic (or __bic...) instead (there are multiple different aliases of this intrinsic, check your compiler documentation.

    1. I did first one. but I'm not sure am I right.

    2. I got lid of LPM inside an ISR. and use bic_sr_register_on_exit()

     

    Jens-Michael Gross said:

    Another thing: in sequence or repeated modes, ADC12SC needs to be cleared before it can be set again. The trigger is edge-sensitive. Only in single conversion mode, ADC12SC clears when the conversion is done.

    However, you're setting ADC12SHP and MSC bits. This means, once started, the ADC will run the sequence over and over again with fastest speed, until you clear the ENC bit. This is why your timer has no effect at all, and changes on CCR0 won't change anything. Clear the MSC bit if you want to trigger each single conversion of the sequence separately. Or use sequence mode (instead of repeated sequence mode). In this case, your timer ISR can trigger the conversion of a new sequence by clearing and then setting the ADC12SC bit.

    3. I change to sequence mode but I think I don't use it exatcly as you say. I think this is one of problem.

     

    Jens-Michael Gross said:

    __enable_interrupt();
       while(1);

    Why don't you enter LPM right here and leave it this way?

    while(1) __bis_SR_register(LPM0_bits | GIE);

    Junwoo Yoo said:
    __interrupt void ADC12_ISR(void){
       readADC();
    }

    That's a bad idea. Calling functions from inside an ISR should be avoided.
    The function call forces the compiler to assume that the parameter passing registers are clobbered during the function call (standard calling convention). So it has to save them on stack. Simply move the function content directly into the ISR instead of making it a function. Otherwise you're wasting time and space. And ISRs should be as fast as possible.

    Also, your function called inside the ISR does so some really time-consuming things, like LCD output. This is misplaced in an ISR execution. The ISR should simply collect the data and then wake main, telling it (implicitly because of the wake, or explicitly by a global flag) that new data needs to be processed and sent.

    ISRs are no threads. ISRs are fast-in, fast out handlers for events. They shouldn't do more than immediately necessary to handle the event. Everything else should be done in main.

    [/quote]

    4. I tried to avoid that calling functions froom inside an ISR. one problem is when I put numtostring() function to inside main while function, it doesn't work. I didn'd solve the problem.

    Jens-Michael Gross said:

    So teh ADC ISR copies the data from ADC12MEM, clearing the IFG bits, then calls __bic_SR_register_on_exit(LPM0_bits).
    In main, you do

    while(1) {
      __bis_SR_register(LPM0_bits | GIE);
      _no_operation(); // don't put a breakpoint here right after entering LPM
      // new data has been arrived
      // process and display it
      // then loop back into LPM
    }

     

    5. I think I did :)

     

    anyway thanks for your help

    But I still got low samples (~100)  and TA1CCR0 doesn't work.

    Could you give me a favour? thx indeeds :)

     

    ------------------------------------------------------------------

     

    I attach modified source code below here.

    ------------------------------------------------------------------

     

    void main(void) {

        

    WDTCTL = WDTPW + WDTHOLD; // Stop watch dog timer

        

    //Initialize LCD

    halLcdInit();

    halLcdBackLightInit();

    halLcdSetBackLight(0);

    halLcdSetContrast(100);

    halLcdClearScreen();

        

    __bis_SR_register(SCG0); // Disable the FLL control loop

    UCSCTL0 = 0x0000; // Set lowest possible DCOx, MODx

    UCSCTL1 = DCORSEL_7; // Select DCO range 16MHz operation

    UCSCTL2 = FLLD_1 + 249;           // Set DCO Multiplier for 8MHz

    // (N + 1) * FLLRef = Fdco

                                              // (249 + 1) * 32768 = 8MHz

                                              // Set FLL Div = fDCOCLK/2

    __bic_SR_register(SCG0);              // Enable the FLL control loop

        

    // Initialize LED

    P1DIR |= 0x01; // LED, Set P1.0 to output direction

        

    // Initialize ADC12_A

    P7SEL |= 0x50; // Enable A/D channel Port7.4, 7.6 (A12, A14)

    ADC12CTL0 = ADC12ON+ADC12MSC+ADC12SHT1_0; // Turn on ADC12, set sampling time , ADC12CLK Cycle 4

    ADC12CTL1 = ADC12SHP+ADC12CONSEQ_1+ADC12DIV_0; // Use sampling timer, Sequence-of-channels

    ADC12CTL2 = ADC12RES_2;

    ADC12IE = BIT9+BITB; // Enable interrupt for ADC12MEM9, ADC12MEM11

    ADC12MCTL9 = ADC12INCH_12; // ref+=AVcc, channel = A12, ADC_12A Memory Control 9

    ADC12MCTL11 = ADC12INCH_14; // ref+=AVcc, channel = A14, ADC_12A Memory Control 11

    ADC12CTL0 |= ADC12ENC; // Enable conversions

    //ADC12SHSx??

    // Initialize UART

    P3SEL = 0x30; // Port3.4, 3.5

    UCA0CTL1 |= UCSWRST; // **Put state machine in reset**

    UCA0CTL1 |= UCSSEL_2; // CLK = SMCLK 8Mhz

    UCA0BR0 = 4; // 8MHz/115200=69.44 (see User's Guide)

    UCA0BR1 = 0x00; // 32.3.13 Typical Baud Rates and Errors

    UCA0MCTL = UCBRS_5+UCBRF_3+UCOS16; // Modulation UCBRSx=5, UCBRFx=3, Oversamping ON

    UCA0CTL1 &= ~UCSWRST;

    UCA1IE |= UCRXIE;

        

    // Initialize Timer

    TA1CTL = TASSEL_2 + MC_1 + TACLR; // SMCLK 8MHz, Up mode, clear TAR

    TA1CCTL0 = CCIE + OUTMOD_2; // CCR0 interrupt enabled, Toggle/Reset Output mode

    TA1CCR0 = 3999; // 8MHz/4000ms

        

    // __enable_interrupt(); // Enable global Interrupts

        

    while(1)

    {

    ADC12CTL0 |= ADC12SC; // Start conversion

    __bis_SR_register(LPM0_bits|GIE);

    _no_operation(); // don't put a breakpoint here right after entering LPM

    // new data has been arrived

    // process and display it

    // then loop back into LPM

    }

    }

        

    //// Timer A0 interrupt service routine

    //#pragma vector=TIMER1_A0_VECTOR

    //__interrupt void TIMER1_A(void)

    //{

    //   while(1){

    //

    //      __bis_SR_register(LPM0_bits+GIE);

    //   }

    //}

        

    // ADC12 interrupt service routine

    #pragma vector=ADC12_VECTOR

    __interrupt void ADC12_ISR(void){

    sum_data[0] = ADC12MEM9; // Move sum_data[0], IFG is cleared p.7.4

    sum_data[1] = ADC12MEM11; // Move sum_data[1], IFG is cleared p.7.6

    numtostring(sum_data[0],sum_data[1]);

    __bic_SR_register_on_exit(LPM0_bits);

    }

        

    void numtostring(long num, long num2){

    char str[12] = {' '};

    int tmp;

    int i = 0;

    int j, k;

        

    while(num!=0){

    for(j=i; j>0; j--){

    *(str+j) = *(str+j-1);

    }

        

    tmp = num%10;

    if(tmp==0)

    *str='0';

    else if(tmp==1)

    *str='1';

    else if(tmp==2)

    *str='2';

    else if(tmp==3)

    *str='3';

    else if(tmp==4)

    *str='4';

    else if(tmp==5)

    *str='5';

    else if(tmp==6)

    *str='6';

    else if(tmp==7)

    *str='7';

    else if(tmp==8)

    *str='8';

    else if(tmp==9)

    *str='9';

    num = num / 10;

    i++;

    }

    str[i++]='A';

    str[i++]='/';

        

    k=i;

    halLcdPrintLineCol("PPG", 3, 1, OVERWRITE_TEXT);

    // halLcdPrintLineCol(str, 3, 7, OVERWRITE_TEXT);

        

    while(num2!=0){

    for(j=i; j>k; j--){

    *(str+j) = *(str+j-1);

    }

    tmp = num2%10;

    if(tmp==0)

    *(str+k)='0';

    else if(tmp==1)

    *(str+k)='1';

    else if(tmp==2)

    *(str+k)='2';

    else if(tmp==3)

    *(str+k)='3';

    else if(tmp==4)

    *(str+k)='4';

    else if(tmp==5)

    *(str+k)='5';

    else if(tmp==6)

    *(str+k)='6';

    else if(tmp==7)

    *(str+k)='7';

    else if(tmp==8)

    *(str+k)='8';

    else if(tmp==9)

    *(str+k)='9';

    num2 = num2 / 10;

    i++;

    }

    str[i++]='B';

    str[i++]='/';

        

    halLcdPrintLineCol("GSR", 5, 1, OVERWRITE_TEXT);

    // halLcdPrintLineCol(str+k, 5, 7, OVERWRITE_TEXT);

    for(j=0;j<12; j++){

    while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?

    UCA0TXBUF = str[j];

    }

    }

        

  • Junwoo Yoo said:
    1. I did first one. but I'm not sure am I right.

    From the tag in your first post, I got that you're using the 5438A. On this MSP, the signals TA0.0, TB0.0 and TB0.1 can be used for triggering the ADC.
    Only TB0.1 can be used to additionally control the sampling time, when not using the ADC12SHP bit.
    In your case, you use TA0.0, but you forgot to tell the ADC that you are doing it:

    ADC12CTL1 = ADC12SHP+ADC12CONSEQ_1+ADC12DIV_0;
    must read
    ADC12CTL1 = ADC12SHP|ADC12CONSEQ_1|ADC12DIV_0|ADC12SHS_1;

    (note the '|' for concatenating bits, '+' is for numerical values)

    The ADC12SC is unused then.

    Junwoo Yoo said:
    ADC12IE = BIT9+BITB; // Enable interrupt for ADC12MEM9, ADC12MEM11

    This will call the ISR twice. Once after ADC12MEM9 got a new value, and once more two conversions later.
    This is probably, why you have problems with doing numtostring in main: when you block interrupts while doing it inside the ISR, the second interrupt will be delayed until you're done. When you do this time-consuming task in main, it will be interrupted twice per sequence.
    Don't set BIT9, only get an interrupt at the last conversion of a sequence.

    I also noticed that you are doing the sequence wrong.

    Currently, your sequence begins with ADC12MCTL0 (you don't specify a different start) and never ends (none of the ADC12MCTL registers has the EOS bit set).

    Replace

    ADC12IE = BIT9+BITB; // Enable interrupt for ADC12MEM9, ADC12MEM11
    ADC12MCTL9 = ADC12INCH_12; // ref+=AVcc, channel = A12, ADC_12A Memory Control 9
    ADC12MCTL11 = ADC12INCH_14; // ref+=AVcc, channel = A14, ADC_12A Memory Control 11

    ADC12IE = BIT0;
    ADC12MCTL0 = ADC12INCH_12;
    ADC12MCTL1 = ADC12INCH_14|ADC12EOS;

    and read the results from ADC12MEM0+1 in the ISR.

    Also, are you sure the minimum sampling time (ADC12SHT1_0) is long enough for your signal source impedance? Better use a larger one. And since now ADC12MCTL0+1 are used, use ADC12SHT0_x.

    If you want to use other ADC12MCTL register than 0+1, then set ADC12CSTARTADD_x accordingly, so the sequence starts with the right one.

  • Thanks Jens-Michael Gross:)

    I tried to manage but, there are still some problem.

     

    Jens-Michael Gross said:

    1. I did first one. but I'm not sure am I right.

    From the tag in your first post, I got that you're using the 5438A. On this MSP, the signals TA0.0, TB0.0 and TB0.1 can be used for triggering the ADC.
    Only TB0.1 can be used to additionally control the sampling time, when not using the ADC12SHP bit.
    In your case, you use TA0.0, but you forgot to tell the ADC that you are doing it:

    [/quote]

    1. I don't understand which part is  TA0.0~TB0.1.. Could you explain me?

    Jens-Michael Gross said:

    ADC12CTL1 = ADC12SHP+ADC12CONSEQ_1+ADC12DIV_0;
    must read
    ADC12CTL1 = ADC12SHP|ADC12CONSEQ_1|ADC12DIV_0|ADC12SHS_1;

    (note the '|' for concatenating bits, '+' is for numerical values)

    The ADC12SC is unused then.

    2. I did. but one thing when I put ADC12SHS_1, it doesn't work.

    Jens-Michael Gross said:

    ADC12IE = BIT9+BITB; // Enable interrupt for ADC12MEM9, ADC12MEM11

    This will call the ISR twice. Once after ADC12MEM9 got a new value, and once more two conversions later.
    This is probably, why you have problems with doing numtostring in main: when you block interrupts while doing it inside the ISR, the second interrupt will be delayed until you're done. When you do this time-consuming task in main, it will be interrupted twice per sequence.
    Don't set BIT9, only get an interrupt at the last conversion of a sequence.

    I also noticed that you are doing the sequence wrong.

    Currently, your sequence begins with ADC12MCTL0 (you don't specify a different start) and never ends (none of the ADC12MCTL registers has the EOS bit set).

    Replace

    ADC12IE = BIT9+BITB; // Enable interrupt for ADC12MEM9, ADC12MEM11
    ADC12MCTL9 = ADC12INCH_12; // ref+=AVcc, channel = A12, ADC_12A Memory Control 9
    ADC12MCTL11 = ADC12INCH_14; // ref+=AVcc, channel = A14, ADC_12A Memory Control 11

    ADC12IE = BIT0;
    ADC12MCTL0 = ADC12INCH_12;
    ADC12MCTL1 = ADC12INCH_14|ADC12EOS;

    and read the results from ADC12MEM0+1 in the ISR.

    Also, are you sure the minimum sampling time (ADC12SHT1_0) is long enough for your signal source impedance? Better use a larger one. And since now ADC12MCTL0+1 are used, use ADC12SHT0_x.

    If you want to use other ADC12MCTL register than 0+1, then set ADC12CSTARTADD_x accordingly, so the sequence starts with the right one.

    [/quote]
     
    3. I fellow your intruction. one thing I wonder is ADC12SHT0_x. I supposed if I use larger one it makes sampling slower. Because it is cycle ex) CLK/CYCLE. Could you exaplain to me?
     
    4. I got very many sample(200~400) through turn off LCD and TA1CCR0 finally work if I set 0 value. if I set larger value it makes transmit very very slow. 
     
    5. I can't put num2string inside main yet. if I do it makes stuck..
     
     
    Could you give me a favour again? haha. thx indeed.
     
    ---------------------------------
           my new code
    ---------------------------------
     

    // Function

    long sum_data[2]={0,0};

    int cnt=0;

    int average=0;

    int adc_result_0=0;

    int average_buffer[400]={0};//임시버퍼

    int counter, heartrate, pulseperiod,beats;

    void num2string(long num, long num2);

    void int2binary(unsigned int int1, unsigned int int2);

    void send_uart(char  *data);

    void LCD_Init(void);

    void Clock_Init(void);

    void Timer_Init(void);

    void ADC_Init(void);

    void UART_Init(void);

        

        

    void main(void) {

        

    WDTCTL = WDTPW + WDTHOLD; // Stop watch dog timer

        

    // Initialize

    LCD_Init();

    Clock_Init();

    Timer_Init();

    ADC_Init();

    UART_Init();

        

    __enable_interrupt(); // Enable global Interrupts

        

    while(1)

    {

    ADC12CTL0 |= ADC12SC; // Start conversion

    __bis_SR_register(LPM0_bits|GIE);

    _no_operation(); // don't put a breakpoint here right after entering LPM

    // new data has been arrived

    // process and display it

    // then loop back into LPM

    }

    }

        

    // ADC12 interrupt service routine

    #pragma vector=ADC12_VECTOR

    __interrupt void ADC12_ISR(void)

    {

    sum_data[0] = ADC12MEM0; // Move sum_data[0], IFG is cleared p.7.4

    sum_data[1] = ADC12MEM1; // Move sum_data[1], IFG is cleared p.7.6

    num2string(sum_data[0],sum_data[1]);

    __bic_SR_register_on_exit(LPM0_bits);

    }

        

    void LCD_Init(void){

    halLcdInit();

    halLcdBackLightInit();

    halLcdSetBackLight(0);

    halLcdSetContrast(100);

    halLcdClearScreen();

    }

    void Clock_Init(void){

    __bis_SR_register(SCG0); // Disable the FLL control loop

    UCSCTL0 = 0x0000; // Set lowest possible DCOx, MODx

    UCSCTL1 = DCORSEL_7; // Select DCO range 16MHz operation

    UCSCTL2 = FLLD_1 + 249;       // Set DCO Multiplier for 8MHz

    // (N + 1) * FLLRef = Fdco

    // (249 + 1) * 32768 = 8MHz

    // Set FLL Div = fDCOCLK/2

    __bic_SR_register(SCG0);           // Enable the FLL control loop

    }

    void Timer_Init(void){

    TA1CTL = TASSEL_2 + MC_1 + TACLR; // SMCLK 8MHz, Up mode, clear TAR

    TA1CCTL0 = CCIE + OUTMOD_2; // CCR0 interrupt enabled, Toggle/Reset Output mode

    TA1CCR0 = 0; // 8MHz/4000ms

    }

    void ADC_Init(void){

    P7SEL |= 0x50; // Enable A/D channel Port7.4, 7.6 (A12, A14)

    ADC12CTL0 = ADC12ON+ADC12MSC+ADC12SHT0_6; // Turn on ADC12, set sampling time , ADC12CLK Cycle 128

    ADC12CTL1 = ADC12SHP|ADC12CONSEQ_1|ADC12DIV_0|ADC12SHS_0; // Use sampling timer, Sequence-of-channels

    ADC12CTL2 = ADC12RES_2;

    ADC12IE = BIT0; // Enable interrupt for ADC12MEM0

    ADC12MCTL0 = ADC12INCH_12; // ref+=AVcc, channel = A12, ADC_12A Memory Control 9

    ADC12MCTL1 = ADC12INCH_14|ADC12EOS; // ref+=AVcc, channel = A14, ADC_12A Memory Control 11

    ADC12CTL0 |= ADC12ENC; // Enable conversions

    }

    void UART_Init(void){

    P3SEL = 0x30; // Port3.4, 3.5

    UCA0CTL1 |= UCSWRST; // **Put state machine in reset**

    UCA0CTL1 |= UCSSEL_2; // CLK = SMCLK 8Mhz

    UCA0BR0 = 4; // 8MHz/115200=69.44 (see User's Guide)

    UCA0BR1 = 0x00; // 32.3.13 Typical Baud Rates and Errors

    UCA0MCTL = UCBRS_5+UCBRF_3+UCOS16; // Modulation UCBRSx=5, UCBRFx=3, Oversamping ON

    UCA0CTL1 &= ~UCSWRST;

    UCA1IE |= UCRXIE;

    }

        

    void num2string(long num, long num2){

    char str[12] = {' '};

    int tmp;

    int i = 0;

    int j, k;

        

    while(num!=0){

    for(j=i; j>0; j--){

    *(str+j) = *(str+j-1);

    }

        

    tmp = num%10;

    if(tmp==0)

    *str='0';

    else if(tmp==1)

    *str='1';

    else if(tmp==2)

    *str='2';

    else if(tmp==3)

    *str='3';

    else if(tmp==4)

    *str='4';

    else if(tmp==5)

    *str='5';

    else if(tmp==6)

    *str='6';

    else if(tmp==7)

    *str='7';

    else if(tmp==8)

    *str='8';

    else if(tmp==9)

    *str='9';

    num = num / 10;

    i++;

    }

    str[i++]='A';

    str[i++]='/';

        

    k=i;

    halLcdPrintLineCol("PP", 3, 1, OVERWRITE_TEXT);

    // halLcdPrintLineCol(str, 3, 7, OVERWRITE_TEXT);

        

    while(num2!=0){

    for(j=i; j>k; j--){

    *(str+j) = *(str+j-1);

    }

    tmp = num2%10;

    if(tmp==0)

    *(str+k)='0';

    else if(tmp==1)

    *(str+k)='1';

    else if(tmp==2)

    *(str+k)='2';

    else if(tmp==3)

    *(str+k)='3';

    else if(tmp==4)

    *(str+k)='4';

    else if(tmp==5)

    *(str+k)='5';

    else if(tmp==6)

    *(str+k)='6';

    else if(tmp==7)

    *(str+k)='7';

    else if(tmp==8)

    *(str+k)='8';

    else if(tmp==9)

    *(str+k)='9';

    num2 = num2 / 10;

    i++;

    }

    str[i++]='B';

    str[i++]='/';

        

    // halLcdPrintLineCol("GSR", 5, 1, OVERWRITE_TEXT);

    // halLcdPrintLineCol(str+k, 5, 7, OVERWRITE_TEXT);

    for(j=0;j<12; j++){

    while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?

    UCA0TXBUF = str[j];

    }

    }

        

     

        

  • Junwoo Yoo said:
    1. I don't understand which part is  TA0.0~TB0.1.. Could you explain me?

    These are signal names. TA0.0 is the Timer A0 CCR0 output signal, TB0.1 is the Timer B(0) CCR1 output signal. These signals can be output on specific port pins, but are also also available internally. (If you see TA1, this may refer to Timer A1 module or to Timer A0 CCR1 signal, as on older MSPs there was only one TimerA).

    A similar notation is commonly used for port pins, like P4.7 is Port4 pin 7. Also, it is used to refer to individuals bits of a register, like P4OUT.5 for bit 5 of P4OUT register. Sometimes also used with the bit names: ADC12CTL0.ENC

    Junwoo Yoo said:
    2. I did. but one thing when I put ADC12SHS_1, it doesn't work.

    Then there's something else still wrong. This is required to accept the timer output as trigger for the ADC instead of the ADC12SC bit.

    Junwoo Yoo said:
    3. I fellow your intruction. one thing I wonder is ADC12SHT0_x. I supposed if I use larger one it makes sampling slower. Because it is cycle ex) CLK/CYCLE. Could you exaplain to me?

    That's right. However, sampling time is important.

    The ADC12 has an internal capacitor array that is charged during the sample period and (slightly, the faster the conversion, the less it is discharged, the better the result) discharged during the conversion.
    This capacitor (See datasheet for your MSPs value Ci) together with the input switch impedance of 1kOhm and the output impedance of your signal source forms a low pass. Depending on the maximum frequency you want to sample and the worst case minimum precision you want to achieve, you need to give this capacitor some time to charge (it is a nonlinear charge function). R*C gives the time constant Tau. 1Tau gives you 70% charge level. But 70% is only 2 bit precision. For 12 bit resolution (1/2 LSB precision), you want the capacitor charged to 1-1/8192 of the input voltage. (99.95%). It is R*C*ln(2^13) or 9Tau. How long Tau is, depends on your signal source (output Impedance). And how many clock cycles it is, depends on your ADC12 clock speed. Only you can know.

    Assuming 6MHz clock, 4 clock cycles are just 666ns, but with 10pF/1k (assuming a zero-impedance signal source), this is 66Tau, but if your signal source has 10k output impedance, this is only 6 Tau and not enough to charge the capacitor. See users guide and device datasheet for the exact capacitance and resistor values.

    Junwoo Yoo said:
    4. I got very many sample(200~400) through turn off LCD and TA1CCR0 finally work if I set 0 value. if I set larger value it makes transmit very very slow. 

    LCD output is sloooow. How fast do you need the samples? And does it make any sense to update the LCD that often?
    What do you mean with 'if I set 0 value'?

    Junwoo Yoo said:
    5. I can't put num2string inside main yet. if I do it makes stuck..

    Shouldn't. So there's another problem. Maybe with the timing. What if you output only every 100th value to LCD?

    UCSCTL2 = FLLD_1 + 249;       // Set DCO Multiplier for 8MHz
    On 8 MHz, CPU speed, it could be a good idea to raise the core voltage to PMMCOREV_1, as FLL-adjusted DCO speed is an average of a higher and lower frequency. So you partly operate the CPU above 8MHz and the default core voltage is only specified for up to 8MHz. However, this is likely within the accepted tolerances.

    TA1CCR0 = 0; // 8MHz/4000ms
    Bad idea. This makes the timer count from 0 to 0, effectively stopping it (no interrupts generated. And here is the problem you're have:

    TA1CCTL0 = CCIE + OUTMOD_2; // CCR0 interrupt enabled, Toggle/Reset Output mode
    You enable the timer CCR0 interrupt (CCIE). This causes the TIMER1_A0_VECTOR to be called when an interrupt occurs. But you don't have an ISR (and you don't need one). This will crash the MSP.

    Don't set CCIE, and set TA1CCR0 to a value that fits the frequency at which you want your sequence to be sampled.
    E.g. 8000 for one sequence run per ms. Or 40000 for 200 sequences per second. (set an additional divider for the timer clock if you want less).

  • Hello Jens-Michael Gross:)

    Thanks for your kind.

    I tried to follow ur instruction and I couldn't do 4 things now.

     

    Jens-Michael Gross said:

    2. I did. but one thing when I put ADC12SHS_1, it doesn't work.

    Then there's something else still wrong. This is required to accept the timer output as trigger for the ADC instead of the ADC12SC bit.

    [/quote]

    1. I still have no idea. you mean when I put ADC12SHS_1, I have to get rid of ADC12SC bit from while in main (ref ADC12CTL0 |= ADC12SC;)? which can I put and where?

     

    Jens-Michael Gross said:

    3. I fellow your intruction. one thing I wonder is ADC12SHT0_x. I supposed if I use larger one it makes sampling slower. Because it is cycle ex) CLK/CYCLE. Could you exaplain to me?

    That's right. However, sampling time is important.

    The ADC12 has an internal capacitor array that is charged during the sample period and (slightly, the faster the conversion, the less it is discharged, the better the result) discharged during the conversion.
    This capacitor (See datasheet for your MSPs value Ci) together with the input switch impedance of 1kOhm and the output impedance of your signal source forms a low pass. Depending on the maximum frequency you want to sample and the worst case minimum precision you want to achieve, you need to give this capacitor some time to charge (it is a nonlinear charge function). R*C gives the time constant Tau. 1Tau gives you 70% charge level. But 70% is only 2 bit precision. For 12 bit resolution (1/2 LSB precision), you want the capacitor charged to 1-1/8192 of the input voltage. (99.95%). It is R*C*ln(2^13) or 9Tau. How long Tau is, depends on your signal source (output Impedance). And how many clock cycles it is, depends on your ADC12 clock speed. Only you can know.

    Assuming 6MHz clock, 4 clock cycles are just 666ns, but with 10pF/1k (assuming a zero-impedance signal source), this is 66Tau, but if your signal source has 10k output impedance, this is only 6 Tau and not enough to charge the capacitor. See users guide and device datasheet for the exact capacitance and resistor values.

    [/quote]
    2. unfortunately it is very hard part for me even your good explain.:( anyway I tried to understand and study. Could you give me a hint like.. which part of datasheet have to read & which I have to know(like out impedance?) thx..

     

    Jens-Michael Gross said:

    4. I got very many sample(200~400) through turn off LCD and TA1CCR0 finally work if I set 0 value. if I set larger value it makes transmit very very slow. 

    LCD output is sloooow. How fast do you need the samples? And does it make any sense to update the LCD that often?
    What do you mean with 'if I set 0 value'?

    What if you output only every 100th value to LCD?

    [/quote]
    3. I understand your explain about LCD part. And I use TB0.1 which you recommend me. Now it doesn't makes any problem just doesn't work with any value. I need 300~400sample:).
     
     

    Jens-Michael Gross said:
    UCSCTL2 = FLLD_1 + 249;       // Set DCO Multiplier for 8MHz
    On 8 MHz, CPU speed, it could be a good idea to raise the core voltage to PMMCOREV_1, as FLL-adjusted DCO speed is an average of a higher and lower frequency. So you partly operate the CPU above 8MHz and the default core voltage is only specified for up to 8MHz. However, this is likely within the accepted tolerances.

    4. Do I have to use PMMCOREV_1, If I have to.. Could you let me know where can I put?. I tried to put SetVCore(PMMCOREV_1); inside main. it makes a problem.

      

    Thanks a lot.

    Looking forward to your comment:)

  • I forgot my code.

     

    thx.

     

    void main(void) {

     

             WDTCTL = WDTPW + WDTHOLD; // Stop watch dog timer

     

             // Initialize

             LCD_Init();

             Clock_Init();

             Timer_Init();

             ADC_Init();

             UART_Init();

     

             __enable_interrupt();                            // Enable global Interrupts

     

             while(1)

             {

                      ADC12CTL0 |= ADC12SC;             // Start conversion

                      __bis_SR_register(LPM0_bits|GIE);

                      num2string(sum_data[0],sum_data[1]);

                      _no_operation();                                          // don't put a breakpoint here right after entering LPM

                      // new data has been arrived

                      // process and display it

                      // then loop back into LPM

             }

    }

     

    // ADC12 interrupt service routine

    #pragma vector=ADC12_VECTOR

    __interrupt void ADC12_ISR(void)

    {

             sum_data[0] = ADC12MEM0;                  // Move sum_data[0], IFG is cleared p.7.4

             sum_data[1] = ADC12MEM1;                  // Move sum_data[1], IFG is cleared p.7.6

             __bic_SR_register_on_exit(LPM0_bits);

    }

     

    void LCD_Init(void){

             halLcdInit();

             halLcdBackLightInit();

             halLcdSetBackLight(0);

             halLcdSetContrast(100);

             halLcdClearScreen();

    }

    void Clock_Init(void){

             __bis_SR_register(SCG0);                                // Disable the FLL control loop

                      UCSCTL0 = 0x0000;                             // Set lowest possible DCOx, MODx

                      UCSCTL1 = DCORSEL_7;                       // Select DCO range 16MHz operation

                      UCSCTL2 = FLLD_1 + 249;         // Set DCO Multiplier for 8MHz

                                                                                                              // (N + 1) * FLLRef = Fdco

                                                                                                              // (249 + 1) * 32768 = 8MHz

                                                                                                              // Set FLL Div = fDCOCLK/2

             __bic_SR_register(SCG0);                // Enable the FLL control loop

    }

    void Timer_Init(void){

    //       TA1CTL = TASSEL_2 + MC_1 + TACLR;     // SMCLK 8MHz, Up mode, clear TAR

    //       TA1CCTL0 = CCIE + OUTMOD_2;                     // CCR0 interrupt enabled, Toggle/Reset Output mode

    //       TA1CCR0 = 0;         // 8MHz/4000ms

             TB0CTL = TBSSEL_2 + MC_1 + TBCLR;               // SMCLK 8MHz, Up mode, clear TAR

             TB0CCTL1 = OUTMOD_2;                                       // CCR0 interrupt enabled, Toggle/Reset Output mode

             TB0CCR1 =  26665;                                                       // 8MHz/26666 = 300hz (ex. 8000 for one sequence run per ms)

    }

    void ADC_Init(void){

             P7SEL |= 0x50;                 // Enable A/D channel Port7.4, 7.6 (A12, A14)

             ADC12CTL0 = ADC12ON+ADC12MSC+ADC12SHT0_12;           // Turn on ADC12, set sampling time , ADC12CLK Cycle 1024

             ADC12CTL1 = ADC12SHP|ADC12CONSEQ_1|ADC12DIV_0;         // Use sampling timer, Sequence-of-channels

             ADC12CTL2 = ADC12RES_2;                                             // 8,000,000Hz(12+1Resolution+1024+1Cycle) = 7707

             ADC12IE = BIT0;                                                                             // Enable interrupt for ADC12MEM0

             ADC12MCTL0 = ADC12INCH_12;                              // ref+=AVcc, channel = A12, ADC_12A Memory Control 9

             ADC12MCTL1 = ADC12INCH_14|ADC12EOS;       // ref+=AVcc, channel = A14, ADC_12A Memory Control 11

             ADC12CTL0 |= ADC12ENC;                                              // Enable conversions

    }

    void UART_Init(void){

             P3SEL = 0x30;                                   // Port3.4, 3.5

             UCA0CTL1 |= UCSWRST;     // **Put state machine in reset**

             UCA0CTL1 |= UCSSEL_2;     // CLK = SMCLK 8Mhz

             UCA0BR0 = 4;                           // 8MHz/115200=69.44 (see User's Guide)

             UCA0BR1 = 0x00;                      // 32.3.13 Typical Baud Rates and Errors

             UCA0MCTL = UCBRS_5+UCBRF_3+UCOS16;        // Modulation UCBRSx=5, UCBRFx=3, Oversamping ON

             UCA0CTL1 &= ~UCSWRST;

             UCA1IE |= UCRXIE;

    }

     

    void num2string(long num, long num2){

             char str[12] = {' '};

             int tmp;

             int i = 0;

             int j, k;

     

             while(num!=0){

                      for(j=i; j>0; j--){

                               *(str+j) = *(str+j-1);

                      }

     

                      tmp = num%10;

                      if(tmp==0)

                               *str='0';

                      else if(tmp==1)

                               *str='1';

                      else if(tmp==2)

                               *str='2';

                      else if(tmp==3)

                               *str='3';

                      else if(tmp==4)

                               *str='4';

                      else if(tmp==5)

                               *str='5';

                      else if(tmp==6)

                               *str='6';

                      else if(tmp==7)

                               *str='7';

                      else if(tmp==8)

                               *str='8';

                      else if(tmp==9)

                               *str='9';

                      num = num / 10;

                      i++;

             }

                      str[i++]='A';

                      str[i++]='/';

     

             k=i;

             halLcdPrintLineCol("PP", 3, 1, OVERWRITE_TEXT);

    //       halLcdPrintLineCol(str, 3, 7, OVERWRITE_TEXT);

     

             while(num2!=0){

                      for(j=i; j>k; j--){

                               *(str+j) = *(str+j-1);

                      }

                      tmp = num2%10;

                      if(tmp==0)

                               *(str+k)='0';

                      else if(tmp==1)

                               *(str+k)='1';

                      else if(tmp==2)

                               *(str+k)='2';

                      else if(tmp==3)

                               *(str+k)='3';

                      else if(tmp==4)

                               *(str+k)='4';

                      else if(tmp==5)

                               *(str+k)='5';

                      else if(tmp==6)

                               *(str+k)='6';

                      else if(tmp==7)

                               *(str+k)='7';

                      else if(tmp==8)

                               *(str+k)='8';

                      else if(tmp==9)

                               *(str+k)='9';

                      num2 = num2 / 10;

                      i++;

             }

             str[i++]='B';

             str[i++]='/';

     

    //       halLcdPrintLineCol("GSR", 5, 1, OVERWRITE_TEXT);

    //       halLcdPrintLineCol(str+k, 5, 7, OVERWRITE_TEXT);

             for(j=0;j<12; j++){

                      while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?

                               UCA0TXBUF = str[j];

             }

    }

     

    }

     

  • Junwoo Yoo said:
    tried to put SetVCore(PMMCOREV_1); inside main. it makes a problem.

    PMMCOREV_1 is a bitfield value for use with the PMM register. For the API function, I think a simple '1' is to be used.

    Junwoo Yoo said:
    you mean when I put ADC12SHS_1, I have to get rid of ADC12SC bit from while in main

    Yes. The ADC12SC bit is a don't care if you switch the conversion trigger over to the timer. However, for TB.1, I think the required setting then is ADC12SHS_3.
    You should take a look at the ADC12 diagram in the users guide (right at the start of the chapter). You there can see how the register settings and the different signals control the conversion flow.

    Junwoo Yoo said:
    which part of datasheet have to read & which I have to know(like out impedance?)

    The datasheet of your signal source (sensor or whatever) should tell you its output impedance.
    An ideal output impedance of zero would mean that the output voltage remains constant, no matter how low the input impedance is. So if you short the sensor output, it will still provide the output voltage (and an unlimited current). Which is of course impossible. On the other hand, assuming a 1MOhms output impedance, connecting a 1MOhm input impedance to it would result in 50% output voltage only, a 100kOhms input impedance would result in only 9% voltage on the input (voltage divider). So it is important to know the output and input impedance, in order to know how much the input affects the output.

    in other words: a high output impedance only provides a low current to charge the sampling capacitor. To have it fully charged, more time is required. Example: on 1V signal and 1MOhm output impedance, the maximum current is 1µA. Or 1µC/s. To charge a 10pF capacitor to 1V, it would take 10µs. But the more the capacitor charges, the smaller is the voltage difference and the smaller is the charging current. Charging effectively never ends, but after ~90µs, the capacitor is charged to 99.95%

    Even with 1ms S&H time and two channels, you'll get 2*500 samples/s

    You current main loop (except for the superfluous ADC12SC), the ADC will do one sequence every 3.3ms. However, main will only update the LCD less often, as an LCD update likely takes much longer than 3.33ms. Int his case, main won't notice the conversions and will just complete the LCD update and then wait for the next conversion (while several ones have slipped).

    Suggestion: in the ISR you update a global counter (declare it volatile !), along with reading the conversion results. And you also pass this counter to num2string and the LCD. You'll see that the LCD updates will show skipped counter values, indicating samples that have been taken but not sent to the LCD due to the slow LCD update.

    Alternatively, you can toggle a port pin inside the ISR. So you can see with a scope how often a new sample arrived, even though the LCD doesn't show them all.

  • Thanks for helping Jens-Michael Gross.

    I still got some problem..sadly..

    Jens-Michael Gross said:

    tried to put SetVCore(PMMCOREV_1); inside main. it makes a problem.

    PMMCOREV_1 is a bitfield value for use with the PMM register. For the API function, I think a simple '1' is to be used.

    Junwoo Yoo said:
    you mean when I put ADC12SHS_1, I have to get rid of ADC12SC bit from while in main

    Yes. The ADC12SC bit is a don't care if you switch the conversion trigger over to the timer. However, for TB.1, I think the required setting then is ADC12SHS_3.
    You should take a look at the ADC12 diagram in the users guide (right at the start of the chapter). You there can see how the register settings and the different signals control the conversion flow.

    [/quote]

    I tried to solve the problem for a long time. but, I couldn't..

    anyway I have to use ADC12SHS_3. I got it. but when I put this and get rid of ADC12SC bit in while. MSP doesn't work. Am I wrong setting register..?

    P.s Could you let me know when I need to use reference voltage in ADC(ADC12REFON)?

    ====my code=====

    #include <hal_lcd.h>
    #include <hal_lcd.c>
    #include <hal_lcd_fonts.h>
    #include <hal_lcd_fonts.c>
    #include <msp430.h>
    #include <msp430x54xA.h>
    #include <math.h>
    #include <string.h>

    // Function
    long sum_data[2]={0,0};
    int cnt=1;
    int average=0;
    int adc_result_0=0;
    int average_buffer[400]={0};//임시버퍼
    int counter, heartrate, pulseperiod,beats;
    void num2string(long num, long num2);
    void int2binary(unsigned int ppg_val, unsigned int gsr_val);
    void LCD_Init(void);
    void Clock_Init(void);
    void Timer_Init(void);
    void ADC_Init(void);
    void UART_Init(void);


    void main(void) {

      WDTCTL = WDTPW + WDTHOLD; // Stop watch dog timer
    //  PMMCTL0 = PMMCOREV_1;   // Core voltage level 1
      // Initialize
      LCD_Init();
      Clock_Init();
      ADC_Init();
      Timer_Init();
      UART_Init();

    // __enable_interrupt();    // Enable global Interrupts

     while(1)
     {
      ADC12CTL0 |= ADC12SC;  // Start conversion 이제 필요없음 타이머가 adc트리거 기능함 ADC12SHS_3
      __bis_SR_register(LPM0_bits|GIE);
    //  num2string(sum_data[0],sum_data[1]);
      int2binary(sum_data[0],sum_data[1]);
      _no_operation();     // don't put a breakpoint here right after entering LPM
               // new data has been arrived
               // process and display it
               // then loop back into LPM
     }
    }

    // ADC12 interrupt service routine
    #pragma vector=ADC12_VECTOR
    __interrupt void ADC12_ISR(void)
    {
     // start conversion and store result without CPU intervention
     // unsigned int conversion
     // 표현범위 일단은. +2.5v -2.0v로 예측.
     sum_data[0] = (int)((long)ADC12MEM0 * 4500 / 4095) - 2000 + 2000; // Move sum_data[0], IFG is cleared p.7.4
     sum_data[1] = (int)((long)ADC12MEM1 * 4500 / 4095);      // Move sum_data[1], IFG is cleared p.7.6
     __bic_SR_register_on_exit(LPM0_bits);
    }

    void LCD_Init(void){
     halLcdInit();
     halLcdBackLightInit();
     halLcdSetBackLight(0);
     halLcdSetContrast(100);
     halLcdClearScreen();
    }
    void Clock_Init(void){
     __bis_SR_register(SCG0);   // Disable the FLL control loop
      UCSCTL0 = 0x0000;   // Set lowest possible DCOx, MODx
      UCSCTL1 = DCORSEL_7;  // Select DCO range 16MHz operation
      UCSCTL2 = FLLD_1 + 249;    // Set DCO Multiplier for 8MHz
               // (N + 1) * FLLRef = Fdco
               // (249 + 1) * 32768 = 8MHz
               // Set FLL Div = fDCOCLK/2
     __bic_SR_register(SCG0);        // Enable the FLL control loop
    }
    void ADC_Init(void){
     P7SEL |= 0x50;  // Enable A/D channel Port7.4, 7.6 (A12, A14)
     ADC12CTL0 = ADC12ON | ADC12REFON | ADC12REF2_5V | ADC12SHT0_12; // Turn on ADC12, ADC12CLK Cycle 1024
     ADC12CTL0 |= ADC12MSC;      // multiple sample and conversion
     ADC12CTL1 = ADC12SHP | ADC12CONSEQ_1 | ADC12DIV_7 | ADC12SSEL_3 ; // Use sampling timer, Sequence-of-channels
    // ADC12CTL1 |= ADC12SHS_3;
     ADC12CTL2 = ADC12RES_2;      // 8,000,000Hz/(12+1Resolution+10248)*8 = 964
     ADC12IE = BIT0;         // Enable interrupt for ADC12MEM0
     ADC12MCTL0 = ADC12SREF_0 | ADC12INCH_12;    // Vref+=AVcc, channel = A12, ADC_12A Memory Control 9
     ADC12MCTL1 = ADC12SREF_0 | ADC12INCH_14|ADC12EOS; // Vref+=AVcc, channel = A14, ADC_12A Memory Control 11
     ADC12CTL0 |= ADC12ENC;       // Enable conversions
    }
    void Timer_Init(void){
     TB0CTL = TBSSEL_2 | MC_1 | TBCLR;  // SMCLK 8MHz, Up mode, clear TAR
     TB0CCTL1 = OUTMOD_2;      // CCR0 interrupt enabled, Toggle/Reset Output mode
     TB0CCR1 =  26666;       // 8MHz/26666 = 300hz (ex. 8000 for one sequence run per ms)
    }
    void UART_Init(void){
     P3SEL = 0x30;     // Port3.4, 3.5
     UCA0CTL1 |= UCSWRST;  // **Put state machine in reset**
     UCA0CTL1 |= UCSSEL_2;  // CLK = SMCLK 8Mhz
     UCA0BR0 = 4;    // 8MHz/115200=69.44 (see User's Guide)
     UCA0BR1 = 0x00;    // 32.3.13 Typical Baud Rates and Errors
     UCA0MCTL = UCBRS_5+UCBRF_3+UCOS16; // Modulation UCBRSx=5, UCBRFx=3, Over Sampling ON
     UCA0CTL1 &= ~UCSWRST;
     UCA1IE |= UCRXIE;
    }

    void num2string(long num, long num2){
     char str[12] = {' '};
     int tmp;
     int i = 0;
     int j, k;

     while(num!=0){
      for(j=i; j>0; j--){
       *(str+j) = *(str+j-1);
      }

      tmp = num%10;
      if(tmp==0)
       *str='0';
      else if(tmp==1)
       *str='1';
      else if(tmp==2)
       *str='2';
      else if(tmp==3)
       *str='3';
      else if(tmp==4)
       *str='4';
      else if(tmp==5)
       *str='5';
      else if(tmp==6)
       *str='6';
      else if(tmp==7)
       *str='7';
      else if(tmp==8)
       *str='8';
      else if(tmp==9)
       *str='9';
      num = num / 10;
      i++;
     }
     str[i++]='A';
     str[i++]='/';
     k=i;

     halLcdPrintLineCol("PPG", 3, 1, OVERWRITE_TEXT);
     halLcdPrintLineCol(str, 3, 7, OVERWRITE_TEXT);

     while(num2!=0){
      for(j=i; j>k; j--){
       *(str+j) = *(str+j-1);
      }
      tmp = num2%10;
      if(tmp==0)
       *(str+k)='0';
      else if(tmp==1)
       *(str+k)='1';
      else if(tmp==2)
       *(str+k)='2';
      else if(tmp==3)
       *(str+k)='3';
      else if(tmp==4)
       *(str+k)='4';
      else if(tmp==5)
       *(str+k)='5';
      else if(tmp==6)
       *(str+k)='6';
      else if(tmp==7)
       *(str+k)='7';
      else if(tmp==8)
       *(str+k)='8';
      else if(tmp==9)
       *(str+k)='9';
      num2 = num2 / 10;
      i++;
     }
     str[i++]='B';
     str[i++]='/';

     halLcdPrintLineCol("GSR", 5, 1, OVERWRITE_TEXT);
     halLcdPrintLineCol(str+k, 5, 7, OVERWRITE_TEXT);

     for(j=0;j<12; j++){
      while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?
       UCA0TXBUF = str[j];
     }
     cnt++;
    }

    void int2binary(unsigned int ppg_val,unsigned int gsr_val){
     char str[4] = {' '};
     char ppg_binary_s, ppg_binary_e, gsr_binary_s, gsr_binary_e;
     int temp_ppg, temp_gsr, j;

     temp_ppg = ppg_val;
     temp_gsr= gsr_val;
     ppg_binary_s = (char)((temp_ppg) >> 7);
     ppg_binary_e = (char)(temp_ppg)&0x7F; // 0111 1111
     gsr_binary_s = (char)((temp_gsr) >> 7);
     gsr_binary_e = (char)(temp_gsr)&0x7F;
     str[0] = ppg_binary_s|0xA0;     // 1010 0000
      str[1] = ppg_binary_e;
      str[2] = gsr_binary_s|0xC0;     // 1100 0000
      str[3] = gsr_binary_e;

     halLcdPrintLineCol("YJW", 3, 1, OVERWRITE_TEXT);
     for(j=0;j<4; j++){
      while (!(UCA0IFG&UCTXIFG));  // USCI_A0 TX buffer ready?
       UCA0TXBUF = str[j];   // Byte 단위로 전송
     }
    }

  • Hmm..

    I'm sorry being late answer. I took a final term.

    so .. Could you give me a faver again?
     I'm looking forward to your brilliant anwser.

    thanks a lot.

  • Junwoo Yoo said:
    MSP doesn't work. Am I wrong setting register..?

    As I just wrote in your other thread, the timer isn't running. Or actually, it is running, but not counting, as you instructed it to count from 0 to 0.

    The slow conversion rate is the slow speed of your LCD printout. The ADC is taking the samples at the desired rate, but when your main loop with the LCD is done formatting and sending one result, a couple others have already been taken - and discarded.

    Your LCD write is so slow that you simply cannot run num2string more than 60 times per second. Even though the ADC is doing 300 conversions per second. Only every 5th can get printed. if you remove the calls to halLcdPrintLineCol and just send data to the UART, you'll get the desired 300Hz.

**Attention** This is a public forum