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.

MSP430BT5190 USCI Communication

Other Parts Discussed in Thread: MSP430BT5190

While trying to get my MSP430BT5190 to communicate with another device using the USCI I had issues sending and receiving data so I tried to communicate with the MSP430 itself before connecting to another device. My code worked when connected to a terminal on my computer using a UART interface, but when sending and receiving to itself (ie the USCITX pin is connected to the USCIRX pin) I would only receive the last character in the transmitted sequence a large number of times instead of receiving the entire sequence. It seems to be caused by the interrupt that should handle characters that are received not triggering until the entire sequence has been sent but I'm not sure why the interrupt isn't being triggered. I've put my code below and help would be appreciated in getting it to send/receive the full sequence correctly:

void configure( unsigned, unsigned char, unsigned char );
void uart_send( char * );

int main(void) {

    WDTCTL = WDTPW | WDTHOLD;    // Stop watchdog timer

    /* Configure Pin Muxing P3.5 RXD and P3.4 TXD */
    P3SEL |= 0x30;

    configure( 104, 1, 0 );

    UCA0IE |= UCRXIE + UCTXIE; //enable Rx & Tx interrupt

    char command[5] = { 'A', 'T', '\r', 'U', '\0' };
    printf( "command is: %s\n", &command);
    uart_send(&command);


    printf( "command sent successfully\n");
    __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0, interrupts enabled

}

void configure( unsigned divider, unsigned char fM, unsigned char sM ){
    /* Place UCA0 in Reset to be configured */
    UCA0CTL1 = UCSWRST;

    /* Configure */
    UCA0CTL1 = UCSSEL1;
    UCA0BR0 = divider & 0xFF;
    UCA0BR1 = divider >> 8;
    UCA0MCTL = ( (fM & 0xF) << 4 |(sM & 0x7) << 1);

    /* Take UCA0 out of reset */
    UCA0CTL1 &= ~UCSWRST;
}

void uart_send( char * command ){
    while( *command ){
        while (!(UCA0IFG & UCTXIFG));
        printf( "transmitting %c\n", *command );
        UCA0TXBUF = *command;
        UCA0IFG &= ~UCTXIFG;
        if( !(UCA0IFG & UCTXIFG) ){
            printf ("interrupt flag is disabled\n");
        }
        command++;
    }

}

/*  Echo back RXed character, confirm TX buffer is ready first */
#pragma vector=USCI_A0_VECTOR
__interrupt void USCI_A0_ISR(void)
{
    while (!(UCA0IFG & UCTXIFG));

    printf( "char: %c\n", (char)UCA0RXBUF );
    UCA0IFG |= UCTXIFG;
}

  • Hi Michael,

    I noticed something about your code - the global interrupts are not enabled until the very last line of main() when you do "    __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0, interrupts enabled"

    This is after you have already called your uart_send() function and transmitted the entire message. But the UCA0RXBUF is only able to hold 1 byte - so it is getting overwritten while you send that whole message, because you do not have your global interrupts (GIE) set until after you've finished sending everything, so the ISR cannot be called to read out the RXBUF this entire time. This is why you are always seeing only the last byte of your transmission, because that is the byte that is left in the buffer when you finally enable interrupts and go to the ISR.

    So change #1 is that you need to enable interrupts before you start transmitting, so that your receiver will be able to handle data while you are sending from the transmitter. You can do this with __enable_interrupt(); or __bis_SR_register(GIE); or something like this. Alternately you could move your transmit code to happen in the UART ISR as well (with a switch statement to distinguish a TX interrupt from an RX interrupt in the ISR), and then you could still go to LPM0 while you transmit if you like.

    I also see that you have enabled both TX and RX interrupts. The problem with this and the way that you have your code written right now, is that your UART ISR does not distinguish between a TX or RX interrupt - so the reason it is getting called over and over again even though there is no new data in the RXBUF (just that same last byte of the transmission) is that it is still entering the ISR because the TXIFG flag is set because the TXBUF is empty. So it is just entering the ISR over and over because of the transmit flag.

    So change #2 is that you either need to distinguish between TX and RX inside your ISR with a switch statement, or you should only enable the RX ISR if you want to control TX from your uart_send function instead.

    A couple of other comments:

    I also see that you are checking UCTXIFG in your ISR - this flag is set when the TXBUF is empty and ready to send more data, so it doesn't really make sense to check this before reading UCA0RXBUF - it would be better to check UCRXIFG to determine if the RXBUF is filled with new data or to distinguish whether TX or RX interrupt called the ISR (if you have decided to keep both TX and RX enabled). Setting UCTXIFG at the end of the ISR also doesn't really make sense - the UCTXIFG will be set automatically by hardware after the data has been shifted out so that you know the buffer is ready to receive new data without over-writing anything that hasn't been sent yet. So you don't need to do this in your ISR either.

    One thing that may make your whole experiment here easier would be if you used two different USCI modules on the device - that way your code/ISR will look more like it will if your real application is only sending from the USCI, and you will have separate ISRs for your TX and RX because you would be TX'ing on one USCI module and receiving on a different one with a different ISR. This might make it easier for you to act like two different devices are talking.

    I hope some of these recommendations help get you up and running.

    Regards,

    Katie

    P.S. when you are debugging your communication with a separate device and it is not working, it may be helpful to use a logic analyzer to see the data that is coming out of the MSP430 and see any data coming back from the other device.

  • Thanks for the response Katie. I made the changes to the LPM and also to our interrupt handler and can now communicate between UARTs.

    I just have a couple branching questions now about how some things work and if you could answer them it'd be a great help.

    1) Does the __enable_interrupts() function use the GIE to enable interrupts? There seems to be very little documentation on how it works so I just want to know where it gets the interrupts to be enabled form.

    2) Can we get interrupted inside of an interrupt? We don't want to be in the middle of receiving something and then suddenly change to sending or vice versa.

    3) I was wondering about the UCRXIFG flag as when the interrupt is called by this flag supposedly being high, if we check it in the interrupt handler to see if RX called it over TX we will see that RXIFG is low and not ready to receive once we're in the handler. Does the flag get reset when the interrupt is detected on this system rather than when you exit the interrupt handler?

    4) In our uart_send() function, instead of actually allowing the interrupt handler to do sends, is the check "while (!(UCA0IFG & UCTXIFG));" equivalent to waiting for the flag to pop and an interrupt handler taking care of it?

    5) What is the purpose of PxSEL? Is it like the PxDIRs but an unspecified direction or something else as I also can't seem to find this information?

    Thanks for any extra help as well as the initial answer,
    Michael
  • Hi Michael,

    Great to hear that you got it working!

    Michael De Paola said:
    1) Does the __enable_interrupts() function use the GIE to enable interrupts? There seems to be very little documentation on how it works so I just want to know where it gets the interrupts to be enabled form.

    Yes, __enable_interrupts() is functionally equivalent to __bis_SR_register(GIE) - it's just easier to read. You can find this and other similar intrinsics defined in the MSP430 C/C++ compiler guide www.ti.com/lit/pdf/slau132 see Table 6-5.

    Michael De Paola said:
    2) Can we get interrupted inside of an interrupt? We don't want to be in the middle of receiving something and then suddenly change to sending or vice versa.

    Great question! Interrupts are disabled upon entry to an ISR, so that this can't happen. See the 5xx/6xx user's guide www.ti.com/lit/pdf/slau208 section 1.3.4 Interrupt Processing. This section explains all the steps happening on entering or exiting an ISR - note how one of the steps is pushing the SR register onto the stack. The SR register contains your LPM bits (so you can return to the same LPM after you leave the ISR because it is stored on the stack), as well as the GIE bit. So these are all stored for later to be restored after the ISR completes. Then the SR register is cleared (see step 6 in section 1.3.4.1) so that interrupts are disabled, and the device wakes from low power mode to be in active mode to be able to execute the ISR code.

    Because of this process above, the only way this sort of nested interrupts could happen would be if your code in the ISR explicitly re-enabled interrupts, and this is not really recommended for the kind of reasoning you mentioned - it makes the code pretty tricky to do correctly. If you want to do something like clear or enable interrupts or change the LPM you were in at the end of your ISR (like if you want to wake from LPM and return to main at the end of the ISR), we provide the additional intrinsics __bic_SR_register_on_exit() and __bis_SR_register_on_exit() - again defined in the Compiler guide linked above. These will modify the SR register only after the ISR has finished and is exiting, again to avoid these kinds of nested interrupts.

    Michael De Paola said:

    3) I was wondering about the UCRXIFG flag as when the interrupt is called by this flag supposedly being high, if we check it in the interrupt handler to see if RX called it over TX we will see that RXIFG is low and not ready to receive once we're in the handler. Does the flag get reset when the interrupt is detected on this system rather than when you exit the interrupt handler?

    The UCTXIFG will be high when the TX buffer is ready for you to load it with more data. It will automatically clear the TX flag when you load the UCxTXBUF register, until it has shifted the data out of the buffer and so is ready for you to write more data in at which point it will go high again.

    Similarly, the UCRXIFG is set to 1 when there is data ready to be read out of UCRXBUF. When you read the UCRXBUF register, the flag will be automatically cleared, until new data is shifted into the RX buffer and it's ready to be read at which point it will go high again.

    So neither one is automatically set or cleared by entering the ISR - it's all done by reading or writing the RX/TX buffers. You can find this information in the 5xx/6xx user's guide www.ti.com/lit/pdf/slau208 in section 34.3.15 USCI interrupts.

    The other way these might be cleared is by reading the UCAxIV interrupt vector register. If you ISR does this (perhaps via a switch statement), it will clear the highest-priority enabled pending interrupt flag (see section 34.3.15.3). Maybe this is what you were thinking of that is similar to having entering the ISR clear it, except that in that case it's not really being cleared by entering the ISR but by UCAxIV being read. Some of our code examples include a switch statement that switches based on the read value of UCAxIV, and have cases for handling the different interrupt vector values - but your code posted above doesn't look like it has this so it wouldn't be getting cleared. As a note this is how a lot of MSP430 interrupt handlers operate, not just the USCI - a lot of our code examples read the interrupt vector in a switch statement to decide what to do and this auto-clears the highest pending enabled interrupt.

    Michael De Paola said:
    4) In our uart_send() function, instead of actually allowing the interrupt handler to do sends, is the check "while (!(UCA0IFG & UCTXIFG));" equivalent to waiting for the flag to pop and an interrupt handler taking care of it?

    It's pretty much the same with a few differences. The polling method you mention you would simply not enable the UCTXIE bit, and poll the flag in a while loop. One difference is that with the polling method you are staying in active mode while you wait for the flag to be set, when you could just go to LPM while you wait for the ISR if you use the interrupt method instead, saving power. Another is that even if you wanted to stay in active all the time, you could execute other code or get other tasks done while waiting for the interrupt if you use the ISR. Conversely, using the ISR, if you are in a LPM there is some wake-up time associated with coming out of low power mode before you can service the ISR - usually this doesn't matter unless you are doing a high speed communication.

    One last thing to note, is after you get past the while() where you are polling the flag and go to write to the buffer, if you have interrupts enabled this could be interrupted by an interrupt (whereas when you are in an ISR it can't be interrupted by default). Whether this is good or bad depends on what you're trying to do and your program flow - you can disable interrupts before you execute this piece of code if you need it to not be interrupted for some reason. Polling is generally considered less efficient coding, but, it does make code easy to follow/control/keep everything executing in order. So it sort of depends on your priorities I'd say. You may want to see www.ti.com/lit/pdf/slaa294 which is an app note on MSP430 coding techniques. It demonstrates a typical interrupt-driven program flow.

    Michael De Paola said:
    5) What is the purpose of PxSEL? Is it like the PxDIRs but an unspecified direction or something else as I also can't seem to find this information?

    You can find this in the device datasheet, www.ti.com/lit/gpn/msp430bt5190 at the end in the Input/output Schematics section. PxSEL is to select alternate functions for the I/O pins, typically peripheral module functions. The reason that this is listed in the datasheet instead of the user's guide, is that it is different for different devices in the same family, because different devices can have different pinouts and have different peripherals in them.

    Regards,

    Katie

**Attention** This is a public forum