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.

MSP430F14x/ USART Single Wire / Implementation problem

Other Parts Discussed in Thread: MSP430F149

Hi.. folks...

I have a master device M and slave device S. M polls request from S.  M would reply a handshake as soon as it gets a valid request from S. [Both reply and request is 8 bytes long.]  M and S are connected by two wire, one for ground and the other for usart data trans/recv. And S uses MSP430F149.

To make single wire communication work. S sends data while its recv module is disabled and vice versa for recv procedure. To make my question clear, here's my code:

 

#include <msp430x14x.h>

unsigned char txbuf[8];
unsigned char rxbuf[8];
unsigned char rxIndex;
unsigned char txIndex;

void init_UART()
{
   P3SEL |= 0x10; //P3.4,P3.5
   P3SEL &= ~0x20; //P3.4 select peripheral function and P3.5 selected as GPIO.

   ME1 |= UTXE0;
   ME1 &= ~URXE0; //Slave always send data first, so enable TX module and disable RX module.

   U0CTL = CHAR+SWRST;

   U0BR0 = 0x03; //Baud Rate 9600
   U0BR1 = 0x00;
   U0MCTL = 0x4A; //Modulation

   U0CTL &= ~SWRST; //reset

   IE1 |= UTXIE0;  
   IE1 &= ~URXIE0;

   U0TCTL = SSEL0; //32.768kHz
   IFG1 &= ~UTXIFG0;             

}

void main()
{
   WDTCTL = WDTPW + WDTHOLD;

      // BCSCTL1 = 0x86;
   // BCSCTL2 = 0x00; // Sys Clock cofigured to ~800kHz DCO when BCSCTLx is not assigned any value?


   rxIndex = 0;
   txIndex = 0;

   unsigned char i;
   for(i = 0; i < 8; i++){
   txbuf[i] = 0xb0+i;
   }

   for(i = 0; i < 8; i++){
   rxbuf[i] = 0;
   }

   init_UART();
   _EINT();


  while(1)

 {

   //_NOP();

  if(key_pressed())

   

   IE1  |= UTXIE0;
   IFG1 |= UTXIFG0; //?
   _EINT();

  }

    delay();//Buy enough time for slave to enter 8 RX ISR to recieve master's handshake.

    _NOP(); //Break point here to see if slave receive master's reply in rxbuf[8]

}
}

#pragma vector = UART0TX_VECTOR
__interrupt void UART0_TX()
{
  if(txIndex < 8)
  {
    U0TXBUF = txbuf[txIndex];
    txIndex ++;
  }
  else
 {
   txIndex = 0;

       unsigned int i;

       for(i = 0; i < 150; i ---); 
   

  //while((U0TCTL&TXEPT) == 0);

    // As what I understand in USR Guide,  TXEPT set indicates the 8th byte has been sent.

    //And this should be the  exactly moment to switch TXD(P3.4 for this MCU) to make P3.5(RXD port) ready to recv master's       //reply. 

    // The problem is here,  '' while((U0TCTL&TXEPT) == 0);  " comment out as it does not work as I thought. When prog runs to breakpoint mentioned earlier, rxbuf gets some

   // corruputed bytes. While " for(i = 0; i < 150; i ---); " works fine. I get exactly want I expected in rxbuf. 

   IE1 &= ~UTXIE0;
   IFG1 &= ~UTXIFG0;
   ME1 &= ~UTXE0;

   P3SEL &= ~0x10;
   P3DIR &= ~0x10;

   P3SEL |= 0x20;
   P3DIR &= ~0x20;

   ME1 |= URXE0;
   IE1 |= URXIE0;

   _NOP();
 }
}


#pragma vector = UART0RX_VECTOR
__interrupt void UART0_RX()
{
   if(rxIndex < 8)
  {
    rxbuf[rxIndex] = U0RXBUF;
    rxIndex++;
  }
  else
 {
   rxIndex = 0;
   IE1 &= ~URXIE0;
   IFG1 &= ~URXIFG0;
   ME1 &= ~URXE0;

   P3SEL &= ~0x20;
   P3DIR &= ~0x20;

   P3SEL |= 0x10;
   P3DIR |= 0x10;

   ME1 |= UTXE0;
   IE1 |= UTXIE0;

   //IFG1 |= UTXIFG0;

   _NOP();
 }
}


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

// MSP430F149
// -----------------
// /|\|             XIN|-
// |  |                | 32kHz
// -- |RST         XOUT|-
// |  |                |
// |                  P3.4|----------->
// |                   | 9600 - 8N1
// |                  P3.5|<----------- // P3.4 and P3.5 is connected by a 1uF capacitor instead of shorted by a wire.

                                        // data wire connect to P3.5

  • Very interesting setup...

    ... but if I really could not use a third wire, I'd prefer OneWire protocol (wich you can realize in firmware)...

    unless the master is a PC and you would take advantage of an existing RS232 port.

    Regards,

    Peppe 

  • By saying One wire protocol, do you mean this one ? [ http://www.atmel.com/Images/AVR274.pdf ] I've read that and find it's hard to understand. Actually, I have make one wire communication work in my setup when DCO is ~800kHz. But when DCO is configured to 2MHz, my solution failed.

    I want to make my set up independent of the DCO configuration. Can I do that?

  • I've used this one-wire-UART trick (successfully), but with the F2xx and UCBUSY, not with the F1xx.

    The words used to describe TXEPT (shift register empty) vs UCBUSY (transmitter doing something) are different -- the difference being the Stop bit(s). Reading literally, that allows for the possibility that the F1xx transmitter could still be busy when TXEPT comes on, and you still have to wait 100us (at 9600bps) for the Stop bit to finish. If so, this would also explain why your symptom gets worse when you speed up the CPU.

    This is just a guess -- I don't know. It's also only half a theory, since if it were true you'd expect the RS-485 people (who have to do this all the time) would be fussing pretty loudly.

  • xiao bing said:
    By saying One wire protocol, do you mean this one ?

    No, I mean this one.

    Regards,

    Peppe

  • Bruce McKenney47378 said:
    The words used to describe TXEPT (shift register empty) vs UCBUSY (transmitter doing something) are different

    So what is the UCBUSY for MSPF149? Neighter UxCTL and UxTCTL gives any clue about UCBUSY for F1xx.

    Bruce McKenney47378 said:
    since if it were true you'd expect the RS-485 people (who have to do this all the time) would be fussing pretty loudly.

    My implementation actually comes from a RS-485 example. That one uses TXEPT in the 9th interrupt for TX-ISR.

  • xiao bing said:
    To make single wire communication work. S sends data while its recv module is disabled and vice versa for recv procedure.

    Elegant way of single wire serial comms is to leave receiver enabled all the time. Byte send routine then shall look like:

    switch transmitter on,
    put byte into UART tx register,
    wait for byte we just sent to be received and discard it,
    switch transmitter off.

    Such way you do not risk to cut stop bits whatsoever by abruptly switching TX off due to various implementations of UART. When you receive sent byte by your receiver - you know that destination received it either.

  • Ilmars said:

    switch transmitter on,
    put byte into UART tx register,
    wait for byte we just sent to be received and discard it,
    switch transmitter off.

    Such way you do not risk to cut stop bits whatsoever by abruptly switching TX off due to various implementations of UART. When you receive sent byte by your receiver - you know that destination received it either.

    This way in my set up, the TX operation will trigger RX ISR. ... Wouldn't that be a problem?

  • xiao bing said:
    This way in my set up, the TX operation will trigger RX ISR. ... Wouldn't that be a problem?

    So RX ISR shall have some "discard byte you just going to receive, then switch transmitter off w/o any delay" flag and follow instructions when set. That's it.

    Other option would be to disable RX IRQ (or all interrupts) and use polling to wait until receiver receives byte, switch TX off and then enable interrupts.

    Which approach is best for you - you decide.

  • Ilmars said:
    Such way you do not risk to cut stop bits whatsoever by abruptly switching TX off due to various implementations of UART. When you receive sent byte by your receiver - you know that destination received it either.

    Not necessarily. Reception may happen in the middle of the stop bit. if you switch the transmitter off then, the remainder of the stop bit might be cut-off and maybe the receiver gest a framing error. Depends on the specific hardware and current bus termination.

    In my RS485 (which is sort of differential single-wire, and faces the same problem) I know my baudrate and have a timeout befor ethe transmitte ris switched off and an even longer timeout before the answer starts after receiving the question.

  • Jens-Michael Gross said:
    In my RS485 (which is sort of differential single-wire, and faces the same problem) I know my baudrate and have a timeout befor ethe transmitte ris switched off and an even longer timeout before the answer starts after receiving the question.

    Is your "timeout" equivalent to a software delay? If so, how to calculate that delay interval?

    In my set up, system's clock DCO is configured to ~800kHz, I make a "for(i = 150; i > 0; i--);" delay by some guessing.[Which by some luck it works under ~800kHz].

    However when DCO is set to 2MHz, it seems no longer working no matter how I set up that delay.

    (And many thanks to everyone's reply, thanks for your patience!)

  • xiao bing said:
    Is your "timeout" equivalent to a software delay? If so, how to calculate that delay interval?

    Actually yes. I know the baudrate (9600Bd = 1ms/byte) and just give 1/2 of the byte size as delay before deactivating the transmitter, and 1 byte as delay before answering.

    xiao bing said:
    I make a "for(i = 150; i > 0; i--);" delay by some guessing.

    For loops are no reliable delays. You don't know how the compiler will compile them (if at all - optimization may completely remove an 'empty' loop).

    For short delays (<65ms), I have a timer runing at 1MHz (it also give me the 1ms timer interrupt). For a delay of N µs, I write TAR+N-6 to CCR1 (6µs is the estimated overhead of the delay function call and the setup). Then I wait for the CCIFG bit being set in CCTL0. It marks the end of the delay. Since the clock system setup ensures that the timer is running with 1MHz by definition (if the crystal speed changes, the constant denoting the current crystal speed has to be changed too), the delays will be the same without need to adjust all delay loops manually.

    xiao bing said:
    However when DCO is set to 2MHz, it seems no longer working no matter how I set up that delay.

    On most MSPs, and for sure the 1x family, the UART speed is limited to 1MBd. So if you increase the baudrate to 2MBd, the hardware might just cease to work properly - independent of your code.

  • Jens-Michael Gross said:
    For short delays (<65ms), I have a timer runing at 1MHz (it also give me the 1ms timer interrupt). For a delay of N µs, I write TAR+N-6 to CCR1 (6µs is the estimated overhead of the delay function call and the setup). Then I wait for the CCIFG bit being set in CCTL0. It marks the end of the delay. Since the clock system setup ensures that the timer is running with 1MHz by definition (if the crystal speed changes, the constant denoting the current crystal speed has to be changed too), the delays will be the same without need to adjust all delay loops manually

    TACTL = TASSEL_2 + MC_1; //Up mode

    CCTL0 |= CCIE;
    CCR1 = TAR + 400 - 6; // 1/800k = (1/800) byte, so 400 will be half byte delay?
    while((CCTL0&CCIFG == 0)); //Wait for half byte interval before disable TX.
    CCTL0 &= ~CCIE;


    //I've tried with the above code with DCO ~800KHz.And did not work.

    //Hardware delay is definately more reliable than a for loop. So I will give this more trial.

  • xiao bing said:
    CCTL0 |= CCIE;

    THis enables calling the ISR once the CCIFG bti is set by the hardware. But you don't want an ISR to be called (do you have one at all? If not, the MSP will crash). You want to poll the IFG bit by yourself. So don't set the CCIE bit for this application.

  • Jens-Michael Gross said:
    do you have one at all? If not, the MSP will crash

    I have one for keypad debouncing.

    #pragma vector = PORT1_VECTOR
    __interrupt void P1_ISR(void)
    {

        if((P1IFG&0x0f) != 0)
            {
               CCTL0 |= CCIE;
               TACTL = TASSEL_2 + MC_1;
               CCR0 = 30000;
            }
    }


    #pragma vector=TIMERA0_VECTOR
    __interrupt void Timer_A_ScanKeyPad (void)
    {
         if((P1IFG&0x0f) != 0)
        {
           Scan_KeyPad();
           KEYPAD_IO_IFG = 0x00;
           KEYPAD_IO_OUT = 0x00;

           TACTL = 0;
           CCTL0 &= ~CCIE;
           _NOP();
         }
    }

    And I noticed this issue after this post. . .

    TACTL = TASSEL_2 + MC_1;

    CCR1 = TAR + 400 - 6;
    while(CCTL1&CCIFG == 0);
    CCR1 = 0;
    TACTL = 0;//Do i need to stop the timer when delay expired?

    Anything wrong with my code now? Timer A ISR will not affect this hardware delay? 

    And Timer B has been used too for other application.

  • xiao bing said:
    do you have one at all? If not, the MSP will crash

    I have one for keypad debouncing.[/quote]The moment the ISR is entered (for CCR0, that is(, the CCIFG bti is cleared by hardware. So your waiting loop will never see it. As soon as it appears, it's already gone. You can't have both, ISR and main loop polling of the same IFG bit.
    For CCR1, the timer ISR wouldn't automatically clear the IFG bit, but then if you don't clear it, you are unable to leave the ISR (or rahter, you will enter it immediately again after leaving) unbtil you clear it. The effect on main is the same: it will eithe rfreeze (as 100% of the CPU are spent to over and over call the ISR again) or the IFG bit is cleared.

    So it's an either-or. Either you have an ISR called or you can poll the IFG bit by software. Not both.
    But you can set a global volatile variable inside the ISR and the main code checks this variable and not the IFG bit. But this causes an additional overhead, increasing the minimum delay time.
    Not to mention that an active ISR will add to the delay itself (as teh ISR is executed first, before the waiting loop can detect the end of the delay)

**Attention** This is a public forum