Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Using a PIC16F877 to read a Devantech CMPS03 compass and send the results over RS232

Status
Not open for further replies.

oeginc

New Member
I feel like an idiot here, and I can't believe there aren't any examples of this on the web already somewhere...

Here is my schematic, very straight forward. 16F877 chip with a MAX232 interface chip, and a Devantech CMPS03 compass module.

**broken link removed**

And here is my code (written in CCS-C):
Code:
#include <16F877.h>
#device adc=10

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES HS                       //High speed Osc (> 4mhz)
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES LVP                      //Low Voltage Programming on B3(PIC16) or B5(PIC18)
#FUSES NOCPD                    //No EE protection
#FUSES NOWRT                    //Program memory not write protected
#FUSES NODEBUG                  //No Debug mode for ICD

#use delay(clock=20000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
#use i2c(Master,Fast,sda=PIN_C4,scl=PIN_C3)

long   read_compass()
{
   long   value;

   i2c_start();
   i2c_write( 0xC0 );   // The address of the compass (READ LOW)
   i2c_write( 0x02 );   // Command (read 360 degrees)

   i2c_start();         // Restart to change data dir
   i2c_write( 0xC1 );   // The address of the compass (READ HIGH)

   // Read 2 bytes (HI/LO) and combine into one LONG
   value = i2c_read() << 8;      // Read the HI value * 256
   value = value + i2c_read();   // Read the LO value and add it
   i2c_stop();

   return( value );
}

void main()
{
   long   heading;

   setup_adc_ports(AN0_AN1_AN2_AN3_AN4);
   setup_adc(ADC_CLOCK_INTERNAL);
   setup_psp(PSP_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);

   do
   {
      // Take a compass reading
      heading = read_compass();

      printf("%Lu\r\n", heading );
      delay_ms( 1000 );
   } while( TRUE );
}

I expect to get a value between 0 and 3600 displayed once per second. I expect that while I am just sitting here, the value will be fairly consistent. What I actually get is this:

Code:
65534
1023
2710
65535
2710
65535
2713
65535
2706
65535
2713
245
65534
1023
2711
65535
2710
65535
2712
65535
2716
1525

Does anyone have any idea what I'm doing wrong? If I had to guess I would say that it's got something to do with a timing issue (notice the 6553x every 1-2 reads).

-- Rob
 
Why don't you use their example c code that uses the hardware directly instead of relying on the inbuilt i2c routines. **broken link removed**

Mike.
 
Pommie said:
Why don't you use their example c code that uses the hardware directly instead of relying on the inbuilt i2c routines. **broken link removed**

Mike.

Because that's for a different C compiler and the entire reason I switched to C from assembly was to make the code more readable in the first place.
 
And people argue that c makes code more portable.

That code should work in any compiler. You can turn it into a the relevant functions to make it more readable once you have it working. It will also confirm that your hardware is not the problem.

Mike.
 
Your output is almost correct except, once you get proper value ranging in 2710-2716 and second time 0xFFFF (65535) just check if you are reading CMPS03 very fast. Just try slowing down the reading. I haven't Checked your code but from output it seems this way
 
neelam29 said:
Your output is almost correct except, once you get proper value ranging in 2710-2716 and second time 0xFFFF (65535) just check if you are reading CMPS03 very fast. Just try slowing down the reading. I haven't Checked your code but from output it seems this way

That's what I was thinking originally, but I am only reading the sensor once per second, and this line in the manual made me believe that the output was buffered anyways, so it shouldn't matter.

"There is no synchronism between the PWM or I2C outputs and the conversion. They both retrieve the most recent internal reading, which is continuously converted, whether it is used or not."

Also note the ~1024 values and the 245 mixed in there as well, so which ones are correct?
 
hi rob,
I expect you have checked the RS232 system and 'maths' using a few fixed test values, rather than real time data from the Compass?

Is the 50MHz xtal on the circuit a typo?

EDIT: OK Rob, Its best to ask.
I dont use C, sorry.
 
Last edited:
ericgibbs said:
hi rob,
I expect you have checked the RS232 system and 'maths' using a few fixed test values, rather than real time data from the Compass?

Is the 50MHz xtal on the circuit a typo?

Yes, I have, and yes it is. :) I have been working with the SX28 chips recently and they run a 50 Mhz resonator.

I am running the PIC at 20 Mhz. They data being sent over the RS232 appears to be working fine.
 
Can you try this?

Code:
long read_compass()
{
   long value;

   i2c_start();
   i2c_write( 0xC0 );   // The address of the compass (READ LOW)
   i2c_write( 0x02 );   // Command (read 360 degrees)

   i2c_start();           // Restart to change data dir
   i2c_write( 0xC1 );   // The address of the compass (READ HIGH)

   // Read 2 bytes (HI/LO) and combine into one LONG
   value = i2c_read();      // Read the HI value * 256
   value = value << 8;
   value += i2c_read();   // Read the LO value and add it
   i2c_stop();

   return value;
}
 
eng1 said:
Can you try this?

Code:
long read_compass()
{
   long value;

   i2c_start();
   i2c_write( 0xC0 );   // The address of the compass (READ LOW)
   i2c_write( 0x02 );   // Command (read 360 degrees)

   i2c_start();           // Restart to change data dir
   i2c_write( 0xC1 );   // The address of the compass (READ HIGH)

   // Read 2 bytes (HI/LO) and combine into one LONG
   value = i2c_read();      // Read the HI value * 256
   value = value << 8;
   value += i2c_read();   // Read the LO value and add it
   i2c_stop();

   return value;
}

Yes, that is actually one of my original code samples, I've tried all combinations (that I could think of) of the above:

value = i2c_read() * 256;
value = i2c_read() << 8;
value = i2c_read(); value *= 256;
value = i2c_read(); value = value << 8;

and
value = value + i2c_read();
value += i2c_read();

all had the same results.
 
Maybe you should try the example code as I suggested 2 days ago. At least you would now know if the problem is with the built in routines. BTW, how does the compiler know when you want to send a repeated start bit? Does the compiler have a different command?

Mike.
 
Pommie said:
Maybe you should try the example code as I suggested 2 days ago. At least you would now know if the problem is with the built in routines. BTW, how does the compiler know when you want to send a repeated start bit? Does the compiler have a different command?

Mike.

Unfortunately that code doesn't compile under the CCS C compiler, it's made for the HiTech C compiler... I've downloaded and installed the HiTech demo, but it seems like it's going to be a lot of work to convert what I already have to work in HiTech's compiler (trying to figure out how to even get the serial routines to work at the moment).

With the CCS compiler, if you send another i2c_start without sending an i2c_stop it assumes you want a restart.
 
Surely it's very simple to change the code so it does work. All it is doing is writing to SFRs and setting/clearing bits.

Mike.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top