|
|
View previous topic :: View next topic |
Author |
Message |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
SRF08 with 16F877A using I2C not working |
Posted: Tue Mar 14, 2006 1:09 pm |
|
|
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
|
|
Posted: Tue Mar 14, 2006 1:22 pm |
|
|
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
|
Re: SRF08 with 16F877A using I2C not working |
Posted: Tue Mar 14, 2006 4:36 pm |
|
|
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
|
|
Posted: Wed Mar 15, 2006 12:08 pm |
|
|
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
|
|
Posted: Wed Mar 15, 2006 1:31 pm |
|
|
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
|
|
Posted: Wed Mar 15, 2006 7:46 pm |
|
|
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
|
|
Posted: Wed Mar 15, 2006 11:11 pm |
|
|
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
|
|
Posted: Thu Mar 16, 2006 6:31 am |
|
|
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
|
|
Posted: Thu Mar 16, 2006 7:46 am |
|
|
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
|
|
Posted: Thu Mar 16, 2006 9:17 am |
|
|
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
|
|
Posted: Thu Mar 16, 2006 9:59 am |
|
|
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
|
|
Posted: Thu Mar 16, 2006 12:21 pm |
|
|
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
|
|
Posted: Thu Mar 16, 2006 1:06 pm |
|
|
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 |
|
|
|
|
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
|