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

CRC-8 Question

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



Joined: 09 Jul 2004
Posts: 70

View user's profile Send private message

CRC-8 Question
PostPosted: Mon Mar 10, 2008 7:36 am     Reply with quote

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







PostPosted: Mon Mar 10, 2008 10:26 am     Reply with quote

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







PostPosted: Mon Mar 10, 2008 10:40 am     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Mon Mar 10, 2008 12:07 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Mar 10, 2008 2:08 pm     Reply with quote

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







PostPosted: Mon Mar 10, 2008 3:39 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Mar 10, 2008 6:31 pm     Reply with quote

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);
}
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