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.

Standard/Typical RS232 TX handling on MSP430

I'm using the MSP430 Launchpad and I've read most of the forum posts on the RS232 topic.  I have the jumpers configured to use the hardware UART on the G2553 IC and having it transmitting at 9600 baud.  I plan to update this using off-board hardware in the future to obtain a higher baud rate.  My question is, if I want to transmit serial data as fast as possible, is the most appropriate way to do this:

a) Wait until USCI TX interrupts are disabled                  while ((IE2 & UCA0TXIE));                 // assuming USCI TX interrupts are initially disabled.

 b) Write to the transmit buffer                                       UCA0TXBUF = number;

c) Enable the USCI TX interrtupt;                                    IE2 |= USC0TXIE;

d) within the USCIAB0TX interrupt routine, disable the USCI TX Interrupt       IE2 |= UCA0TXIE;

Originally, I tried to wait/pause on the UXA0XIFG interrupt flag, but it seems easier just to work with the interrupt enable bit.

I just wanted to check if this is the typical way, or if I was missing something.

  • You could simply wait for TXIFG. When it is set, load the next character into TXBUF. i.e., while ((IFG2 & UCA0TXIE)==0) {/* nothing */}; UCA0TXBUF = char;

    What you did is to (a) set up an ISR, (b) enable TXIE, (c) when TXIFG is set, it (d) enters ISR which clears TXIE. And you wait for TXIE to clear and then load TXBUF. This will work too. But why go from (a) to (d) to translate TXIFG being set to TXIE being clear?

  • If by TXIFG you mean UCA0TXIFG, then that's where I started as I saw something in a previous forum posting, but I had problems with it hanging.   As I understand it, UCA0TXIFG is cleared after a write to UCA0TXBUF and then SET when after the TX buffer is emptied.  So I used:

                UCA0TXBUFF = number;

                while( ! (IFG2 & UCA0TXIFG) ){ };

    and then within the interrupt service routine, I would manually clear UCA0TXIFG, so that only one interrupt would occur.

                IFG2 &= ~UCA0TXIFG;

    But this would hang in the while statement and when I manually suspended operation and checked the status of the IFG2 register, UCA0TXIFG was cleared (zero), so it wasn't clear why the it was hung.  That's the reason I used the alternative approach and why I asked the original question.


       for(;;)
        {   if(TXOK)
                IE2 |= UCAT0XIE;
                UCA0TXBUF = tmp;                    // TX 1 character
    #if NOTWORKING
                while( ~(IFG2 & UCA0TXIFG)){ };     //HANG
    #else
                while ((IE2 & UCA0TXIE)){};         // Wait for ISR to disable TX
    #endif
        }

    //End of Main

    #pragma vector=USCIAB0TX_VECTOR
    __interrupt void USCI0TX_ISR(void)
    {    IFG2 &= ~UCA0TXIFG;    //clear TX Buffer Empty Interrupt Flag
         IE2  &= ~UCA0TXIE;     // disable USCI_A0 TX interrupt for empty buffer
    }

    Perhaps the correct way is to use the busy flag, although this is TRUE for both RX and TX, so if both are being used could cause a delay issue.

                 while (UCA0STAT & UCBUSY){ };

  • What I meant was:

    (a) Initially, set up the clock, the baudrate, the format, etc. No need to enable interrupt, no need to have ISR or a vector for the ISR.

    (b) When you want to send a char, do:
              while( !(IFG2 & UCA0TXIFG) ){ };
              UCA0TXBUFF = number;
    (c) Nothing else.

  • Okay, that works.  Thanks.  But now I want to add the TX interrupt so that I can transmit a buffer instead of a single character. I'm planning to transmit a circular buffer that is being filled by an ADC10    So, within the interrupt service routine, clearing IFG will stop the interrupt, but it will also affect the While loop. 

    But the interrupt appears to be on the level of IFG, not the transition, so TX interrupts continue as long as IFG and Enable are both high.

    I can disable the TX interrupt, but then I'll have to re-enable it the next time I want to transmit.

    So my question is, what is the typical approach for completing a transmission?

  • If the ADC is producing new data (to be transmitted by the UART) at an even rate, you may not need any buffering at all. Buffering helps only if the peak producing rate is faster, and the average rate is slower then the UART can handle.

    A circular buffer (or FIFO buffer) can be used to "even out" the transmission.

    The TXIFG-ISR need to examine that buffer. If it is empty, it should disable TXIE and exit. If not, it should take the oldest byte out of the buffer and load that to TXBUF and exit.

    To add a byte to the queue, you need to save the current GIE, disable GIE to update the buffer, and restore the original GIE afterwards. You should also enable TXIE while you update the buffer.

  • 2000098 said:
    I'm planning to transmit a circular buffer that is being filled by an ADC10 

    Well, a circular buffer filled by ADC only makes sense if the average speed the data comes in is lowe rthan the sending speed. And then you might not need a buffer at all.
    But perhaps if your main code is busy sometimes and cannot feed the USCI while data comes in continuously, a buffer makes sense.

    For a circular (or linear) buffer, a good setup is:

    The TX ISR jsut reads the next value form buffer and stuffs it into TXBUF. IF the last byte from buffer has been put into TXBUF, the ISR clears the TXIE bit: no more interrupt appear.

    The main code (or any other source for content in the buffer) sets the TXIE bit as soon as data has been written to the buffer. Writing to buffer must happen while GIE is clear (interrupts disabled). This will start eh sending of data again, now that the buffer isn't empty anymore.

    For a circular buffer you'll need a read and a write counter, for a linear buffer (such as a string), only a read counter is required, which is reset when the buffer is filled (or the buffer pointer is switched to new string to send).

    The advantage of a circular buffer is that you can add to it while it is still sendign old data. However, the disadvantage is additional management overhead.
    A circular (ring) buffer maks msot sense if you are addign data in different amopunts and unforseeable intervals. In case o a constant data source, other means of buffering are more advised.
    On MSPs with independent DMA unit, If some timing considerations are obeyed, you may even use the DMA for sending all conversion results directly to the USCI. It is tricky, since the USCI needs byte data and the ADC produces word data (unless you go for 8 bit conversion reults), but doable.
    The ADC10s DTC, however, is incapable to do this, as it can do only 16bit transfers.

  • Thanks for all of the advise, but you guys are getting carried away. I only mention a circular buffer in passing, my question was about an appropriate way to terminate a serial transmission which appears to have been addressed (just disable the TX interrupt). I know how to use a circular buffer and I'm just playing with the application. At the moment, with 9600 Baud with a 30 kSps rate, I'm filling the circular buffer way faster than I can drain it, but that's okay at the moment because I'm looking at static data AND I'm actually also looking at 256 separate analog measurements switched onto one line, (it a linear optical array of 256 photodetectors) so the circular buffer isn't circular in time, it actually 256 separate analog signals that I just keep overwriting. I'll update the Baud rate once I'm confident of the serial transmission approach. Thanks for the clarification on disabling the TX interrupt.
  • 2000098 said:
    you guys are getting carried away

    Happens sometimes :) But I bet, in one or two years someone would revive his thread with a questions that is related to exactly this off-topic branch if we hadn't talked abotu it (and it still may happen) :)

    2000098 said:
    I'm filling the circular buffer way faster than I can drain it

    By a factor of ~60.

    2000098 said:
    I'll update the Baud rate once I'm confident of the serial transmission approach.

    You'll need to go for 115200Bd then. 57600 would be not enough for 30ksps.

    One question: how does the array provide 256 different sensor readings to you? The highest number of separate analog inputs on any MSP is 12, so you need to do some analog multiplexing (or digitally switching th array element output on the array). How do you synchronize it with the ADC conversion at such a high rate?

  • Yes, the analog signals from the 256 photodiodes are switched based on a clock signal, and I'm using the clock signal to trigger the analog conversion. I'm only using one analog input pin. For details, see: http://www.taosinc.com/ProductDetails.aspx?id=6 Basically a line scanner like a copy machine. I'm not using any of the low power features of the MSP430. This is just an application I had that I wanted to use to learn more about the MSP430.
  • I had a similar need, although for me it was a power issue. I could only transmit or sample. Anyway the way clear the interrupt is to simply write a zero to  USCA0TXIFG in the IFG2 register. This will clear out the Interrupt. Note the register is r/w.

    IFG2 &= ~UCA0TXIFG;

    Regards,

    Andy

**Attention** This is a public forum