|
|
View previous topic :: View next topic |
Author |
Message |
nmeyer
Joined: 09 Jul 2004 Posts: 70
|
CRC-8 Question |
Posted: Mon Mar 10, 2008 7:36 am |
|
|
Hello, I am trying to write a program that talks to a Texas Instruments BQ2060A gas gauge chip using SMbus (variation of I2C) communications. I have found that there are times that i get back bad or corrupt data from the BQ2060A. The BQ2060A has a PEC function that uses a CRC-8 polynomial to check data sent and recieved. I have activated this in the chip and now i am trying to write the CRC-8 for my PIC (16F690). I have used code i found here on the forum to accomplish this. However, it does not return the correct CRC value. I have monitored what the BQ2060 sends as the CRC value (always is correct) and what my PIC sends (always wrong). The BQ2060 is looking for a value computed off of the polynomial C(X)=X8+X2+X1+1. I belieave the code i have works that polynomial, but i am not sure. Can anyone review the code and see if i am implementing it wrong or if i have an error in my polynomial? Thanks V4.062
Code: | #include <16F690.h>
#device adc=10
//#priority ssp,rtcc,ext
#use delay(clock=8000000, RESTART_WDT)
#fuses WDT,INTRC_IO, PUT, MCLR, BROWNOUT, NOCPD, NOPROTECT
//#use i2c(SLAVE,FORCE_HW,fast,sda=PIN_B4,scl=PIN_B6,address=0x12,RESTART_WDT,stream=ex)
#use i2c(MASTER,slow,sda=PIN_C4,scl=PIN_C6,stream=in)
#use i2c(SLAVE,FORCE_HW,fast,sda=PIN_B4,scl=PIN_B6,address=0x12,RESTART_WDT,stream=ex)
#include <math.h>
#use fast_io(A)
#define sw_in PIN_A2 //interrupt pin
#define LED4 PIN_A4 //60% led, green
#define LED2 PIN_A5 //10-20% led, yellow
#use fast_io(B)
#define SMBD_E PIN_B4 //smbus data external
#define LED1 PIN_B5 //100% led, green
#define SMBC_E PIN_B6 //smbus clock external
#use fast_io(C)
#define LED5 PIN_C0 //80% led, green
#define SMBD_I PIN_C4 //smbus data, internal
#define LED3 PIN_C5 //40% led, green
#define SMBC_I PIN_C6 //smbus clock, internal
#define LED6 PIN_C7 //10% or less led, red
#define INTS_PER_SECOND 122 //244
#BYTE SSPCON1 = 0x14
////////////////////////////////////////////////////////////////////////////////
int address,lsb_ch_crnt,lsb_percent,msb_status,crc_good,crc;
char i,int_count,bus_free,fault,change_current,chg_term=0,set=0;
int lsb_current,msb_current,count=0,msb,lsb_volt,lsb_status;
int lsb_percent_send,lsb_temp_send,lsb_volt_send,lsb_ch_crnt_send,crc_sent;
int lsb_status_send,msb_status_send,lsb_temp_last,lsb_percent_last;
long time1,seconds,voltage,percent,status,ch_current;
long time_cnt=0,value,loop;
int32 new_val;
signed long current,temperature;
signed int i2c_temp,lsb_temp,lsb;
////////////////////////////////////////////////////////////////////////////////
int crc8_pic(int old_crc,int newbyte)
{
int data_bit,sr_lsb,fb_bit,j;
crc=old_crc;
for(j=0;j<8;j++)
{
data_bit=(newbyte>>j)&0x01;
sr_lsb=crc&0x01;
fb_bit=(data_bit^sr_lsb)&0x01;
crc=crc>>1;
if(fb_bit)
crc=crc^0x8c;
}
return(crc);
}
////////////////////////////////////////////////////////////////////////////////
void read_sm_percent (void)
{
crc=0;
i2c_start(in); //read capacity
i2c_write(in,0x16);
crc8_pic(crc,0x16);
i2c_write(in,0x0D); //relative state of charge
crc8_pic(crc,0x0D);
i2c_start(in);
i2c_write(in,0x17); //tell battery to write data
crc8_pic(crc,0x17);
lsb=i2c_read(in); //read the data
crc8_pic(crc,lsb);
msb=i2c_read(in);
crc8_pic(crc,msb);
crc_sent=i2c_read(in,0);
i2c_stop(in);
output_float(SMBC_I); //release data line
output_float(SMBD_I); //release clock line
lsb_percent=lsb;
if(crc==crc_sent)
crc_good=1;
else
crc_good=0;
}
////////////////////////////////////////////////////////////////////////////////
void read_sm_temperature (void)
{
crc=0;
i2c_start(in); //read temperature
i2c_write(in,0x16); //battery address
crc8_pic(crc,0x16);
i2c_write(in,0x08); //temperature register
crc8_pic(crc,0x08);
i2c_start(in);
i2c_write(in,0x17); //tell battery to write data
crc8_pic(crc,0x17);
lsb=i2c_read(in); //read in data bytes
crc8_pic(crc,lsb);
msb=i2c_read(in);
crc8_pic(crc,msb);
crc_sent=i2c_read(in,0);
i2c_stop(in);
temperature=make16(msb,lsb); //make word
output_float(SMBC_I); //release data line
output_float(SMBD_I); //release clock line
temperature=temperature-2740; //change to celcius
i2c_temp=temperature/10; //make degrees 1C
lsb_temp=make8(i2c_temp,0);
if(crc==crc_sent)
crc_good=1;
else
crc_good=0;
}
////////////////////////////////////////////////////////////////////////////////
void read_sm_voltage (void)
{
crc=0;
i2c_start(in); //read capacity
i2c_write(in,0x16); //battery address
crc8_pic(crc,0x16);
i2c_write(in,0x09); //voltage register
crc8_pic(crc,0x09);
i2c_start(in);
i2c_write(in,0x17); //tell battery to write data
crc8_pic(crc,0x17);
lsb=i2c_read(in); //read data bytes
crc8_pic(crc,lsb);
msb=i2c_read(in);
crc8_pic(crc,msb);
crc_sent=i2c_read(in,0);
i2c_stop(in);
voltage=make16(msb,lsb); //make word
output_float(SMBC_I); //release data line
output_float(SMBD_I); //release clock line
voltage=voltage/100;
lsb_volt=make8(voltage,0);
if(crc==crc_sent)
crc_good=1;
else
crc_good=0;
i2c_start(in); //test crc
i2c_write(in,0x16); //battery address
i2c_write(in,crc); //crc value
i2c_stop(in);
}
////////////////////////////////////////////////////////////////////////////////
void read_sm_current (void)
{
crc=0;
i2c_start(in); //read capacity
i2c_write(in,0x16); //battery address
crc8_pic(crc,0x16);
i2c_write(in,0x0A); //current register
crc8_pic(crc,0x0A);
i2c_start(in);
i2c_write(in,0x17); //tell battery to write data
crc8_pic(crc,0x17);
lsb=i2c_read(in); //read bytes
crc8_pic(crc,lsb);
msb=i2c_read(in);
crc8_pic(crc,msb);
crc_sent=i2c_read(in,0);
i2c_stop(in);
current=make16(msb,lsb); //combine into a word
output_float(SMBC_I); //release data line
output_float(SMBD_I); //release clock line
if(crc==crc_sent)
crc_good=1;
else
crc_good=0;
}
////////////////////////////////////////////////////////////////////////////////
void read_sm_charging_current (void)
{
crc=0;
i2c_start(in); //read status
i2c_write(in,0x16); //battery address
crc8_pic(crc,0x16);
i2c_write(in,0x14); //current register
crc8_pic(crc,0x14);
i2c_start(in);
i2c_write(in,0x17); //tell battery to write data
crc8_pic(crc,0x17);
lsb=i2c_read(in); //read bytes
crc8_pic(crc,lsb);
msb=i2c_read(in);
crc8_pic(crc,msb);
crc_sent=i2c_read(in,0);
i2c_stop(in);
ch_current=make16(msb,lsb); //combine into a word
output_float(SMBC_I); //release data line
output_float(SMBD_I); //release clock line
ch_current=ch_current/64;
lsb_ch_crnt=make8(ch_current,0);
if(crc==crc_sent)
crc_good=1;
else
crc_good=0;
}
////////////////////////////////////////////////////////////////////////////////
void read_sm_status (void)
{
crc=0;
i2c_start(in); //read status
i2c_write(in,0x16); //battery address
crc8_pic(crc,0x16);
i2c_write(in,0x16); //status register
crc8_pic(crc,0x16);
i2c_start(in);
i2c_write(in,0x17); //tell battery to write data
crc8_pic(crc,0x17);
lsb=i2c_read(in);
crc8_pic(crc,lsb);
msb=i2c_read(in);
crc8_pic(crc,msb);
crc_sent=i2c_read(in,0);
i2c_stop(in);
status=make16(msb,lsb);
output_float(SMBC_I); //release data line
output_float(SMBD_I); //release clock line
lsb_status=make8(status,0);
msb_status=make8(status,1);
if(crc==crc_sent)
crc_good=1;
else
crc_good=0;
}
////////////////////////////////////////////////////////////////////////////////
#INT_SSP
void ssp_interupt (void) //detect external smbus activity
{
BYTE incoming, state;
state = i2c_isr_state();
if(state < 0x80) //Master is sending data
{
if(state == 1 ) //First received byte is address
{
incoming = i2c_read(ex,1);
address = incoming;
}
else if(state == 2) //Second received byte is data
{
incoming = i2c_read(ex,1);
msb_current = incoming;
change_current=1;
}
else
{
i2c_read(ex);
}
}
else if(state == 0x80) //Master is requesting data
{
if(address==0x00)
lsb=lsb_status_send;
if(address==0x01) //i2c register
lsb=msb_status_send;
if(address==0x02) //i2c register
lsb=lsb_volt_send;
if(address==0x04) //i2c register
lsb=lsb_temp_send;
if(address==0x08) //i2c register
lsb=lsb_percent_send;
if(address==0x14) //smbus register
lsb=lsb_ch_crnt_send;
i2c_write(ex,lsb); //write first byte
bit_set(SSPCON1,4); //set to let clock float
bit_clear(SSPCON1,5); //turn off serial
output_float(SMBC_E); //let go of clock line
output_float(SMBD_E); //let go of data line
bit_set(SSPCON1,5); //reanable serial
} //added due to errata with PIC
else
{
i2c_write(ex,0x00);
}
output_float(SMBC_E); //let go of clock line
output_float(SMBD_E); //let go of data line
}
#use delay(clock=8000000, RESTART_WDT)
////////////////////////////////////////////////////////////////////////////////
void write_sm_current (void)
{
i2c_start(in);
i2c_write(in,0x16);
i2c_write(in,0x00);
i2c_write(in,0x06);
i2c_write(in,0x06);
i2c_stop(in);
delay_ms(1000);
i2c_start(in);
i2c_write(in,0xA0);
i2c_write(in,0x1A);
i2c_write(in,msb_current);
i2c_write(in,lsb_current);
i2c_stop(in);
delay_ms(20);
i2c_start(in);
i2c_write(in,0x16);
i2c_write(in,0x7D);
i2c_write(in,0x80);
i2c_write(in,0x00);
i2c_stop(in);
i2c_start(in);
i2c_write(in,0x16);
i2c_write(in,0x7D);
i2c_write(in,0x00);
i2c_write(in,0x00);
i2c_stop(in);
output_float(SMBC_I); //release data line
output_float(SMBD_I); //release clock line
}
////////////////////////////////////////////////////////////////////////////////
void sm_free (void)
{
while (count<=25)
{
if ((input(SMBC_I))&&(input(SMBD_I)))
bus_free=1;
else
{
bus_free=0;
count=26;
}
delay_us(3);
count=count+1;
}
count=0;
}
////////////////////////////////////////////////////////////////////////////////
void SSP_bugfix(void)
{
SSPCON1=0x00;
delay_cycles(10);
SSPCON1=0x39;
SSPCON1=0x36;
}
///////////////////////////////////////////////////////////////////////////////
void main()
{
output_float(SMBC_I); //release data line
output_float(SMBD_I); //release clock line
output_float(SMBC_E); //release data line
output_float(SMBD_E); //release clock line
output_high(LED1); //turn led off
output_high(LED2); //turn led off
output_high(LED3); //turn led off
output_high(LED4); //turn led off
output_high(LED5); //turn led off
output_high(LED6); //turn led off
setup_oscillator(osc_8mhz);
setup_adc_ports(NO_ANALOGS);
setup_wdt(WDT_ON|WDT_36MS);
SSP_bugfix();
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
set_tris_a(0b00000100);
set_tris_b(0b01010000);
set_tris_c(0b01010000);
int_count=INTS_PER_SECOND; //used for timer
seconds=0; //clear out variable
//time1=1; //counter for time,30 seconds
time1=5000;
loop=0;
//crc_good=1;
while (TRUE)
{
output_float(SMBC_I); //release data line
output_float(SMBD_I); //release clock line
output_float(SMBC_E); //release data line
output_float(SMBD_E); //release clock line
output_high(LED1); //turn led off
output_high(LED2); //turn led off
output_high(LED3); //turn led off
output_high(LED4); //turn led off
output_high(LED5); //turn led off
output_high(LED6); //turn led off
sleep();
if (!(input(SMBC_E))) //check for clock low
time_cnt=time_cnt+1; //count times throug loop
else //that clock is low
{
time_cnt=0; //if it is not, reset counter
set=0;
}
if((time_cnt==0xFF) && (set==0))
{ //if clock has been low for 0.065s
msb_current=0; //write charge current to 0
lsb_current=0;
write_sm_current(); //reset counters
time_cnt=0;
set=1;
}
delay_us(10);
if(seconds>=time1) //15 seconds has elapsed
{
delay_ms(10);
sm_free();
if (bus_free==1)
read_sm_voltage();
if (lsb_volt>=160)
{
if(crc_good==1)
lsb_volt_send=lsb_volt;
else
delay_us(10);
sm_free(); //check to see if bus is free
if (bus_free==1) //read values
{
read_sm_percent();
if(crc_good==1)
lsb_percent_send=lsb_percent;
else
delay_us(10);
}
sm_free();
if (bus_free==1)
{
read_sm_temperature();
if(crc_good==1)
lsb_temp_send=lsb_temp;
else
delay_us(10);
}
sm_free();
if (bus_free==1)
{
read_sm_current();
}
sm_free();
if (bus_free==1)
{
read_sm_status();
if(crc_good==1)
{
lsb_status_send=lsb_status;
msb_status_send=msb_status;
}
else
delay_us(10);
}
sm_free();
if (bus_free==1)
{
read_sm_charging_current();
if(crc_good==1)
lsb_ch_crnt_send=lsb_ch_crnt;
else
delay_us(10);
}
}
seconds=0;
loop=1;
} //change smbus pins
if((change_current==1) && (address==0x81))
{
if(msb_current>0x1F)
value=0x1F;
else
value=msb_current;
new_val=value*64;
msb_current=make8(new_val,0);
lsb_current=make8(new_val,1);
write_sm_current();
change_current=0;
}
seconds=seconds+1;
}
} |
|
|
|
Ttelmah Guest
|
|
Posted: Mon Mar 10, 2008 10:26 am |
|
|
Haven't looked through the code, but the XOR value used in the code posted, does not appear to be the one for C(X)=X8+X2+X1+1....
Best Wishes |
|
|
Ttelmah Guest
|
|
Posted: Mon Mar 10, 2008 10:40 am |
|
|
Er.
Looking further at the code, glaring problem. You return a value from the crc calculation, but never write this back into the crc byte. The crc needs to be _cumulative_. So in the first few lines of you read_sm_percent code, you have:
Code: |
crc=0;
i2c_start(in); //read capacity
i2c_write(in,0x16);
crc8_pic(crc,0x16);
i2c_write(in,0x0D); //relative state of charge
crc8_pic(crc,0x0D);
|
Which needs to be:
crc=0;
i2c_start(in); //read capacity
i2c_write(in,0x16);
crc=crc8_pic(crc,0x16);
i2c_write(in,0x0D); //relative state of charge
crc=crc8_pic(crc,0x0D);
[/code]
etc...
Best Wishes |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Mon Mar 10, 2008 12:07 pm |
|
|
Ttelmah excellently spots the issue. The CRC has a c in it that means cyclical.
That means the crc value must be continually cycled like a dog chasing its tail. |
|
|
nmeyer
Joined: 09 Jul 2004 Posts: 70
|
|
Posted: Mon Mar 10, 2008 2:08 pm |
|
|
Thanks for the catch. Now, does everyone think this CRC will work for the polynominal or is it not going too? I have made the changes as stated above and i am still not getting the correct CRC value. So, i am thinking that the calculation in the subroutine crc8_pic is not what i really want. |
|
|
Ttelmah Guest
|
|
Posted: Mon Mar 10, 2008 3:39 pm |
|
|
There are other problems. You are using 'crc' as a temporary variable inside the calculation, when it is the same variable used to hold the crc...
I'd think the code should be something like:
Code: |
int8 calc_crc(int8 old_crc,int8 newbyte) {
int8 ctr;
int1 fbbit;
for (ctr=0;ctr<8;ctr++) {
fbbit=((newbyte & 0x80)==0x80)^old_crc;
newbyte<<=1;
old_crc>>=1;
if (fbbit==1)
old_crc^=0x83;
}
return(old_crc);
}
|
This should be about right.
Remember CRC's ae calculated on a _bitwise_ basis as the data is sent. I2C, is sent MSBit _first_. Hence the CRC calculation, needs to work through the byte in this order, not from the LSbit first as the original code is doing. Hopefully the test as shown, should access the top bit each time round.
Best Wishes |
|
|
nmeyer
Joined: 09 Jul 2004 Posts: 70
|
|
Posted: Mon Mar 10, 2008 6:31 pm |
|
|
Thanks for the information. After doing some websearching i was able to find some sample code from Atmel that was designed specifically for the polynomial and smbus crc. I have updated my program for that code and is appears to be working. Below is my new CRC routine.
Code: |
int8 crc8_pic(int8 old_crc,int8 newbyte)
{
int i;
int data;
data=old_crc^newbyte;
for(i=0;i<8;i++)
{
if((data&0x80)!=0)
{
data<<=1;
data^=0x07;
}
else
{
data<<=1;
}
}
return(data);
} |
|
|
|
|
|
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
|