|
|
View previous topic :: View next topic |
Author |
Message |
Tunfiskur
Joined: 01 Jun 2012 Posts: 13
|
16F886 I2C question |
Posted: Fri Jun 08, 2012 7:22 am |
|
|
Hello, im trying to get started with I2C stuff. Im currently trying to get my 16F886 to read from my L3G4200D gyro. I just wanted to be able for start to read the WHO_AM_I register which should return the device ID ?
Here is my code:
Code: | #include "ic2.h"
void main()
{
int result=0;
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_oscillator(False);
while(1)
{
printf("RS232 communication working\n\r");
delay_ms(1500);
i2c_start();
i2c_read(0x0F); //0x0F is the who_am_i register in L3G4200D
result = i2c_read();
printf("I2C read:\n\r",result);
delay_ms(1500);
}
} |
Code: |
#include <16F886.h>
#device adc=10
#FUSES NOWDT //No Watch Dog Timer
#FUSES XT //Crystal osc <= 4mhz
#FUSES NOPUT //No Power Up Timer
#FUSES MCLR //Master Clear pin enabled
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOCPD //No EE protection
#FUSES BROWNOUT //Reset when brownout detected
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES LVP //Low Voltage Programming on B3(PIC16) or B5(PIC18)
#FUSES NODEBUG //No Debug mode for ICD
#FUSES BORV40 //Brownout reset at 4.0V
#FUSES NOWRT //Program memory not write protected
#use delay(clock=4000000)
#use rs232(baud=19200,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
#use i2c(Master,Fast,sda=PIN_C4,scl=PIN_C3)
|
I get nothing from the i2c_read().
Anyone see what im doing wrong? Any help much appreciated
Compiler V4.013 |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9246 Location: Greensville,Ontario
|
|
Posted: Fri Jun 08, 2012 8:06 am |
|
|
do you have the correct pullup resistors for the I2C bus based on Vdd and speed ?
also you should always include 'errors' to the use rs232(...options). to prevent UART 'lockup'. |
|
|
Tunfiskur
Joined: 01 Jun 2012 Posts: 13
|
|
Posted: Fri Jun 08, 2012 8:08 am |
|
|
yeah the pull-up resistors are in place |
|
|
drh
Joined: 12 Jul 2004 Posts: 192 Location: Hemet, California USA
|
|
Posted: Fri Jun 08, 2012 8:13 am |
|
|
The I2C embedded in the L3G4200D behaves like a slave device, and the following protocol
must be adhered to. After the START (ST) condition, a slave address is sent. Once a slave
acknowledge (SAK) has been returned, an 8-bit sub-address is transmitted. The 7 LSb
represent the actual register address while the MSB enables address auto-increment. If the
MSb of the SUB field is 1, the SUB (register address) is automatically incremented to allow
multiple data read/write
_________________ David |
|
|
Tunfiskur
Joined: 01 Jun 2012 Posts: 13
|
|
Posted: Fri Jun 08, 2012 11:12 am |
|
|
I worked a bit on this code but no results. I have set up the right protocols for the I2C i think.
Code: | #include "ic2.h"
#include "SendRecive.c"
#define Vistfang_Gyro 0x69
void main()
{
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_oscillator(False);
Gyro_Init();
while(1)
{
printf("RS232 communication working\n\r");
delay_ms(1500);
Read1Byte(105,0x0F);
delay_ms(1500);
}
} |
Found a "driver" for the gyro which i modified a little:
Code: | #define Accel_Wr 0x30
#define Accel_Rd 0x31
#define Gyro_Wr 0xD0
#define Gyro_Rd 0xD1
#define WHO_AM_I 0x0F
#define CTRL_REG1 0x20
#define CTRL_REG2 0x21
#define CTRL_REG3 0x22
#define CTRL_REG4 0x23
#define CTRL_REG5 0x24
#define CTRL_REG6 0x25
#define OUT_X_L 0x28
#define OUT_X_H 0x29
#define OUT_Y_L 0x2A
#define OUT_Y_H 0x2B
#define OUT_Z_L 0x2C
#define OUT_Z_H 0x2D
#define FIFO_CTRL_REG 0x2E
//*** New Types ***//
typedef unsigned short u8;
typedef unsigned int u16;
typedef unsigned long u32;
typedef struct{
int X;
int Y;
int Z;
}TriAxes;
//*** Prototypes ***//
void Gyro_Init(void);
void Write1Byte(u8 Vistfang_Gyro,u8 I2Cadr,u8 I2Cdata);
u8 Read1Byte(u8 Vistfang_Gyro,u8 I2Cadr);
int TwoComp(u8 MSB, u8 LSB);
TriAxes GetSensor(u8 I2Cadr);
//*** Get Sensor ***//
TriAxes GetSensor(u8 I2Cadr)
{
u8 Xlow,Xhigh,Ylow,Yhigh,Zlow,Zhigh;
TriAxes SensorAxe;
Xlow = Read1Byte(I2Cadr,OUT_X_L);
Xhigh = Read1Byte(I2Cadr,OUT_X_H);
Ylow = Read1Byte(I2Cadr,OUT_Y_L);
Yhigh = Read1Byte(I2Cadr,OUT_Y_H);
Zlow = Read1Byte(I2Cadr,OUT_Z_L);
Zhigh = Read1Byte(I2Cadr,OUT_Z_H);
SensorAxe.X = TwoComp(Xhigh, Xlow);
SensorAxe.Y = TwoComp(Yhigh, Ylow);
SensorAxe.Z = TwoComp(Zhigh, Zlow);
return SensorAxe;
}
//*** Two's Complement ***//
int TwoComp(u8 MSB, u8 LSB)
{
int result;
u16 temp;
temp = (MSB * 256) + LSB;
//return temp;
result = ~temp + 1;
return result;
}
//*** Gyro_Init ***//
void Gyro_Init(void)
{
Write1Byte(Gyro_Wr,CTRL_REG1,0x0F); // 100Hz, 12.5, Power Up
Write1Byte(Gyro_Wr,CTRL_REG4,0x20); // 0x00-250dps, 0x10-500dps, 0x20-2000dps
}
//*** Write1Byte ***//
void Write1Byte(u8 Vistfang_Gyro,u8 I2Cadr, u8 I2Cdata)
{
I2C_Start();
I2C_Write(Vistfang_Gyro);
I2C_Write(I2Cadr);
I2C_Write(I2Cdata);
I2C_Stop();
}
//*** Read1Byte ***//
u8 Read1Byte(u8 Vistfang_Gyro,u8 I2Cadr)
{
u8 I2CRead;
I2C_Start();
I2C_Write(Vistfang_Gyro);
I2C_Write(I2Cadr);
I2C_Start();
I2C_Write(Vistfang_Gyro + 1);
I2CRead = I2C_Read();
I2C_Stop();
printf("Something : \n\r",I2CRead);
return I2CRead;
}
|
I dont get anything back, any help would be much appreciated |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Jun 08, 2012 1:35 pm |
|
|
Quote: | #define Vistfang_Gyro 0x69
|
Here you are using an i2c address in 7-bit Philips format. That's not
correct for CCS. CCS uses a left-justified 8-bit slave address.
That means the correct i2c slave address for CCS is 0xD2.
Also, you are using the optional slave address, with the LSB = 1. So that
means that the SDO pin on the L3G4200D must be connected to Vdd to
enable that mode. Did you make that connection on your board ?
Also, the standard in C is for constants to be in ALL CAPS. This helps
to quickly identify them as constants when you read the code.
Quote: |
Read1Byte(105, 0x0F);
|
Here you are using a decimal number for the i2c Slave address. Don't
use decimal, it's not the custom to use it for i2c addresses. And, as I
said above, the address that you want to use is 0xD2. (Provided that
you have pin SDO on the LG4200D connected to Vdd).
Quote: |
u8 Read1Byte(u8 Vistfang_Gyro,u8 I2Cadr)
{
u8 I2CRead;
I2C_Start();
I2C_Write(Vistfang_Gyro);
I2C_Write(I2Cadr);
I2C_Start();
I2C_Write(Vistfang_Gyro + 1);
I2CRead = I2C_Read();
I2C_Stop();
printf("Something : \n\r",I2CRead);
return I2CRead;
}
|
Don't use variable names that are very similar to the function name.
It's confusing, it makes the code harder to read quickly, and it's a bad
coding method.
Quote: | I2CRead = I2C_Read();
|
Here you are making a common mistake. The i2c specification requires
a NACK on the last i2c read operation in a transaction. In CCS, this is
done by using a parameter of 0. So it should be like this:
Code: |
I2CRead = I2C_Read(0);
|
Here you are using a rare fuse setting. It's only for special home-built
programmers that program the PIC without using a 12 or 13 volt
programming voltage. Maybe only 1% of all people do this. All normal
programmers such as ICD2, ICD3, ICD-U40, etc., PicKit 2, Pickit 3,
PicStart-Plus, etc., all use High Voltage programming. For those, you
must use the NOLVP fuse.
Quote: |
while(1)
{
printf("RS232 communication working\n\r");
delay_ms(1500);
Read1Byte(105,0x0F);
delay_ms(1500);
}
} |
Here in your main code, you are calling the Read1Byte() routine but you
are not using the return value. Why ? (And also the 105 needs to be
changed to 0xD2).
Your code should more like the code shown below. You get the return
value from the function, and then you display it.
Code: |
void main()
{
int8 result;
.
.
.
while(1)
{
result = Read1Byte(0xD2, 0x0F);
printf("result = %x \n\r", result);
delay_ms(1500);
}
}
|
Below, you have the printf line in the low-level read routine. It doesn't
really belong there, except maybe as a temporary debugging tool.
The printf belongs in the main while(1) loop, as I showed above.
Quote: |
u8 Read1Byte(u8 Vistfang_Gyro,u8 I2Cadr)
{
.
.
.
printf("Something : \n\r",I2CRead);
return I2CRead;
} |
|
|
|
Tunfiskur
Joined: 01 Jun 2012 Posts: 13
|
|
Posted: Fri Jun 08, 2012 2:15 pm |
|
|
Thank you all for the help guys, i really appreciate it. I have fixed all the suggestions above. I'm getting something back at least but only two numbers from the results. Either 01 or 00, depending on what register adress i choose. I have never work with the i2c before, since i used the %x in printf i should only be getting two hex letters back right?
Also i have only been using CCS for a few weeks, and the int8 and int16 does not work for me(does not get the blue text). Is that normal, do i only use int for all integers?
All the questions looks pretty silly probably but i rather look silly for one day then stupid for the rest of my days hehe |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Jun 08, 2012 3:38 pm |
|
|
I'm not sure if you are really talking to the L3G4200D. Let's find out.
Here is an i2c bus scanner program that will report the addresses of any
i2c slave devices that it finds on the bus. Modify the #include, #fuses,
and the #use statements so it will work with your PIC and your board.
Then run it and see what devices it finds:
http://www.ccsinfo.com/forum/viewtopic.php?t=47707&start=21 |
|
|
Tunfiskur
Joined: 01 Jun 2012 Posts: 13
|
|
Posted: Fri Jun 08, 2012 3:49 pm |
|
|
Thats a neat little code. It returned the slave address and a number of i2c chips.
Code: | Start:
ACD ADDR: D2
Number if i2c chips found: 1 |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Jun 08, 2012 4:54 pm |
|
|
OK, it found your chip. Post your current test program that reads the
ID register. It should be a compilable program. In other words, if
I copy and paste it into MPLAB and press the compile button, it should
not give me any errors, such as undefined variables, etc. Test it before
you post it. |
|
|
Tunfiskur
Joined: 01 Jun 2012 Posts: 13
|
|
Posted: Fri Jun 08, 2012 5:04 pm |
|
|
Copied, compiled without errors before posted:
Code: | #include <16F887.h>
#fuses INTRC_IO,NOWDT,NOPROTECT,PUT,NOLVP,NOBROWNOUT
#use delay(clock=4M)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7)
// This function writes the slave address to the i2c bus.
// If a slave chip is at that address, it should respond to
// this with an "ACK". This function returns TRUE if an
// ACK was found. Otherwise it returns FALSE.
int8 get_ack_status(int8 address)
{
int8 status;
i2c_start();
status = i2c_write(address); // Status = 0 if got an ACK
i2c_stop();
if(status == 0)
return(TRUE);
else
return(FALSE);
}
//=================================
void main()
{
int8 i;
int8 status;
int8 count = 0;
printf("\n\rStart:\n\r");
delay_ms(1000);
// Try all slave addresses from 0x10 to 0xEF.
// See if we get a response from any slaves
// that may be on the i2c bus.
for(i=0x10; i < 0xF0; i+=2)
{
status = get_ack_status(i);
if(status == TRUE)
{
printf("ACK addr: %X\n\r", i);
count++;
delay_ms(2000);
}
}
if(count == 0)
printf("\n\rNothing Found");
else
printf("\n\rNumber of i2c chips found: %u", count);
while(1);
} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Jun 08, 2012 5:09 pm |
|
|
I didn't mean the i2c bus scanner program. I mean your program that
reads the WHO_AM_I register in the L3G4200D (register address 0x0F).
Post your current compilable version of that. You said it was returning
0 or 1, which is not correct. |
|
|
Tunfiskur
Joined: 01 Jun 2012 Posts: 13
|
|
Posted: Fri Jun 08, 2012 5:33 pm |
|
|
This is currently what im working with:
Code: | #include "i2cversion2.h"
void write_device(unsigned char address, unsigned char data)
{
i2c_start();
i2c_write(0xD2);
i2c_write(address);
i2c_write(data);
i2c_stop();
Delay_Ms(10);
}
unsigned char read_device(unsigned char address)
{
unsigned char data;
i2c_start();
i2c_write(0xD2);
i2c_write(address);
i2c_start();
i2c_write(0xD2+1);
data=i2c_read(0);
i2c_stop();
return(data);
}
void init_device()
{
// not done
}
void main()
{
int8 result=0;
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_oscillator(False);
output_high (PIN_A4);
printf("I2C TEST: \n\r");
while(1)
{
result=read_device(0x2D);
printf("result = %x \n\r", result);
delay_ms (1000);
}
}
|
i2cversion2.h :
Code: | #include <16F886.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz)
#FUSES NOPUT //No Power Up Timer
#FUSES MCLR //Master Clear pin enabled
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOCPD //No EE protection
#FUSES BROWNOUT //Reset when brownout detected
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NODEBUG //No Debug mode for ICD
#FUSES BORV40 //Brownout reset at 4.0V
#FUSES NOWRT //Program memory not write protected
#use delay(clock=4000000)
#use rs232(baud=19200,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
#use i2c(Master,Fast,sda=PIN_C4,scl=PIN_C3) |
I'm getting some data back from the gyro, for example with the current setup i have above i get result=06 . But i would think that i need to set the chip in "ON" mode by writing some values to the registers to get the chip into on mode, because according to the datasheet it comes in power down mode by default |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Jun 08, 2012 5:51 pm |
|
|
Here you are trying to read the OUT_Z_H register.
Quote: |
result=read_device(0x2D); |
Why don't you change it and try to read the WHO_AM_I register:
Code: |
result=read_device(0x0F); |
|
|
|
Tunfiskur
Joined: 01 Jun 2012 Posts: 13
|
|
Posted: Fri Jun 08, 2012 5:58 pm |
|
|
Update: this is finally working to some extent at least, and I'm receiving data back when i request a read from the registers. I added a init_device() to the code that sets up the chip to ON mode and other configurations.
Code: | void init_device()
{
write_device(0x20,0x0F); // freq & power up/down
write_device(0x23,0x20); // dps settings
}
|
Code: | void main()
{
int8 result=0;
...
...
...
printf("I2C TEST: \n\r");
init_device();
delay_ms (50);
... |
|
|
|
|
|
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
|