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.

UART RX Initialization problem

Other Parts Discussed in Thread: MSP430F4152, MSP430BT5190

Hello, All. Im having some problem about UART RX in MSP430F4152.

Briefly, as soon as it is turned on, It can not receive datas from other device by RS-485 within 120ms.

It is supposed to receive 4 characters, but within 120ms it only can receive just 1 character. Also I can see RS-485 Drive IC can get the datas all by an oscilloscope and it jumps in ISR only one time.  

If I send the data to MSP430F4152 with 100ms delaying, so that totally 220ms after, it works! it can receive the data, 4 characters all.

I checked that SPI and UART TX works without ISR after turning on within 30ms, However why only RX has problem like this? Does UART RX take some time to be ready?

 

//ACLK = LFXT1 = 32768Hz, MCLK = SMCLK = DCO = (121+1) x 32768 x 2 = 7.99 Mhz
void Init_Clock(void)
{
   
    WDTCTL = WDTPW + WDTHOLD;           // Stop watchdog timer to prevent time out reset
   
    SCFI0 |= FN_4;                      // x2 DCO freq, 8Mhz nominal DCO
    SCFQCTL = 121;                      // (121+1) x 32768 x 2 = 7.99 Mhz   
    FLL_CTL0 |= DCOPLUS + XCAP18PF;     // DCO+ set so freq= xtal x D x N+1    

}

void Init_Uart(void)
{
  UCA0CTL1 |= UCSSEL_2;    // CLK = MCLK
  UCA0BR0 = 0xA0;         // 8M / 19200
  UCA0BR1 = 0x01;      
  UCA0MCTL = UCBRS_6;             
 
  UCA0CTL1 &= ~UCSWRST;    // Initialize USCI state machine
  IE2 |= UCA0RXIE;              // Enable USCI_A0 RX interrupt

 
  // SPI MODE
  UCB0CTL1 |= UCSSEL_2 + UCSWRST;              // **Put state machine in reset**
  UCB0CTL0 |= UCSYNC+UCCKPH+UCMSB+UCMST;      // 3-pin, 8-bit SPI master
  UCB0BR0 = 0x01;                             // /4
  UCB0BR1 = 0x00;
  UCB0CTL1 &= ~UCSWRST;                       // **Initialize USCI state machine**
                    
}

// Copy the data received to r_buf
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCIA0RX_ISR(void)
{
  uReceiver(&r_buf);
}

  • No, the UART RX chain doesn't need any delay.  It works immediately after clearing UCSWRST.

    However, the receive clock can cause you trouble.  Be sure the receive clock is running reliably and continuously from before you clear UCSWRST and all through your usage of the UART.

    Also the UART must synchronize to the bus after you clear UCSWRST.  You can miss the character underway (if any) and sometimes even the first full character.

    Jeff


  • Hello, Jeff.

    Thanks for replying. But still I have a question.

    I checked that UART Tx works very well even as soon as it turns on, which means that it seems UART clock is running stably.

    Is there any difference between UART Tx and Rx clock?

     

    Thanks,

  • Hi Scott,

    No, there's no difference between the Tx and Rx clocks.  The UART just uses the one clock.

    I don't think you can assume all is well with the UART clock just because the Tx is working.  Tx requires only clock; Rx requires clock and a transmitting remote end at the same time.

    It appears that you are using MCLK to clock the UART.  Do you ever let MCLK stop (LPMx)?  Can you turn on MCLK at its pin and watch for discontinuous operation?

    If your MCLK is continuous, then I'm guessing you have a software problem in your receive logic.  I believe you said the ISR fires only once when you expected more, so that would give you a very nice lead on the problem.  But my money is on discontinuous MCLK.

    Jeff

  • Hi Scott,

    you only posted a fracton of your code. But I guess I know what's happening.

    First, it is a bad idea to call a function inside an ISR if not absolutely necessary. The function call does not only take time, also the compiler does not know that the called function runs in interrupt context, so the ISR needs to save and restore all registers wich might be clobbered by the function call (usually 4 that are used for parameter passing and return values) and the called funciton has to save and restore registers which are already saved (and restored) by teh ISR itself. Lots of overhead, while an ISR should be a s short and fast a s possible.

    However, you seem to use UCB0 for SPI transfer. You don't write whether your receive problem is UART or SPI related. In any case, when you write to SPI, you'll always receive a byt ein return, so your RX ISR gets called too for every byte TXed through SPI. You didn't post your uReceiver function, so it may well be that you're taking this SPI RX interrupt as UART RX interrupt and do 'the wrong thing'.
    I don't see, however, an activation of the SPI interrupts at all, so maybe this is not related to the problem.

    Then, you say something about an RS485 drive IC. RS485 is bi-directional, but half duplex. You can only read or write at the same time through RS485 (unless you implement two separate RS485 lines, but this would counteract the meaning of RS486 as a multi-master bus system). Some 'automatic' RS485 receivers toggle the direction based on a monoflop triggered by the TX line. So the transmitte rgoes to send direction the moment a start bit arrives, but it switches back to receiving only after a certain (fixed) time, depending on the monofloops time constant. Since the very last bit of a byte might have retriggered the monoflop, the transceiver will possibly continue sending for one more byte, even if the used baudrate and the monoflop perfectly match. In this period (and during sending) the receiving end is completely deaf.
    It might even be that you echo the data you receive, so the very moment you start sending the echo, you cannot receive anything anymore for some time.

    Well, without knowing the entire code 8regardign the transfers), I cannot exaclty say what's wrong.

  • Thank you so much. That was right ! Jeff.

    Now I have found the reason of problem, which is generating MCLK by FLL. It was supposed to be 8MHz when receiving the data. However, when it received the data from other device, MCLK was around 4MHz. That is why SPI or UART Tx looks working well but UART Rx does not.

    So I have a new problem that how I can make MCLK stable at that point. I have tried to make it 1MHz, because I thought 100ms was too short to make it 8MHz but 4MHz was fine. However, it could not make even 1MHz at the same time, MCLK was just 0.5MHz stragely.   

    Is there any way to make it MCLK supposed to at the point receiving the data?

     

    Scott

  • Scott,

    How are you measuring MCLK?  A frequency counter can be very misleading if your MCLK is discontinuous.

    The way to keep MCLK going while your UART is supposed to be active is to avoid using the Low Power Modes (LPM) that turn off MCLK.

    Are you using any LPMx ?   Which ones?   That is still the most likely cause of your trouble.

    I don't see anything wrong with your clock setup for 8MHz as posted.  But I don't use the 4xx series so I don't know about any subtleties and nuances.

    Jeff

  • Jeff,

    I don't use the LPM, and I am measuring MCLK with an oscilloscope. 

    I guess that FLL takes some time to make MCLK to 8MHz with 32KHz watch crystal, but my wonder is that FLL needs more than 120ms, which is too long time, I think.

     

    Scott

  • Hi Scott,

    Can you confirm that your 32kHz watch crystal is operating correctly?  If you select XT1 as the source for ACLK, for example, then you can observe ACLK on your scope from the appropriate MSP430 pin.

    Keep in mind that such a crystal takes anywhere from 100 to 500 ms to start even under good circumstances.  If you look at the DCO clock (MCLK) before XT1 is oscillating, you are likely to get a very low rate (4MHz instead of 8MHz, 500 kHz instead of 1 MHz, etc.).  The FLL slows the DCO to the limit in that case since the reference isn't ticking.

    Also keep in mind that you cannot transmit or receive via UART until XT1 is up and running reliably since the DCO depends upon XT1 to give you correct clock rates.  However, once XT1 is running reliably, you're clear for communication.  The MSP430 won't stop XT1 unless you explicitly ask it to.

    Jeff

  • Scott Noh said:
    my wonder is that FLL needs more than 120ms, which is too long time

    No, teh FLL is quicker. It takes at most 256 reference ticks (256/32768=8ms). But this is if (and nonly if) there IS actually a reference. It can take several 100 ms to bring the watch crystal to life. You don't check for XT1OF to ensure your crystal is running at all. Maybe it takes just as long. And in the meantime, the FLL considers the current DCO frequency as too high compared to the - not clocking - reference, so it keeps it on minimum, which is 4.2MHz for the FN_4 setting. (when you tried 1MHz, I guess you tried FN_0, where the minimum is 0.65MHz).

  • I believe I am experiencing difficulties very similar to this (ie TX working but a problem with RX)

     

    I am connecting a OneTouch Ultra  to an MSP430-EXPF5438 experimenter board but using the MSP430BT5190 chip.

     

    I can send commands such as "DM" and "DMT" down the UART on the MSP430. The OneTouch Ultra interprets

     this correctly, ie, wakeup in response to "DM". The response to DMT should be "T 0054<CR><LF>". However,

     I only see part of the answer, sometimes 0x30 0x0D 0x0A ("0" <CR> <LF>) Giving symptoms that the RX seems

     to be partially working but missing data.

     

    The OneTouch device has TX,RX & GND although it only operates in half duplex mode. Once a command is

     interpreted from the TX line a response follows asynchronously on the RX line. Hence the delays to take into

     account this request/response delay.

     

    Any pointers as to what could be causing the problem would be appreciated ?

     

    My heavily hacked test code is as follows:

     

    //******************************************************************************
    // OneTouch UART comms test
    // I.Pratt
    // 20/5/11
    //******************************************************************************

    //#include "io430.h"
    #include "msp430x54x.h"

    unsigned char tx_char;
    unsigned char rx_char;
    unsigned char tx_char_array[255];
    unsigned char rx_char_array[255];
    unsigned char tx_char_index;
    unsigned char rx_char_index;
    unsigned char i;

    unsigned char rx_char0;
    unsigned char rx_char1;
    unsigned char rx_char2;
    unsigned char rx_char3;
    unsigned char rx_char4;
    unsigned char rx_char5;
    unsigned char rx_char6;

    void delay_ms(unsigned int delay)
    {
        while (delay--)
        {
            __delay_cycles(1000);
        }
    }

    void main(void)
    {
      WDTCTL = WDTPW+WDTHOLD;                   // Stop watchdog timer
      P7SEL |= 0x03;                            // Port select XT1

      do
      {
        UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);
                                                // Clear XT2,XT1,DCO fault flags
        SFRIFG1 &= ~OFIFG;                      // Clear fault flags
        __delay_cycles(100000);                 // Delay for Osc to stabilize
      }while (SFRIFG1&OFIFG);                   // Test oscillator fault flag

      P1OUT = 0x00;                            // P1.0/1 setup for LED output
      P1DIR |= BIT0+BIT1;                       //
      P10SEL |= BIT4+BIT5;                       // P10.4,5 UART option select          

      //UCA3CTL1 |= UCSWRST;                      // **Put state machine in reset**
      ////UCA3CTL1 |= UCSSEL_1;                     // CLK = ACLK
      //UCA3CTL1 |= UCSSEL__ACLK;
      //UCA3BR0 = 0x03;                           // 32k/9600 - 3.41
      //UCA3BR1 = 0x00;
      //UCA3MCTL = 0x06;
      //UCA3CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
      //UCA3IE |= UCTXIE + UCRXIE;                // Enable USCI_A0 TX/RX interrupt

      UCA3CTL1 |= UCSSEL__SMCLK;
      UCA3BR0 = 0x6D;                         
      UCA3BR1 = 0x00;
      UCA3MCTL = 0x88;                          // Modulation 
      UCA3CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
      UCA3IE |= UCTXIE + UCRXIE;                // Enable USCI_A0 TX/RX interrupt

      //__bis_SR_register(LPM3_bits + GIE);       // Enter LPM3 w/ interrupts enabled
      __bis_SR_register(GIE);       // Enter LPM3 w/ interrupts enabled
      __no_operation();                         // For debugger
     

      tx_char_index = 0;
      rx_char_index = 0;
      tx_char_array[0] = 0x44; // "D"
      tx_char_array[1] = 0x4D; // "M"
      tx_char_array[2] = 0;
     
      // send "DM" 
      for (i = 0; i < 1; i++)
      {
        tx_char_index = 0;
        rx_char_index = 0;
        while(tx_char_array[tx_char_index] != 0)
        {
          while(!(UCA3IFG & UCTXIFG));
          tx_char = tx_char_array[tx_char_index++];
          UCA3TXBUF = tx_char;
        }
        __delay_cycles(500);
      }
     
      for (i = 0;i<10;i++)      // Flash LED for sanity
      {
        if (P1OUT & BIT0)
         P1OUT &= ~BIT0;
        else
         P1OUT |= BIT0;
       
        __delay_cycles(1000);
      }
     
      tx_char_array[0] = 0x44; // "D"
      tx_char_array[1] = 0x4D; // "M"
     // tx_char_array[2] = 0x50; // "P"
      tx_char_array[2] = 0x54; // "T"
      tx_char_array[3] = 0x0D; // Carriage return
      tx_char_array[4] = 0x0A; // Line feed
      //tx_char_array[3] = 0x00;
     // tx_char_array[4] = 0x00;
      tx_char_array[5] = 0x00; // NULL termination
     
        for (i = 0;i<10;i++)      // Flash LED for sanity
      {
        if (P1OUT & BIT0)
         P1OUT &= ~BIT0;
        else
         P1OUT |= BIT0;
       
        __delay_cycles(3000);
      }
     
      // send "DMT"
      tx_char_index = 0;
      rx_char_index = 0;
      while( tx_char_array[tx_char_index] != 0 )
      {
        while(!( UCA3IFG & UCTXIFG ));
        tx_char = tx_char_array[tx_char_index++];
        UCA3TXBUF = tx_char;
      }

      for (i = 0;i<10;i++)      // Flash LED for sanity
      {
        if (P1OUT & BIT0)
         P1OUT &= ~BIT0;
        else
         P1OUT |= BIT0;
       
        __delay_cycles(8000);
      }
     
      rx_char0 = rx_char_array[0];
      rx_char1 = rx_char_array[1];
      rx_char2 = rx_char_array[2];
      rx_char3 = rx_char_array[3];
      rx_char4 = rx_char_array[4];
      rx_char5 = rx_char_array[5];
      rx_char6 = rx_char_array[6];
     
    }

    #pragma vector=USCI_A3_VECTOR
    __interrupt void USCI_A3_ISR(void)
    {

      switch(__even_in_range(UCA3IV,4))
      {
        case 0: break;                          // Vector 0 - no interrupt
        case 2: // Vector 2 -
        { 
              rx_char_array[rx_char_index++] = UCA3RXBUF;                    // RXBUF1     
        }
        break;
        case 4:                                 // Vector 4 - TXIFG
            __delay_cycles(5000);                 // Add small gap between TX'ed bytes       
          UCA3TXBUF = tx_char;                 // Transmit character, this is a dummy to clear flag
        break;
        default: break;
      }
    }

  • UCA3TXBUF = tx_char;                 // Transmit character, this is a dummy to clear flag
    You don't need to activate the TX interrupt if you do not use it for background sending. Then you can simply ignore the TXIFG bit if you don't set the TXIE bit.

    Ian Pratt said:
    __delay_cycles(5000);

    Bad idea. don't do waiting cycles inside an ISR.
    I guess that's what causest your problems: during this wait loop, no other ISR will be called. Inlcuding the RX function. So if during this time two bytes arrive, the first will be overwritten by the second.

    After sending your data, just wait in the main loop until you got the response (and with a timeout just in case you won't get a response), that means until rx-char_index has reached the desired count.

    For the TX, you can do it inside the ISR. Just write the data to be sent in the buffer, then set the TXIE bit. Immediately after, the TX ISR will be called and write the first byte. When the TX ISR has written all bytes, it clears the TXIE bit inside the ISR, so it is no longer called.

    This is a first step to a real ring buffer structure, where you can send and receive totally asynchronous - just write to the ring buffer when you want to send something or read from it when you want to process the read data. Personally, I wrote an infrastructure that allows me to use uart_putch(ch,x) to send a byte and uart_getch(ch,x) to read a byte from one of up to 4 simultanouely handled UARTs. Then I have a stdio module which just maps putchar(), puts() etc. to the 'console' USCI channel, and another module that handles RS485, using the same uart_xxx functions for a different USCI channel.

    You're already halfway there with your approach.


**Attention** This is a public forum