UAV DevBoard Pilots:
This topic concerns an efficient way to transfer data through the transmitter of a USART using transmit interrupts.
There are two general approaches to sending out a message through a USART. The simplest way to do it, but not the most efficient, is to transfer a byte at a time to the transmit buffer of the USART, and wait for the transmitter to be ready before transferring another byte. This method is fine if there is not much data to be sent. This is the method that I use in my firmware to send messages to the GPS to initialize it. Since only a few short messages are sent only during initialization, it is not worth the trouble to be efficient.
However, telemetry traffic is another matter. If you would like to send a lot of data through a USART on a regular basis, the extra programming effort of using transmit interrupts pays off in efficiency.
Conceptually, the idea is simple: use the empty-Tx-buffer interrupt to trigger the transfer of the data to the transmit buffer. That way, your firmware can be off doing something else between characters.
As an example, I have modified the
roll-pitch-yaw demo for the UAV DevBoard to use transmit interrupts to pump out GPS data and direction-cosine data through the spare serial port.
Here are some of the highlights of the C code:
When you are ready to send a message, prepare it and place it in a transmit buffer, as shown in the following routine. Then, simply transfer only the first byte of the message to the USART transmitter:
void debug_output( void )
{
unsigned char txchar ;
db_index = 0 ;
sprintf( debug_buffer , "lat: %li, long: %li, alt: %li\r\nrmat: %i, %i, %i, %i, %i, %i, %i, %i, %i\r\n" ,
lat_gps.WW , long_gps.WW , alt_sl_gps.WW ,
rmat[0] , rmat[1] , rmat[2] ,
rmat[3] , rmat[4] , rmat[5] ,
rmat[6] , rmat[7] , rmat[8] ) ;
txchar = debug_buffer[ db_index++ ] ;
U1TXREG = txchar ;
return ;
}
Some of you may be wondering why I did not transfer the byte directly to the transmitter with a statement such as:
U1TXREG = debug_buffer[ db_index++ ] ;
The reason is that the Microchip C compiler is too smart for its own good, or maybe not smart enough. It updates db_index after the transfer of the byte to U1TXREG. That is too late, because a Tx interrupt will be generated immediately.
The statement U1TXREG = txchar will transfer the first character to the transmitter, and trigger a Tx interrupt. After that, whenever the transmitter is ready for another character, it will generate another interrupt. The interrupt service routine responds as follows:
void __attribute__((__interrupt__,__no_auto_psv__)) _U1TXInterrupt(void)
{
unsigned char txchar ;
IFS0bits.U1TXIF = 0 ; // clear the interrupt
txchar = debug_buffer[ db_index++ ] ;
if ( txchar ) U1TXREG = txchar ;
return ;
}
The example in this case is for an ASCII message. The sprintf() call places a 0 at the end of the message, which is used to detect the end of the message, and completes the transfer.
A binary message would be handled a little differently. In that case, you would need to use a message length variable to detect the end of message.
-Bill