|
|
View previous topic :: View next topic |
Author |
Message |
kender
Joined: 09 Aug 2004 Posts: 768 Location: Silicon Valley
|
"hand written" software I2C driver |
Posted: Thu Mar 05, 2009 9:24 pm |
|
|
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
|
|
|
bsturm
Joined: 23 Feb 2009 Posts: 29
|
|
Posted: Fri Mar 06, 2009 7:20 am |
|
|
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
|
|
Posted: Fri Mar 06, 2009 3:01 pm |
|
|
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
|
|
Posted: Mon Mar 09, 2009 2:03 am |
|
|
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
|
|
Posted: Mon Mar 09, 2009 2:29 am |
|
|
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? |
|
|
|
|
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
|