CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

SRF08 with 16F877A using I2C not working

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Harry Mueller



Joined: 17 Oct 2005
Posts: 116

View user's profile Send private message

SRF08 with 16F877A using I2C not working
PostPosted: Tue Mar 14, 2006 1:09 pm     Reply with quote

Just wondering if anyone has any experience with the SRF08 sonar unit from Acroname. I've got 5 volts going to the unit and a small LED tells me I've got the I2C address correct.

The I2C address of the unit is 0xE0, the unit specification to go in that address for inches is 0x50 and 17 separate echos from one ping can be read from 0x02 to 0x23.

When I read the first four locations I just get an "ff".

Here is my code:
Code:
#include <16F877A.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 20000000)
#use i2c(Master,sda=PIN_B1,scl=PIN_B0)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)

main()
{
int i;
int echo_reading[4];
port_b_pullups(TRUE);
while(1)
 {
  i2c_start();
  i2c_write(0xE0);
  i2c_write(0x50);
  delay_ms(66);
  echo_reading[0]=i2c_read(0x02);
  echo_reading[1]=i2c_read(0x03);
  echo_reading[2]=i2c_read(0x04);
  echo_reading[3]=i2c_read(0x05);
  printf("one = %x two= %x three = %x four= %x",echo_reading[0],echo_reading[1],echo_reading[2],echo_reading[3]);
 
 }
}


Thanks, Harry
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Mar 14, 2006 1:22 pm     Reply with quote

i2c_read() doesn't take an address as a parameter. It just takes
ACK or NACK.
kender



Joined: 09 Aug 2004
Posts: 768
Location: Silicon Valley

View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger

Re: SRF08 with 16F877A using I2C not working
PostPosted: Tue Mar 14, 2006 4:36 pm     Reply with quote

Quote:

i2c_start();
i2c_write(0xE0);
i2c_write(0x50);
delay_ms(66);
echo_reading[0]=i2c_read(0x02);
echo_reading[1]=i2c_read(0x03);
echo_reading[2]=i2c_read(0x04);
echo_reading[3]=i2c_read(0x05);
i2c_stop(); // add this line. you have to stop the I2C transaction before you start the next one
Harry Mueller



Joined: 17 Oct 2005
Posts: 116

View user's profile Send private message

PostPosted: Wed Mar 15, 2006 12:08 pm     Reply with quote

Thanks guys, sometimes the obvious is so devious!

I read through a bunch of posts about this and the only thing clear to me is that this subject befuddles more people than just me.

I've tried several alternative approaches but the results are still the same; I'm reading 0xFF.

My last attempt was:
Code:
#include <16F877A.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 20000000)
#use i2c(Master,sda=PIN_B1,scl=PIN_B0)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)

main()
{
int i;
int echo_reading[4];
port_b_pullups(TRUE);
//while(1)
 {
  i2c_start();
  i2c_write(0xE0);
  i2c_write(0x50);
  delay_ms(66);
  i2c_start();
  i2c_write(0x02);
  echo_reading[0]=i2c_read();
  echo_reading[1]=i2c_read();
  printf("one = %x two= %x ",echo_reading[0],echo_reading[1]);
  I2C_stop();
 }
}


Thanks, Harry
rberek



Joined: 10 Jan 2005
Posts: 207
Location: Ottawa, Canada

View user's profile Send private message

PostPosted: Wed Mar 15, 2006 1:31 pm     Reply with quote

This is what I use to read a single byte from a 16-bit address I2C memory, so ignore the second address write:

Code:


// transmit the device address
          I2C_WRITE(a0Write);  // Write command for address
 
          // transmit the memory address
          I2C_WRITE(upperAddress);
          I2C_WRITE(lowerAddress);
 
          I2C_START();
          I2C_WRITE(a0Read);   // Read Command
 
          //transmit the LSB of the data
          lowerData=I2C_READ(0);
          I2C_STOP();



If this sonar unit is following I2C Protocol, you will also need to write the read command to it, as above.

If this unit uses its own special brand of I2C, this post may be of no help whatsoever!
Harry Mueller



Joined: 17 Oct 2005
Posts: 116

View user's profile Send private message

PostPosted: Wed Mar 15, 2006 7:46 pm     Reply with quote

Thanks, things have changed and I think I'm closer but it's still not working. I now get 00 as my value instead of FF. Here is the code I am trying now.
Code:
#include <16F877A.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 20000000)
#use i2c(Master,sda=PIN_B1,scl=PIN_B0)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)

main()
{
int i;
int echo_reading[4];
port_b_pullups(TRUE);
while(1)
 {
  i2c_start();                    //initate start condition
  i2c_write(0xE0);                //write command for address
  i2c_write(0x50);                //set units to inches

  delay_ms(66);                   //wait for returning ping
 
  i2c_write(0x02);                //address of MSB
  i2c_write(0x03);                //Address of LSB
 
  i2c_start();                    //initiate a new start condition
  i2c_write(0xE1);                //read command for address
  echo_reading[0]=i2c_read(1);    //read first byte
  echo_reading[1]=i2c_read(0);    //read second byte
  printf("one = %x two= %x ",echo_reading[0],echo_reading[1]);
  i2c_stop();                     
 }
}
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Mar 15, 2006 11:11 pm     Reply with quote

This robotics site at the following link has got a lot of sample drivers.
Unfortunately it doesn't have any for CCS.
http://www.robot-electronics.co.uk/shop/Examples.htm
Presumably if they're posted there, they work. So let's take the one
for Hi-Tech, which is near the top, and translate it to CCS. Example:
To do this, it takes a little bit of knowledge of how the i2c hardware
works in the PIC. In the listing below, each line of CCS translated
code appears right above the several lines that correspond to it in
the original driver.
Code:

#define SRF08_ADDR   0xE0

int16 get_srf08(void)
{
int16 range;

// This 1st part sends the ranging command
// to the SRF08 to start it ranging.
   
i2c_start();
//   SEN = 1;        // send start bit
//   while(SEN);     // and wait for it to clear
//   SSPIF = 0;
   
i2c_write(SRF08_ADDR);
//   SSPBUF = SRF08_ADDR;  // SRF08 I2C address
//   while(!SSPIF);  // wait for interrupt
//   SSPIF = 0;      // then clear it.
   
i2c_write(0);        
//   SSPBUF = 0;     // address of register to write to
//   while(!SSPIF);  //
//   SSPIF = 0;      //

i2c_write(82);       
//   SSPBUF = 82;    // range in uS command
//   while(!SSPIF);  //
//   SSPIF = 0;      //

i2c_stop();         
//   PEN = 1;        // send stop bit
//   while(PEN);     //

// Now wait for the ranging to complete. This delay is a little
// over 100mS but can be reduced to 66ms if required.

delay_ms(66);   
//   TMR1H = 0;      // delay while the srf08 is ranging
//   TMR1L = 0;                     
//   T1CON = 0x31;   // 1:8 prescale and running (1.6uS/clk)
//   TMR1IF = 0;           
//   while(!TMR1IF); // wait for delay time
//   TMR1ON = 0;     // stop timer   
   
// Finally get the range from the SRF08.
   
i2c_start();
//   SEN = 1;       // send start bit
//   while(SEN);    // and wait for it to clear
//   ACKDT = 0;     // acknowledge bit
//   SSPIF = 0;

i2c_write(SRF08_ADDR);
//   SSPBUF = SRF08_ADDR; // SRF08 I2C address
//   while(!SSPIF);  // wait for interrupt
//   SSPIF = 0;      // then clear it.

i2c_write(2);
//   SSPBUF = 2;     // address of register to read from
//   while(!SSPIF);  //    - high byte of result
//   SSPIF = 0;      //

i2c_start();   
//   RSEN = 1;       // send repeated start bit
//   while(RSEN);    // and wait for it to clear
//   SSPIF = 0;      //

i2c_write(SRF08_ADDR+1);
//   SSPBUF = SRF08_ADDR+1; // SRF08 I2C address
//                    //  - the read bit is set this time
//   while(!SSPIF);   // wait for interrupt
//   SSPIF = 0;       // then clear it.
   
range = i2c_read() << 8;   
//   RCEN = 1;         // start receiving
//   while(!STAT_BF);  // wait for high byte of range
//   range = SSPBUF<<8;// and get it
//   ACKEN = 1;        // start acknowledge sequence
//   while(ACKEN);     // wait for ack. sequence to end

range += i2c_read(0);
//   RCEN = 1;         // start receiving
//   while(!STAT_BF);  // wait for low byte of range
//   range += SSPBUF;  // and get it
//   ACKDT = 1;        // not acknowledge for last byte
//   ACKEN = 1;        // start acknowledge sequence
//   while(ACKEN);     // wait for ack. sequence to end

i2c_stop();
//   PEN = 1;          // send stop bit
//   while(PEN);       //

return(range);
}


Then take that code, strip out the comments and make it into a
test program. I don't have the hardware to test this, but it should
probably work. This program assumes that you have the hardware
wired up correctly, including 4.7K pull-up resistors on the SDA and SCL
signals (pins C4 and C3).
Code:

#include <16F877.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#use i2c(master, sda=PIN_C4, scl=PIN_C3, FORCE_HW)

int16 get_srf08(void);

//=======================
void main()
{
int16 result;

while(1)
  {
   result = get_srf08();
   printf("range in inches = %lu\n\r", result);
   delay_ms(1000);
  }

}

//========================

#define SRF08_ADDR   0xE0

int16 get_srf08(void)
{
int16 range;

// This 1st part sends the ranging command
// to the SRF08 to start it ranging.
i2c_start();
i2c_write(SRF08_ADDR);
i2c_write(0);        
i2c_write(80);  // The range will be returned in inches     
i2c_stop();         

// Now wait for the ranging to complete.
delay_ms(66);   

// Finally get the range from the SRF08.
i2c_start();
i2c_write(SRF08_ADDR);
i2c_write(2);
i2c_start();   
i2c_write(SRF08_ADDR+1);
range = i2c_read() << 8;   
range += i2c_read(0);
i2c_stop();

return(range);
}
Harry Mueller



Joined: 17 Oct 2005
Posts: 116

View user's profile Send private message

PostPosted: Thu Mar 16, 2006 6:31 am     Reply with quote

Thanks so much for all your efforts, PCM.

I had looked at the Hi-Tech example but just couldn't get out of it what you did. When I get this working, I'll send that site this CCS sample.

I'm trying to work this out for a 16F628 but had no way to debug the program so I set up a test on my CCS software evaluation board with the 16F877A.

I assume that I can use the built in pull-up resistors on port B of the 16F877A and choose PIN_B0 PIN_and B1 as SCL and SDA. Is that a correct assumption?

Incidently, the Acroname instructions call for the pull-up resistors to be 1.8k in size.

I'm off to try the new code. Thanks, Harry
Harry Mueller



Joined: 17 Oct 2005
Posts: 116

View user's profile Send private message

PostPosted: Thu Mar 16, 2006 7:46 am     Reply with quote

Oddly enough, I can't get your code to work but when I transpose what I have learned from your code to my program, it works.....sort of.

I'd like to start formating my programs properly so when I have more time I'll try to change it over to what you've provided, step by step until it works.

What happens now is that I get fairly accurate readings up to about 36 inches but nothing but 65535 beyond that. Also the correct readings are bracketed by lots of "readings" of 65535.

Could the value of pull-up resistors affect readings?

The program as it stand right now is:
Code:
#include <16F877A.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 20000000)
#use i2c(Master,sda=PIN_B1,scl=PIN_B0)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)

main()
{
int16 range;
port_b_pullups(TRUE);
while(1)
 {
  i2c_start();                    //initate start condition
  i2c_write(0xE0);                //device address
  i2c_write(0x00);                //register address
  i2c_write(0x50);                //set units to inches

  delay_ms(66);                   //wait for returning ping

  i2c_start();                    //initiate a new start condition
  i2c_write(0xE0);                //device address
  i2c_write(0x02);                //address of high byte register
  i2c_start();
  i2c_write(0xE1);                //device address but read
  range =i2c_read() << 8;         //read first byte and shift left
  range += i2c_read(0);           //read second byte and add to 1st
  printf("range in inches = %lu\n\r", range);
  i2c_stop();                     
  delay_ms(1000);
 }
}


Thanks, Harry
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Mar 16, 2006 9:17 am     Reply with quote

1. According to the 16F877A data sheet, the Port B pullups are typically
20K ohms, but can be as high as 100K. With a 100K pullup, the rise
time could be too long. That's why I suggested using external 4.7K
pullups.

2. Most of the sample drivers on the robotics website do show an
i2c stop bit after the first block which sets the ranging command.
That's right before the delay_ms(66). Are you saying it fails if
you put the i2c_stop() in there ?

3. In the Hi-Tech code, his delay is actually about 105 ms. He suggests
that it could be lowered to 66 ms. You might try increasing the
delay to 105 ms, as this would copy his original code.

4. You have placed the final i2c_stop() after the printf statement.
I don't think there's a good reason to do that, and it might cause
a problem. I would move the stop so it's right after the final read.
Harry Mueller



Joined: 17 Oct 2005
Posts: 116

View user's profile Send private message

PostPosted: Thu Mar 16, 2006 9:59 am     Reply with quote

PCM programmer wrote:
1. According to the 16F877A data sheet, the Port B pullups are typically
20K ohms, but can be as high as 100K. With a 100K pullup, the rise
time could be too long. That's why I suggested using external 4.7K
pullups.

I'll try that this afternoon, that may be the answer. The SRF08 instructions state that 1.8 k resistors be used to pull-up the pins. Should I use those or go with your suggestion?

PCM programmer wrote:
2. Most of the sample drivers on the robotics website do show an
i2c stop bit after the first block which sets the ranging command.
That's right before the delay_ms(66). Are you saying it fails if
you put the i2c_stop() in there ?

I've tried it with both the i2c_stop statement and without it and the results don't seem to vary.

PCM programmer wrote:
3. In the Hi-Tech code, his delay is actually about 105 ms. He suggests
that it could be lowered to 66 ms. You might try increasing the
delay to 105 ms, as this would copy his original code.

Oddly enough, changing it to 105 removed all the extraneous readings. I'm not sure why because the reading is supposed to be finished after 65 ms. The readings beyond about a yard still appear to be wrong (show up as around a yard).

PCM programmer wrote:
4. You have placed the final i2c_stop() after the printf statement.
I don't think there's a good reason to do that, and it might cause
a problem. I would move the stop so it's right after the final read.


I moved it but it didn't appaer to change anything.

Thanks so much, Harry
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Mar 16, 2006 12:21 pm     Reply with quote

1. Use the 1.8K resistors since they recommend them. That's near
the lower limit of the i2c spec and would allow the fastest rise times.
But definitely use external pullups and don't rely on the weak Port B
pullups for i2c communications. 100K ohms is just too weak to give
you a good rise time.

2. The only "data sheet" I can find for the SRF08 is called
devantech-srf08-tech.pdf and it isn't complete. It doesn't show diagrams
of the i2c bus operations, like most other i2c data sheets would have.
Since the other drivers (including the ones written in Basic) have
an i2c_stop() after the initial lines that send the Ranging command,
I think you should keep it in there. It's the normal way to do things
with the i2c protocol.

3. With regard to the 36 inch limit, I suggest that you change the
mode to "81", which returns the range in centimeters. There may
be a problem with the inches mode, and testing it in centimeter mode
will be one way to test this.

4. With regard to the final i2c_stop(), I think it's cleaner and better
to have it occur right after the other i2c instructions, without any
intervening printf() statement. The printf() adds a delay of many
milliseconds and it's not good to introduce unneeded delays into
the middle of a communications protocol.
Harry Mueller



Joined: 17 Oct 2005
Posts: 116

View user's profile Send private message

PostPosted: Thu Mar 16, 2006 1:06 pm     Reply with quote

I tried using 1.8k resistors but nothing much changed, it seems to work equally well with either. However, I have already installed the resistors in the 16F628A that this code will go to once I have it working.

I've also made all the changes to the code that you've recommended.

My outstanding issues are:

When aiming the sonar from a solid position into an open room with the nearest obstacle being over 100 inches away I get wrong and varying readings of between 42 and 52 inches. When I convert the output to centimeters I get about 100 to 125 cm so that's not the problem. the unit works well at closer distances.

The SRF08 collects up to 17 readings with each ping, recording echos from objects at differing distances from the device. I wondered if I could get longer readings from these secondary echos but have been unable to so far because:

1. When I set up an array of int16 range[17], it will only hold 1 byte of data, up to 255.

2. When I print out the 17 values, the 1st is correct but all the others are 255 so I assume I'm not reading the other 16 values correctly.

Here is the code I am using to try to read 17 values:
Code:
#include <16F877A.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 20000000)
#use i2c(Master,sda=PIN_C4,scl=PIN_C3)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)

main()
{
int i;
int16 range[17];
while(1)
 {
  i2c_start();                    //initate start condition
  i2c_write(0xE0);                //device address
  i2c_write(0x00);                //register address
  i2c_write(0x50);                //set units to inches
  i2c_stop();

  delay_ms(105);                   //wait for returning ping

  i2c_start();                    //initiate a new start condition
  i2c_write(0xE0);                //device address
  i2c_write(0x02);                //address of high byte register
  i2c_start();
  i2c_write(0xE1);                //device address but read
  for (i=0;i<17;i++)
  {
  range[i] =i2c_read() << 8;         //read first byte and shift left
  range[i] += i2c_read(0);           //read second byte and add to 1st
  }
  i2c_stop();
  for (i=0;i<17;i++)
  printf("range in inches = %lu\n\r", range[i]);
  delay_ms(1000);
 }
}


Regards, Harry
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group