Transmitting a Double via RS232 on a dsPic30f

An interesting fact you may not be aware of is that the dsPic series micro controllers do not make use of floats. Instead floats are assigned the double type. This means all floats have a much greater precision than they would on other platforms, the downside to this is there is a larger memory use.

Printing a double over RS232 was a challenge that took me a while to get my head around. The main challenge was cutting the double into individual digits for transmission. Unlike the integer type it is not particularly simple to convert a double into hexadecimal. Therefore I have created the function below to solve this issue.

It makes use of the sprintf() function which works in the same was a printf, but instead of the results being transmitted to a terminal the result is stored into a character array.

Once this array has been created it is a simple case of iterating through the array to pring each individual character. It is worth noting that sprintf stores values in ASCII format so there is no need to add 0×30 to value before transmission. There is a simple check to try to catch the decimal place before transmission. The sizeof() function is used to determine how many characters are required to transmit. This does have an issue where transmission times will not be identical, if this is a problem I suggest swapping the sizeof() function for the max size of the char array, ten.

void printDouble(double val){

char tmpArray[10];
int i;

sprintf(tmpArray, “%f”, val);

for(i=0;i if(isdigit(tmpArray[i])){
while(U1STAbits.UTXBF);
U1TXREG = tmpArray[i];
}
else if(tmpArray[i] == ‘.’){
while(U1STAbits.UTXBF);
U1TXREG = 0x2E;
}
}

while(U1STAbits.UTXBF);
U1TXREG = 0xD;

return;
}

Transmitting an Unsigned Integer via RS232 on a dsPic30f

Whilst working with wireless communication, specifically with XBees over RS232, I have found that speed can be an issue when transmitting over RS232. I originally submitted some code in a previous post but found it to be quite clunky and inefficient.

To speed up transmission I opted to convert from transmitting unsigned integers to transmitting hexadecimal code. This was chosen simply because all values between 0 and 65,535 can be transmitted using only four transmissions instead of five. There is less conversion required before transmission also. The downside to this method is some form of conversion back to integer format will probably have to occur at the receiving end, as well as a lessened readability.

The conversions are based around binary logic. An unsigned integer is 16 bytes of data in a dsPic30f, so the value 3217 would look like below when stored in memory.

Binary Representation of Unsgined Integer

By ANDing the data with 0xF we are saying we want to know what the last four bits of data are. For the case of the number 3217 the last four bits of data are 0001, as shown below.

ANDed binary digits

We store the extracted value into an array for temporary storage before printing. Next we shift the data by four bits so we can then examine the next four bits, shown below.

Rotated Unsigned Integer

This then continues until all bits have been examined. The array is then run through backwards, as RS232 shift registers expect that last digit first, and all values are printed. Remember that 0×30 must be added to the value to convert it into ASCII, otherwise the receiver will receive garbage at its end.

Finally 0xD is transmitted, this is a new line character and is not essential for transmission. A space, or another separator will work just as well. Don’t forget that signed integers can also be transmitted with this function, don’t forget to take this account when converting from hex to decimal at the receiving end. The functions code is shown below.


void printUint(unsigned int val){int i;
	unsigned int arr[4];
	for(i=0;i<4;i++){
            arr[i] = val;
            arr[i] &= 0xF;
            val = val >> 4;
	}

	for(i=3;i>=0;i--){
            while(U1STAbits.UTXBF);
            U1TXREG = (arr[i] + 0x30);
	}

        while(U1STAbits.UTXBF);
	U1TXREG = 0xD;

	return;
}

Calculating Pitch and Roll from an Accelerometer

Recently whilst developing a quad rotor UAV I came across the challenge of determining a crafts Pitch and Roll. To beat this challenge I used a combination of an Accelerometer and Gyroscopes. In this post I will discuss how to extract the Pitch and Roll from the Accelerometer exclusively.

An accelerometer works by measuring how much force is applied to an axis. For this task a 3 axis accelerometer is required. I used the Sparkfun 5 Degree of Freedom IMU. What does this mean? 5 Degrees of Freedom (DOF) means that 5 separate axes of motion can be measured. In the case of the IMU (Inertial Measurement Unit) there are 3 axes of accelerometer motion. This means the inertial forces across the x, y and z axes can be measured. There is also a 2 DOF gyroscope attached, measuring the rate of change around the x and y axes.

The forces acting on the accelerometer are shown in (diagram). When an accelerometer is held in place, i.e. there is no acceleration, then the only force acting on it will be gravity. If you imagine the board static with the x and y planes horizontal then gravity will be acting fully on the z axis. This means that the x and y readouts will display a value of 0g whilst the z axis displays a value of 1g, g representing gravity.

If you were to begin rotating the board around its x axis, the y axis will begin moving towards gravity (As shown) and the z axis will begin moving away from gravity. This will mean the value from the y readout will begin increasing, whilst the z axis will start decreasing. The x readout will stay at 0g as we have rotated around the x axis, so it has not moved close too or further from gravity. We can use some relatively simple equations to calculate the pitch and roll using the values we read from the x, y and z axes.

If you are using an analogue accelerometer you face the issue of how do I convert the output into something I can use? Our target is to convert this voltage into g’s. In order to read the data you first require an Analogue to Digital Converter (ADC). These can be found on most micro controllers.

We need to know a few things before we can start programming the micro controller. Firstly what is the voltage reference for the ADC? We need this value to determine what the maximum possible value will be read by the ADC. So if the reference is 3.3v, then the ADC will read a maximum of 3.3v. We also need to know the resolution of the ADC. Typically this will be expressed in bits, the controller I use has a resolution of 10 bits. The 0g voltage is an important factor also, we use 0g as a reference point so this is important. It is possible to detect this value by testing with a multimeter, also the devices data sheet will usually inform you of the 0 values for the accelerometer. Generally the value will be half of the max output, so the device I’m using has a 0g output of 1.5V. Finally we need to know the sensitivity of the outputs, this should be found in the data sheet and will be expressed in mV/g, for the device I’m using the sensitivity is 300mV/g.

What do we do with all this data? We can use it to convert the bits we receive from our ADC into mV, and then into g. I will explain this further as we go on.

Typically an ADC will spit out a value in the form of an unsigned integer. This means we have a string of bits which represent the voltage seen on the analogue pin. We can convert this number to a voltage range using the following (simple!) equation.

x = (ADC_Output * VReference) / ADC_Resolution;

ADC_Output being the value you read from your ADC. VReference is the reference voltage of your ADC. Try to express this in mV, so if your reference is 3.3V then it can be written as 3300mV. As there are 1000mV in 1V. Finally the ADC_Resolution can be found with the following equation;

ADC_Resolution = (2^Resolution) - 1;

So with my 10 bit ADC the value is 1023.

Now as we have expressed our voltage in mV the value of x is expressed in mV. This isn’t useful data just yet, we need to do two things before we can use it. First we need to subtract the 0g level. For my accelerometer the zeroGx value would be 1500mV.

x = x - zeroGx;

Finally we need to convert our voltage reading into g’s. In order to do this we divide by the sensitivity. As the term for x is expressed in mV when we divide by the sensitivity, which is expressed in mV/g, the final result is to have a reading in g.

x = x / sensitivity;

The same steps need to be repeated to find a reading for the y and z axes.

Now that we have 3 outputs expressed in g we should be able to calculate the pitch and the roll. This requires two further equations.

pitch = atan (x / sqrt(y^2 + z^2))
roll = atan (y / sqrt(z^2 + z^2))

This will produce the pitch and roll in radians, to convert them into friendly degrees we multiply by 180, then divide by PI.

pitch = (pitch * 180) / PI
roll = (roll * 180) / PI

You will probably notice that the values that you read from the accelerometer are quite noisy and tend to lose accuracy when the device is moving around a lot. In order to smooth these values out we need to use a complementary filter to combine gyro data, which I hope to include at a later date.

You may also find that when the device is level the x and y values are not 0′s as expected, this can be corrected by altering the zeroG values of the x and y axes. Also if the device is increasing its roll or pitch when you were expecting it to decrease, all you need to do is multiply the output value by -1, to reverse the axis.

Setting up RS232 transmission on a dsPic

UTSetting up communication for RS232 on a dsPic30f is a fairly straight forward task, not as simple as when programming in C18 but still easy enough to do.

The first steps is to obtain the UART reference manual, which can be otained from the microchip website. (Note that this is just a section from the larger dsPIC30F Family Reference Manual). You will also want the data sheet for the dsPIC you are working with, that will also be avaliable on the microchip website.

Next we need to set up the Baud Rate Register, this tells the dsPic what speed the RS232 system is running at. In order to do this we need to know the speed of the oscillator in order to calculate the baud rate register value. The oscillator speed is the frequency of the oscillator, multiplied by the pll and divided by 4. Also we need to know the desired baud rate, from experience 9600 works well.

To calculate the values I have created a spreadsheet which can be found here. Simply input the frequency of the oscillator (after multiplying by the pll value) and the desired baud rate and the UXBRG value will be calculated. Also a percentage error rate will be created, an error rate over 1% will be fairly noticeable when transmitting values, so try to aim for a baud rate which provides an error rate of less than 1%.

Setting the baud rate is quite simple, the command U1BRG = 103; will set the baud rate for a 64MHz oscillator to 9600 baud.

Next the U1MODE register needs to be configured. Looking at the data sheet the following options are required to set the processor up for transmitting data;

  • PDSEL – This configures between 8 and 9 bit data, and whether or not a parity bit will be used
  • STSEL – Stop selection bits configuration, choice between 2 and 1 (1 is usually enough)
  • LPBACK –  Set this to 0 unless you wish the receive line to transmit data, then set to 1.
  • ALTIO – 0 selects the standard UART pins, 1 selects the alternate UART pins
  • ABAUD – setting to 1 uses the receive pin to calculate the baud rate
  • UARTEN – set to 1 to activate the UART.

And finally the U1STA register needs to be configured;

  • UTXBRK – set to 1 to force the  transmissions to 0, set to 0 to run
  • UTXEN – turns on transmissions.

The device should now be configured to transmit data, it is quite straight forward to do this. Firstly you need to ensure the transmit register is not full, or the data you are writing to it will be lost. The command below will wait until the register can accept data;

while(U1STAbits.UTXBF);

And to write the data the following command can be used;

U1TXREG = 0×30;

Bear in mind that data transmitted may be received in ASCII form, so if transmitting numbers they require a value of 0×30 to be added to them. The table on this website demonstrates which hex values can be sent to display in ASCII format. Below I have included a sample program will transmit RS232 data from a dsPIC4011 with a baud rate of 9600 no parity bits and one stop bit.

Change the FOSC to your oscillator speed (note that this is after PLL!), and change BAUD_RATE to your desired baud and this program will print from 0 – 65535 and will loop around again in a hyperterminal window. Hopefully you can use this code to extend your project

/* -----------------------------------------------
--	RS232 Transmit Example Code		--
--						--
--	Author: James Taylor	23/01/11	--
--	Soruce: http://taylor-robotic.co.uk	--
--						--
--	Modify the FOSC and BAUD_RATE values	--
--	to those you desire for use with your	--
--	system. Feel free to modify this code	--
--	for your personal (i.e. non-commerical	--
--	use.					--
------------------------------------------------*/

#include "p30fxxxx.h"
#include "p30f4011.h"

#define FOSC 64000000  	// 64Mhz
#define FCY (FOSC / 4)
#define BAUD_RATE 9600 	//Desired BAUD is 9600
#define UXBRG (((FCY / 16) / BAUD_RATE) -1)

_FOSC(CSW_FSCM_OFF & XT_PLL16);  		// Remove clock monitoring, xt clock, pll 16
_FWDT(WDT_OFF);             			// watch dog off
_FBORPOR(PBOR_OFF & MCLR_EN & PWRT_OFF); 	// Brown out reset disabled, enable MCLR reset pin, turn off power-up timer
_FGS(CODE_PROT_OFF);        			// no code protect

void init_comms(void);

void init_comms(void){
	U1BRG = UXBRG; 		// baud rate
	U1MODEbits.PDSEL = 0; 	// 8 bits no parity
	U1MODEbits.STSEL = 0; 	// 1 stop bit
	U1MODEbits.LPBACK = 0; 	// loopback off
	U1MODEbits.ALTIO  = 1; 	// alternate tx and rx, useful as we can leave the debugger on while doing comms
	U1MODEbits.UARTEN = 1; 	// uart on
	U1MODEbits.ABAUD = 0;	// auto baud off
	U1STAbits.UTXBRK = 0;	// no pause
	U1STAbits.UTXEN   = 1; 	// Enable Transmitter

	return;
}

int main(void){

	unsigned int ab, ac, ad, ae, af, i, value=0;

        init_comms();

	while(1){

		ab = value / 10000;		//take the input and div by 10k to get first digit
		while(U1STAbits.UTXBF);		//make sure the buffer is clear
		U1TXREG = ab + 0x30;		//print first digit (note we add 0x30 to convert to ascii table format, see http://www.asciitable.com
		ab = ab * 10000;		//multiply out again so we can subtract later
		ac = value - ab;		//take the input, remove the first value
		ac /= 1000;			//divide by 1000 to preserve value
		while(U1STAbits.UTXBF);		//this goes on in a similar fashion until we are done
		U1TXREG = ac+ 0x30;
		ac = ac * 1000;
		ad = value - ab - ac;
		ad /= 100;
		while(U1STAbits.UTXBF);
		U1TXREG = ad+ 0x30;
		ad = ad * 100;
		ae = value - ab - ac - ad;
		ae /= 10;
		while(U1STAbits.UTXBF);
		U1TXREG = ae+ 0x30;
		ae = ae * 10;
		af = value - ab - ac - ad - ae;
		while(U1STAbits.UTXBF);
		U1TXREG = af+ 0x30;

		if(value == 65535){
			value = 0; 	//just making sure we don't overflow our unsigned int
		}

		value++;		//create the next value

		for(i=0;i<50000;i++);	//waste some cycles
		for(i=0;i<50000;i++);	//waste some cycles
		for(i=0;i<50000;i++);	//waste some cycles

		while(U1STAbits.UTXBF);
		U1TXREG = 0x0C;		//clears the hyper terminal window
	}

	return 0;
}