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

"hand written" software I2C driver

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
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

"hand written" software I2C driver
PostPosted: Thu Mar 05, 2009 9:24 pm     Reply with quote

Folks,

Is there a good "hand written" software I2C driver that can talk to a slave pic? I've searched the forum, but didn't find one. I'm writing my own. Currently mine can talk PCA9536, but I can't make it write to another slave PIC. It doesn't seem like a problem on a slave side, because the standard I2C routines ( #use i2c(..., force_sw) ) do the job with the same slave. So, I'm stuck and I'm looking for a reference.

- Nick

P.S. This is related to this post: http://www.ccsinfo.com/forum/viewtopic.php?t=37847
_________________
Read the label, before opening a can of worms.


Last edited by kender on Fri Mar 06, 2009 2:26 am; edited 2 times in total
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Mar 05, 2009 11:47 pm     Reply with quote

See this post:
http://www.ccsinfo.com/forum/viewtopic.php?t=32368&start=3
bsturm



Joined: 23 Feb 2009
Posts: 29

View user's profile Send private message

PostPosted: Fri Mar 06, 2009 7:20 am     Reply with quote

I am planning to test a source code I2C driver also. I'm having I2C trouble and would like to have some source to debug and understand. I just ordered a book called Serial Communications from Square 1 Electronics. The book used to be titled Serial PIC'n. It has great reviews and has source code examples in assembly. It seems to be well worth the money, especially if it comes with good code examples.

You can download the code for the book, but it is not very useful without the book. I should have the book today. I can post back in a few days if it was helpful to me.
PICman



Joined: 02 Nov 2007
Posts: 26

View user's profile Send private message

PostPosted: Fri Mar 06, 2009 3:01 pm     Reply with quote

Here's a test code i've written about one year ago.

It is designed for 24LC256 I2C EEPROMS. The code is not optimized at all and highly commented (in French). It does read and write on the EEPROM.

It writes the whole EEPROM using an initialized LFSR, then, re-initialises the LFSR and does a check on the EEPROM's contents.

Comments are sent on a RS-232 line.

Code:

//
//    Normand Martel VE2UM
//
//    Mes premières armes avec les mémoires
//    EEPROM I2C. Prototype d'essai: 24LC256...
//    Je ne voulais pas commencer avec un machin
//    24LC00 et le magasin de mon quartier tient des
//    24LC256.
//
//    Ce logiciel inclue autant les procédures de
//    lecture que d'écriture aléatoires dans un
//    EEPROM série I2C (245LC256).
//
//    Cette procédure vise les PIC's qui n'ont pas
//    le protocole I2C intégré. Elle a aussi pour
//    but de vous familiariser avec la norme I2C.
//
//              _________  _________
//           __|         \/         |__
//    Masse |__|A0               Vdd|__| +5V
//           __|                 __ |__
//    Masse |__|A1    EEPROM     WE |__| Masse pour écrire, +5V pour protéger.
//           __|     24LC256        |__
//    Masse |__|A2               SCL|__|Horloge série
//           __|                    |__
//    Masse |__|Vss              SDA|__|Données série
//             |____________________|
//
// Lignes I2C:
// ===========
// Ce qui est bien avec la norme I2C (développée
// par Philips), c'est que seulement deux lignes
// série sont nécessaires pour accéder aux composants
// I2C. Ces deux lignes sont SDA (Serial DAta) et SCL
// (Serial CLock). Qui plus est, le I2C est TOTALEMENT
// synchrone ! En dehors d'une vitesse maximale à
// ne pas dépasser, vous n'avez absolument pas à vous
// en faire avec les temps !
//
// Une particularité importante des deux lignes SCL et
// SDA, c'est que les composants (microcontrôleurs et EEPROM)
// ne peuvent QUE TIRER CES LIGNE VERS LE GROUND!!! Jamais un
// composant ne peut tirer une ligne vers le haut. Ce rôle
// est confié UNIQUEMENT à deux résistances tirantes (pullup),
// soit une par ligne. Une valeur suggérée pour ces
// résistances est 4700-20k ohms.
//
//    Formes d'onde
//    =============
// Dans la norme I2C, TOUTE transition de SDA doit être
// effectuée alors que SCL est à BAS.
//
// Les seuls moments où une transition de SDA est
// acceptée alors que SCL est à HAUT est dans les
// commandes de départ (Start) et d'arrêt (stoP).
//
////////////////////////////////////////////////
//       ____S(tart)    //       (sto)P___    //
//    SDA    \_______   // ___________/   SDA //
//       _________      //       _________    //
//    SCL         \__   // _____/         SCL //
//                      //                    //
////////////////////////////////////////////////
//         ___     ___     ___     ___     ___     ___     ___     ___    ___
//  SCL  _/ 1 \___/ 0 \___/ 1 \___/ 0 \___/ A2\___/ A1\___/ A0\___/r/w\__/ack\
//       _______         _______
//  Mas_/       \_______/       \_______________________________________
//
//  Sla                                                                 \_____/
//
// Pour les EEPROMS, les 4 premiers bits doivent
// être 1010. Toute autre combinaison est adressée
// à un autre cmposant qu'un EEPROM et est ignorée par le EEPROM.
//
// Les bits A2, A1 et A0 doivent correspondre aux états logiques des
// broches respectivement 3, 2 et 1 du C.I. Toute autre combinaison est
// ignorée par le EEPROM. Cette particularité permet l'utilisation de
// plusieurs 24LC256 en parallèle sur les deux mêmes lignes I2C.
//
// Pour le moment, A0 à A2 sont à ground avec Vss...
//
//               PIC16F648A
//
// RB1 = RXD
// RB2 = TXD
//
//              _________  _________
//           __|         \/         |__
//          |__|Ra2              Ra1|__|SDA
//           __|                    |__
//          |__|Ra3              Ra0|__|SCL
//           __|                    |__
//          |__|Ra4            clkin|__|
//           __|____                |__
//       +5V|__|MCLR/Vpp      clkout|__|
//           __|                    |__
//        0V|__|Vss              Vdd|__| +5V
//           __|                    |__
//          |__|Rb0/int  Rb7/ICSPDAT|__|
//           __|                    |__
//          |__|Rb1          Rb6/PGC|__|
//           __|                    |__
// 9600baud |__|Rb2              Rb5|__|
//           __|                    |__
//          |__|Rb3              Rb4|__|
//             |____________________|
//

#include <16F648A.h>
//#FUSES XT,NOWDT,PUT,NOPROTECT,BROWNOUT,NOMCLR,NOLVP,NOCPD
//#use fast_io(b)  //On ne veut pas barrer le TRIS (port I2C)
#use delay(clock=4000000)
#use rs232(baud=9600,parity=N,xmit=pin_b2,rcv=pin_b1,bits=8)
#FUSES intrc,NOWDT,PUT,NOPROTECT,BROWNOUT,NOMCLR,NOLVP,NOCPD
   //no master clear a5 (reset) ;cpd barrer eedata;nolvp lo-volt prog

////////////////////////////
void rad(void);           //
void init_pic(void);      //
int8 i2c_read(void);      //
void i2c_write(int8);     //
void S(void);             //
void P(void);             // P R O T O T Y P E S
void envoi_donnees(int1); //
void envoi_octet(int8);   //
int1 lire_donnees(void);  //
void aff_err(int8);       //
int8 adresse_haute;       //
int8 adresse_basse;       //
int8 donnee;              //
int8 hasard;              //
int8 x0,x1,x2,x3,x4,x5;   //
int8 x6,x7,y;             //
////////////////////////////


//#ZERO_RAM
//#int_RTCC

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
void main()                                                             /////
{                                                                       /////
   int8 donnee,compteur,compteur2;                                      /////
   int1 valide;                                                         /////
   int16 nbre_erreurs,nbre_prog,octets_ecrits;                          /////
   /*=-=*//*=-=*//*=-=*//*=-=*//*=-=*//*=-=*//*=-=*//*=-=*//*=-=*/      /////
   init_pic();                                                          /////
   adresse_haute=0x00; // Adresse dans un 24LC256: 0x0000-0x07FF        /////
   adresse_basse=0x00; // Adresse basse = 0-255 haute=0-127             /////
//////////////                                                          /////
   x0=174;  // Initialisation                                           /////
   x1=152;  // RAD                                                      /////
   x2=67;   //                                                          /////
   x3=223;  // Ici, j'initialise un LFSR de façon à générer toujours    /////
   x4=98;   // la même séquence pseuo-aléatoire. Ainsi, je peux écrire  /////
   x5=42;   // dans le EEPROM avec une séquence puis ensuite regénérer  /////
   x6=135;  // la même séquence pour faire la vérification plus loin.   /////
   x7=201;  //                                                          /////
//////////////                                                          /////
   for(compteur=0;compteur<3;compteur++)                                /////
      printf("\n\r----------Ecriture EEPROM I2C------------------");    /////
                                                                        /////
                                                                        /////
   printf("\n\r");                                                      /////
                                                                        /////
   compteur2=0;                                                         /////
   nbre_prog=0;                                                         /////
   octets_ecrits=0;                                                     /////
   valide=1;                                                            /////
   while(valide)                                                        /////
   {                                                                    /////
      rad();                                                            /////
//////////////////////////////////////////////                          /////
      donnee=i2c_read();                    // Prélecture. Si la donnée /////
      if(donnee==hasard)                    // enregistrée dans le      /////
         compteur2++;                       // EEPROM, est la même que  /////
      else                                  // celle à entrer, on n'y   /////
      {                                     // touche pas! Cette        /////
         printf("\n\rAdr: %2X%2X: ",        // procédure a un double    /////
            adresse_haute,adresse_basse);   // avantage:                /////
         printf(" ecrit: %2X",hasard);      //                          /////
         i2c_write(hasard);                 // 1) Beaucoup plus rapide  /////
                                            //                          /////
         donnee=i2c_read();                 // 2) on ne sollicite pas   /////
         printf(" Lu: %2X",donnee);         //    inutilement en        /////
         if(donnee==hasard)                 //    EEPROM.               /////
            octets_ecrits++;                //    écriture la mémoire   /////
      }                                     //    EEPROM.               /////
      if(donnee==hasard)                    //                          /////
         nbre_prog++;                       //                          /////
      else                                  //                          /////
         aff_err(13);                       //                          /////
//////////////////////////////////////////////                          /////
                                                                        /////
////////////////////////////////////////////                            /////
         if(adresse_basse==255)           //                            /////
         {                                //                            /////
            if(adresse_haute==127)        // Compteur d'adresses        /////
               valide=0;                  // Valide pour adresses       /////
         }                                // entre 0x0000 et 0x7FFF     /////
         adresse_basse++;                 // (24LC256)                  /////
         if(adresse_basse==0)             //                            /////
            adresse_haute++;              //                            /////
////////////////////////////////////////////                            /////
                                                                        /////
////////////////////////////////////////////                            /////
      if(compteur2==0)                    // A chaque 256 bonnes        /////
         printf(".");                     // vérifications              /////
////////////////////////////////////////////                            /////
   }                                                                    /////
   printf("\n\r%lu=octets ecrits durant cette session",octets_ecrits);  /////
   printf("\n\r%lu=Total des octets corrects:",nbre_prog);              /////
/////////////////////////////////////////////////////////////////////////////
   while(1)                                                             /////
   {                                                                    /////
      printf("\n\r----------Verification EEPROM I2C--------------");    /////
      printf("\n\r");                                                   /////
      nbre_erreurs=0;                                                   /////
      adresse_haute=0x00;                                               /////
      adresse_basse=0x00;                                               /////
//////////////                                                          /////
   x0=174;  // Initialisation                                           /////
   x1=152;  // RAD                                                      /////
   x2=67;   //                                                          /////
   x3=223;  // Ici, j'initialise un LFSR de façon à générer toujours    /////
   x4=98;   // la même séquence pseuo-aléatoire. Ainsi, je peux écrire  /////
   x5=42;   // dans le EEPROM avec une séquence puis ensuite regénérer  /////
   x6=135;  // la même séquence pour faire la vérification plus loin.   /////
   x7=201;  //                                                          /////
//////////////                                                          /////
                                                                        /////
/////////////////////////////////////////////                           /////
      valide=1;                            // Vérification du           /////
      while(valide)                        // contenu du EEPROM.        /////
      {                                    //                           /////
         rad();                            // Pour celà, on             /////
         donnee=i2c_read();                // réinitilaise le LFSR pour /////
         if(donnee!=hasard)                // ravoir les mêmes données  /////
         {                                 // que lors de l'écriture.   /////
            printf(" %X%X",adresse_haute,  //                           /////
               adresse_basse);             //                           /////
            nbre_erreurs++;                //                           /////
         }                                 //                           /////
/////////////////////////////////////////////                           /////
                                                                        /////
/////////////////////////////////////////////                           /////
                                           //                           /////
         if(adresse_basse==255)            //                           /////
         {                                 //                          /////
            printf("+");                   //                         /////
            if(adresse_haute==127)         // Compteur d'adresses    /////
               valide=0;                   //                       /////
         }                                 //                      /////
         adresse_basse++;                  //                     /////
         if(adresse_basse==0)              //                    /////
            adresse_haute++;               //                   /////
                                           //                  /////
      }                                    //                 /////
      printf("\n\rTotal: %lu erreurs",     //                /////
              nbre_erreurs);               //               /////
/////////////////////////////////////////////              /////
   }                                                      /////
}                                                        /////
/////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////
void init_pic(void)                                      //
{                                                        //
   output_a(0);                                          //
   output_b(0);                                          //
   set_tris_a(0b11111111);  // gauche=bit7, droite=bit0  //
   set_tris_b(0b11111011);  // gauche=bit7, droite=bit0  // Routine
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256);            // d'initialisation
   setup_timer_1(T1_DISABLED);                           // du PIC
   setup_timer_2(T2_DISABLED,0,1);                       //
   setup_comparator(NC_NC_NC_NC);  //not connected       //
   setup_vref(FALSE);              // not used           //
   disable_interrupts(INT_RTCC);                         //
   disable_interrupts(GLOBAL);                           //
//   setup_oscillator(False);                            //
   port_b_pullups(true);                                 //
}                                                        //
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////


int8 i2c_read()
/////////////////////////////////////////////////////////////////////
{                                                                  //
   int1 ack,ein;                                                   //
   int8 rot;                                                       //
   int8 compteur;                                                  //
   /*=-=*//*=-=*/                                                  //
                                                                   //
                                                                   //
///////////                                                        //
   S();  // Start                                                  //
///////////                                                        // Lecture
                                                                   // du
////////////////////////                                           // EEPROM
   envoi_octet(0xA0); // ADRESSAGE/ÉCRITURE                        // I2C
////////////////////////                                           //
                                                                   //
//////////////////////////////////                                 //
   envoi_octet(adresse_haute);  // envoi des                       //
////////////////////////////////// adresses                        //
   envoi_octet(adresse_basse);  // au EEPROM                       //
//////////////////////////////////                                 //
                                                                   //
/////////// Pour la lecture du EEPROM. on doit                     //
   S();  // exécuter un second "Start" après                       //
/////////// l'adressage...                                         //
                                                                   //
//////////////////////// ...et on envoie                           // Lecture
   envoi_octet(0xA1); // une commande de                           // du
//////////////////////// lecture                                   // EEPROM
                                                                   // série
//////////////////////////////////                                 // I2C
   donnee=0;                    // Procédure de lecture            //
                                // du EEPROM en tant que tel.      //
   compteur=7;                  //                                 //
   while(!bit_test(compteur,7)) // On utilise la variable          //
   {                            // "rot" dont on chargera le       //
#asm                            // bit 0 avec la donnée            //
      rlf rot,f                 // recueillie sur le port I2C.     //
#endasm                         //                                 //
      ein=lire_donnees();       // Entre chaque bit, on "rote"     //
                                // la variable "rot" à gauche.     //
      bit_clear(rot,0);         //                                 //
      if(ein)                   // Ainsi, on charge "rot" avec     //
         bit_set(rot,0);        // les huit bits, le bit le plus   //
      compteur--;               // lourd en premier.               //
   }                            //                                 //
////////////////////////////////// Georgia Fulmante                //
                                                                   //
////////////////////////// ACK final                               //
   ack=lire_donnees();  //                                         //
   if(ack==0)           // Ici, à la fin de la lecture, le        //
      aff_err(11);      // EEPROM NE doit PAS envoyer de ACK.    //
//////////////////////////                                      //
                                                               //
///////////                                                   //
   P();  // stoP                                             //
///////////                                                 //
                                                           //
   return(rot);                                           //
}                                                        //
//////////////////////////////////////////////////////////

void i2c_write(int8 octet)
/////////////////////////////////////////////////
{                                              //
   int1 ack,valide;                            //
   int8 limite_ecriture;                       //
 /*=-=*//*=-=*//*=-=*//*=-=*//*=-=*//*=-=*/    //
///////////                                    //
   S();  // Start                              //
///////////                                    //
                                               //
////////////////////////                       //
   envoi_octet(0xA0); // ADRESSAGE/ÉCRITURE    //
////////////////////////                       //
                                               //
//////////////////////////////////             //
   envoi_octet(adresse_haute);  // Envoi de    //
////////////////////////////////// l'adresse   //
   envoi_octet(adresse_basse);  // vers le     //
////////////////////////////////// EEPROM      //
                                               //
///////////////////////// Envoi de l'octet de  //
   envoi_octet(octet); // données à être       //
///////////////////////// écrit dans le EEPROM //
                                               //
/////////// Au stoP, le EEPROM écrit           //
   P();  // l'octet dans sa mémoire            //
/////////// interne.                           // Écriture du
                                               // EEPRM I2C
////////////////////////////                   //
   limite_ecriture=0;     //                   //
   valide=0;              // Ici, on envoie    //
   while(valide==0)       // un octet d'essai  //
   {                      // vers le EEPROM.   //
   ///////////            //                   //
      S();  // Start      // Celui-ci nous     //
   ///////////            // envoiera un ACK   //
      envoi_donnees(1);   // uniquement        //
      envoi_donnees(0);   // lorsque la        //
      envoi_donnees(1);   // procédure         //
      envoi_donnees(0);   // d'écriture        //
      envoi_donnees(0);   // interne du        //
      envoi_donnees(0);   // EEPROM sera      //
      envoi_donnees(0);   // terminée.       //
      envoi_donnees(0);   //                //
                          //               //
      ack=lire_donnees(); //              //
      if(ack==0)          ///////        //
         valide=1;             //       //
      if(limite_ecriture>253)  //      //
      {                        //     //
         aff_err(12);     ///////    //
         valide=1;        //        //
      }                   //       //
   ///////////            //      //
      P();  // stoP       //     //
   ///////////            //    //
      limite_ecriture++;  //   //
   }                      //  //
//////////////////////////// //
}                           //
/////////////////////////////

/////////////////////////////////////////////////////////
void S(void)                                           //
{                                                      //
   int1 valide;                                        //
   int8 essais;                                        //
   /*=-=*//*=-=*//*=-=*//*=-=*/                        //
   set_tris_a(0b11111111);  // Tout le monde           //
                            // en l'air!               //
   delay_us(4);                                        //
//////////////////////////////////////////// On        //
   if(!input_state(40)||!input_state(41)) // vérifie   //
      aff_err(7);                         // que SDA   //
//////////////////////////////////////////// et SCL=1  //
                                                       //
//////////////////////////////////////                 //
   essais=0;                        //                 //
   valide=0;                        //                 //
   while(valide==0)                 //                 // procédure
   {                                //                 // Start
      set_tris_a(0b11111101);       // On descend SDA  //
      delay_us(4);                  //                 //
      if(!input_state(41))          //                 //
         valide=1;                  // On vérifie que  //
      else                          // SDA soit bien   //
      {                             // à LO.           //
         aff_err(0);                //                 //
         essais++;                  //                 //
         if(essais>7)               //                 //
            valide=1;               //                 //
      }                             //                 //
   }                                //                 //
//////////////////////////////////////                 //
                                                       //
//////////////////////////////////////                 //
   essais=0;                        //                 //
   valide=0;                        //                 // procédure
   while(valide==0)                 //                 // Start
   {                                // puis, on        //
      set_tris_a(0b11111100);       // descend SCL     //
      delay_us(4);                  //                 //
                                    //                 //
      if(!input_state(40))          // On vérifie que  //
         valide=1;                  // SCL soit bien   //
      else                          // à LO.           //
      {                             //                 //
         aff_err(1);                //                 //
         essais++;                  //                 //
         if(essais>7)               //                 //
            valide=1;               //                 //
      }                             //                 //
   }                                //                 //
   delay_us(4);                     //                 //
//////////////////////////////////////                 //
}                                                      //
/////////////////////////////////////////////////////////

////////////////////////////////////////////////////
void P(void)                                      //
{                                                 //
   int1 valide=0;                                 //
   int8 essais;                                   //
   /*=-=*//*=-=*//*=-=*//*=-=*//*=-=*/            //
///////////////////////////// Avant de relâcher   //
   set_tris_a(0b11111100); // SCL, on doit        //
   delay_us(4);            // s'assurer que SDA   //
///////////////////////////// soit à LO.          //
                                                  //
////////////////////////////////                  //
   essais=0;                  //                  //
   valide=0;                  //                  //
   while(valide==0)           //                  //
   {                          // On               // procédure
      set_tris_a(0b11111101); // relâche          // stoP
      delay_us(4);            // SCL...           //
                              //                  //
      if(input_state(40))     // On vérifie que   //
         valide=1;            // SCL soit bien    //
      else                    // à HI.            //
      {                       //                  //
         aff_err(1);          //                  //
         essais++;            //                  //
         if(essais>7)         //                  //
            valide=1;         //                  //
      }                       //                  //
   }                          //                  //
////////////////////////////////                  //
   delay_us(4);                                   //
                                                  //
////////////////////////////////                  //
   essais=0;                  //                  //
   valide=0;                  //                  //
   while(valide==0)           //                  //
   {                          // ...puis on       // procédure
      set_tris_a(0b11111111); // delâche SDA     // stoP
      delay_us(4);            //                //
                              //               //
      if(input_state(41))     // On s'assure  //
         valide=1;            // SDA soit    //
      else                    // bien à HI. //
      {                       //           //
         aff_err(10);         //          //
         essais++;            //         //
         if(essais>7)         //        //
            valide=1;         //       //
      }                       //      //
   }                          //     //
////////////////////////////////    //
}                                  //
////////////////////////////////////

///////////////////////////////////////////////////////////
void envoi_octet(int8 rot)                               //
//////////////////////////////////                       //
{                               // Pour envoyer un       // Envoi d'un
   int1 ein,ack;                // octet, on  transfère  // octet vers
   int8 compteur;               // l'octet dans une      // le EEPROM
   /*=-=*//*=-=*//*=-=*//*=-=*/ // variable "rot".       // série I2C
   compteur=7;                  //                       //
   while(!bit_test(compteur,7)) // De "rot", on lit et   //
   {                            // envoie le bit 7 (le   //
      ein=0;                    // plus lourd) vers I2C. //
      if(rot&0x80)              //                       //
         ein=1;                 // Ensuite, on rote      //
      envoi_donnees(ein);       // "rot" à gauche pour   //
#asm                            // passer au  bit        //
      rlf rot,f                 // suivant.              //
      bcf rot,0                 //                       //
#endasm                         // Ainsi, on passe les   //
      compteur--;               // huit bits de l'octet  //
   }                            // un à un du plus       //
////////////////////////////////// lourd au plus léger.  //
                                                         //
///////////////////////// A la fin de chaque octet, le   //
   ack=lire_donnees(); // maître attend une confirmation //
   if(ack==1)          // (ACK) de la part du EEPROM.    //
      aff_err(6);      // Pour envoyer son "ACK",        //
///////////////////////// l'esclave (EEPROM) TIRE la     //
// ligne SDA vers le bas. Le maître laisse aller la      //
// ligne SDA et vérifie si le EEPROM la maintient à LOW. //
}                                                        //
///////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////
void envoi_donnees(int1 donnee)                               //
{                                                             //
   int1 valide=0;                                             //
   int8 essais;                                               //
   /*=-=*//*=-=*//*=-=*//*=-=*//*=-=*//*=-=*//*=-=*//*=-=*/   //
////////////////////////////////                              //
   essais=0;                  //                              //
   while(valide==0)           //                              //
   {                          // On descend SCL tout en       //
      set_tris_a(0b11111110); // laissant SDA aller           //
      delay_us(4);            // librement à HI...            //
////////////////////////////////                              //
                                                              //
///////////////////////////// Le maître  vérifie que le       // Envoi d'un
      if(!input_state(41)) // EEPROM laisse SDA aller à Hi.   // bit
         aff_err(11);      // Si le EEPROM maintient SDA      // sur le
                           // à LO, c'est qu'il a             // port I2C
                           // envoyé un ACK inattendu.        //
/////////////////////////////                                 //
                                                              //
////////////////////////////////////                          //
      if(donnee==1)               // on                       //
         set_tris_a(0b11111110);  // relâche                  //
      else                        //   ou                     //
         set_tris_a(0b11111100);  // maintient                //
                                  // SDA selon                //
                                  // la donnée                //
      delay_us(4);                // reçue                    //
////////////////////////////////////                          //
                                                              //
////////////////////////////////                              //
      if(donnee)              //                              //
      {                       //                              //
         if(input_state(41))  // Vérification que la ligne    //
            valide=1;         // SDA suive vien la donnée.    //
         else                 //                              //
         {                    //                              //
            aff_err(2);       //                              //
            essais++;         //                              //
            if(essais>7)      //                              //
               valide=1;      //                              //
         }                    //                              //
      }                       //                              //
      else                    //                              //
      {                       //                              //
         if(!input_state(41)) //                              //
            valide=1;         //                              //
         else                 //                              //
         {                    //                              //
            aff_err(3);       //                              //
            essais++;         //                              //
            if(essais>7)      //                              //
               valide=1;      //                              //
         }                    //                              //
      }                       //                              //
                              //                              //
   }                          //                              //
////////////////////////////////                              //
                                                              //
////////////////////////////////////                          //
   essais=0;                      //                          //
   valide=0;                      //                          //
   while(valide==0)               //                          //
   {                              //                          //
      if(donnee)                  // on                       //
         set_tris_a(0b11111111);  // relâche                  //
      else                        // SCL...                   //
         set_tris_a(0b11111101);  //                          //
                                  //                          //
      delay_us(4);                //                          //
                                  //                          //
      if(input_state(40))         // Vérification que SCL     //
         valide=1;                // est bien relâché         //
      else                        //                          //
      {                           //                          //
         aff_err(4);              //                          //
         essais++;                //                          //
         if(essais>7)             //                          //
            valide=1;             //                          //
      }                           //                          //
   }                              //                          //
////////////////////////////////////                          //
                                                              //
///////////////////////////////////                           //
   essais=0;                     //                           //
   valide=0;                     //                           //
   while(valide==0)              //                           //
   {                             //                           //
      if(donnee)                 // ...et on reprend SCL à    //
         set_tris_a(0b11111110); // LO.                       //
      else                       //                           //
         set_tris_a(0b11111100); //                           //
                                 //                           //
      delay_us(4);               //                           //
                                 //                           //
      if(!input_state(40))       // ...et on vérifie que SCL  //
         valide=1;               // soit bien à LO.          //
      else                       //                         //
      {                          //                        //
         aff_err(3);            //                        //
         essais++;             //                        //
         if(essais>7)         //                        //
            valide=1;        //                        //
      }                     //                        //
   }                       //                        //
                          //                        //
///////////////////////////                        //
   delay_us(4);                                   //
}                                                //
//////////////////////////////////////////////////

////////////////////////////////////////////////////////////
int1 lire_donnees(void)                                   //
{                                                         //
   int1 lecture;                                          //
   int1 valide=0;                                         //
   int8 essais;                                           //
   /*=-=*//*=-=*//*=-=*//*=-=*//*=-=*/                    //
////////////////////////////////                          //
      set_tris_a(0b11111110); // On laisse aller SDA      //
      delay_us(4);            // où qu'il veut...         //
////////////////////////////////                          //
                                                          //
////////////////////////////////                          //
   essais=0;                  //                          //
   valide=0;                  //                          //
   while(valide==0)           //                          //
   {                          // ...et on relâche SCL     //
      set_tris_a(0b11111111); // vers Hi.                 // Réception
                              //                          // d'un
      delay_us(4);            // délai                    // bit sur
                              //                          // le port
      if(input_state(40))     // Vérification que SCL     // I2C
         valide=1;            // soit bien à Hi.          //
      else                    //                          //
      {                       //                          //
         aff_err(5);          //                          //
         essais++;            //                          //
         if(essais>7)         //                          //
            valide=1;         //                          //
      }                       //                          //
   }                          //                          //
////////////////////////////////                          //
                                                          //
/////////////////////////                                 //
   lecture=0;          //                                 //
   if(input_state(41)) // On lit la ligne SDA...          //
      lecture=1;       //                                 //
                       // ...et on sauvegarde dans le     //
   delay_us(4);        // booléen "lecture"               //
/////////////////////////                                 //
                                                          //
////////////////////////////////                          //
   essais=0;                  //                          //
   valide=0;                  //                         //
   while(valide==0)           // ...et on redescend     //
   {                          // SCL à ground.         //
      set_tris_a(0b11111110); //                      //
                              //                     //
      delay_us(4);            //                    //
                              //                   //
      if(!input_state(40))    // On vérifie que   //
         valide=1;            // SCL soit bien   //
      else                    // à LO.          //
      {                       //               //
         aff_err(5);          //              //
         essais++;            //             //
         if(essais>7)         //            //
            valide=1;         //           //
      }                       //          //
   }                          //         //
////////////////////////////////        //
   delay_us(4);                        //
   return(lecture);                   //
}                                    //
//////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
void aff_err(int8 erreur)                                                    //
{                                                                            //
   if(erreur!=13)                                                            //
      printf("\n\rErreur # %u: ",erreur);                                    //
   if(erreur==0)                                                             //
      printf("SDA pas descendu dans S");                                     //
   if(erreur==1)                                                             //
      printf("SCL pas descendu dans S ou pas remonte dans P");               //
   if(erreur==2)                                                             //
      printf("SDA ne suit pas DATA a HI dans envoi vers EEPROM");            //
   if(erreur==3)                                                             //
      printf("SDA ne suit pas DATA a LOW dans envoi vers EEPROM");           //
   if(erreur==4)                                                             //
      printf("SCL pas relache dans envoi de donnees vers EEPROM");           //
   if(erreur==5)                                                             //
      printf("SCL ne suit pas lors de lecture de donnees/ACK du EEPROM");    //
   if(erreur==6)                                                            //
      printf("NAK!");                                                      //
   if(erreur==7)                                                          //
      printf("Lignes SCL et SDA pas a HI avant S");                      //
   if(erreur==8)                                                        //
      printf("Lignes SCL pas a HI a la fin de P");                     //
   if(erreur==9)                                                      //
      printf("Lignes SDA pas a HI a la fin de P");                   //
   if(erreur==10)                                                   //
      printf("Lignes SDA pas remonte dans P");                     //
   if(erreur==11)                                                 // Messages
      printf("ACK inattendu!!");                                 // d'erreur
   if(erreur==12)                                               //
      printf("Limite de temps d'ecriture depassee");           //
   if(erreur==13)                                             //
      printf(" Donnee non ecrite (WP ?)");                   //
   delay_ms(100);                                           //
}                                                          //
////////////////////////////////////////////////////////////

////////////////////////////////////
void rad(void)                    //
{                                 //
   int8 c2;                       //
                                  //
#asm                              //
                                  //
   movlw 8 // huit fois pour      //
   movwf c2 //avoir un octet      //
                                  //
boucle2:                          //
                                  // LFSR pour
   bsf   y,0  // impair           // générer
                                  // des données
   btfsc x0,5 // Porte XOR à      // pseudo-aléatoires.
   incf  y    // plusieurs        //
   btfsc x1,6 // entrées          //
   incf  y    // (le INCF agit    //
   btfsc x2,4 // comme un XOR     //
   incf  y    // auprès du bit 0  //
   btfsc x3,7 // de "y".)         //
   incf  y    //                  //
   btfsc x4,2 //                  //
   incf  y    //                  //
   btfsc x5,3 //                  //
   incf  y    //                  //
   btfsc x6,4 //                  //
   incf  y    //                  //
   btfsc x7,7 //                  //
   incf  y    //                  //
              //                  //
              //                  //
                                  //
                                  //
   rlf   x0,f  // On rote         // LFSR
   rlf   x1,f  // à gauche!       //
   rlf   x2,f  //                 //
   rlf   x3,f  //                 //
   rlf   x4,f  //                 //
   rlf   x5,f  //                 //
   rlf   x6,f  //                 //
   rlf   x7,f  //                 //
                                  //
   bsf   x0,0  // En attendant    //
   btfss y,0   // que je sache    //
   bcf   x0,0  // comment accéder //
               // au STATUS!!     //
                                  //
   decfsz c2,f                    //
   goto boucle2                   //
                                  //
   movf  x0,w                     //
   movwf hasard                   //
#endasm                           //
   return;                        //
                                  //
}                                 //
////////////////////////////////////
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

PostPosted: Mon Mar 09, 2009 2:03 am     Reply with quote

Found the culprit. It’s the timing of the stop condition.

My slave behavior is slightly different from that in the ex_slave.c. The latter precesses every byte inside the ISR. My code buffers the incoming data and processes the complete packet at the end of the I2C transaction – after the stop condition. On the slave side I check the P bit in the SSPSTAT register and set a global flag (which later is examined in the main() loop)

Code:
#use i2c(Slave, sda=PIN_C4, scl=PIN_C3, address=0xB0, FORCE_HW, NO_STRETCH)
   // ...
#bit  SSPSTAT_P   = 0xFC7.4 //SSPSTAT.4       // stop bit in the MSSP status register
   // ...

#INT_SSP
void ssp_interupt()
{
   // ...
            if (SSPSTAT_P)
         {
            g_iI2Cflags = (HAVE_UNPROCESSED_CMD | IDLE);  /* If this is the end of a
                    read transaction - we have a new command.  Even though we have
                    responded to the command, it can still be interpreted outside ISR. */
         }
   // ...
}


It turned out that if the timing of the stop condition is wrong, I don’t see the flag in the slave ISR.

Code:
HZ       // S_OK, if no errors.  HZ describing the timeout otherwise
i2c_safe_stop()
// PURPOSE:          Issue a stop condition for the I2C transaction
{
   HZ hzErr;   // for error code return
   
   output_low(SDA_PIN);    // make sure SDA is drivel low (e.g. after NACK)
   hzErr = wait_stretch(); /* Since the master will not be sending more bytes,
      it doesn't need to wait for the end of clock stretching.  However, this is still an
      opportunity to check if the slave is stuck. */
   delay_us(2);            /* Notice a shorter delay (effectively) between
      _low(SDA) and _high(SDA) with a longer delay. 
      SSPSTAT_P doesn't get set on the slave side if the delay is longer. 
      Yes-yes, I know that "programming with delays" is leads to "brittle" code.  FIXME.*/
   output_float(SDA_PIN);  delay_us(g_i_SCL_HI_US);
   
   return hzErr;
}


I don’t feel comfortable knowing that I have this race condition, although it wasn’t causing problems with the compiler-generated bit-banging code. I probably shouldn’t be checking the stop condition flag in the ISR. But where can I check it?

- Nick
_________________
Read the label, before opening a can of worms.
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Mon Mar 09, 2009 2:29 am     Reply with quote

Quote:
It turned out that if the timing of the stop condition is wrong, I don’t see the flag in the slave ISR.
I wonder, what's a wrong stop condition timing in terms of the I2C specification?
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