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

Views: 270

Reply to This

Replies to This Discussion

It seems quite a good way to send the telemetry to ground with a Xbee

As I am quite new in C30
But I just wondering about db_index it seems to be declared local in void debug_output( void )

I suppose its declared global somewhere else to be modified in _U1TXInterrupt(void)

so the code could be :

void debug_output( void )
{
unsigned char txchar ;
// commented 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[ 0] ; // modified im am not sure about it ??
U1TXREG = txchar ;
return ;
}

Sandra
Sandra,

db_index is a global variable.

In the example I gave, I did not show all of the code. The global variable db_index was defined somewhere else as:

int db_index ;

And then, there was a definition in a .h file that was included into the source for debug output:

extern int db_index ;

Note that where db_index is accessed in the two routines in the example, it is not declared.

Regarding your question:

txchar = debug_buffer[ 0] ; // modified im am not sure about it ??

The code that I published is correct:

txchar = debug_buffer[ db_index++ ] ;
U1TXREG = txchar ;

You start the transfer by passing the first character into the transmit buffer. After that, you want the first interrupt to access the second character, that is why you have to increment db_index.

Incidently, the following would not work:
U1TXREG = debug_buffer[ db_index++] ;
The reason is, the Microchip C compiler transfers the byte to U1TXREG before it increments db_index, as a result the first byte in the buffer would get transmitted twice.

Finally, the code in the example is actually being used exactly as shown for both debugging and for telemetry. It works just fine.

Best regards,
Bill Premerlani
Thanks Bill,

for this explaination . It's quite obvious now .

I will test it right now on my project .

Sandra .
Hi Bill,

I noticed in option.h that I can select #define SERIAL_OUTPUT_FORMAT/Serial_Ardustation.

Would this mean that the Ardustation work with his actual code?
Are the received data the same as from Ardupilot?
If yes I suppose that antenna tracking works too, right?

Thanks and best reards,

Ric
Ric,

There is some work that still needs to be done to make the option that you are referring to work with Ardustation.

Bill
I tested the code using UC00A uart converter.
But i never can see any response in the hyperterminal in my computer.
you put the value of txchar into U1TXREG variable,but i can not see U1TXREG value in debug mode.
this register is always 0.
what problem is that?
van you kindly explain it?
thank you and regards
starsos
Hi Starsos,

I am not sure which version of the firmware you are using. I suggest that you visit the project website, and download the latest version of MatrixPilot, which now includes options for generating output for the serial port. Take a look at the instructions under the Wiki tab of the project site.

I am not familiar with the UC00A. I suggest that you repost your question to the uavdevboard group. There might be someone there who know something about the UC00A.

Best regards,
Bill

RSS

Groups

© 2012   Created by Chris Anderson.

Badges  |  Report an Issue  |  Terms of Service