Utilisation de l'I2C sur des PIC série 16F

Base de données sur les questions les plus courantes / Savoir définir une résistance avant des Led / Utilitaires de calcul et schéma automatique.
AmadeusHF
Rang "3 LEDs"
Rang "3 LEDs"
Messages : 127
Enregistré le : 03 août 2009, 09:18

Utilisation de l'I2C sur des PIC série 16F

Message non lu par AmadeusHF » 04 sept. 2009, 12:30

Je vous propose ce petit exposé, pour vous faire partager le résultat de mes travaux en ce qui concerne l'utilisation de l'I2C sur des pics de la série 16F, et en particulier le 16F690.

Le bus I2C

Petit rappel sur ce dont on parle !

Le bus I2C (Inter Integrated Circuit Bus) est une norme (issue de Philips) numérique qui permet de faire "dialoguer" des composants de type circuits intégrés : processeurs, mémoire, afficheur....une large gamme d'équipements sont aujourd'hui "de type I2C".
Ce bus est de type "bus série" à deux fils : c'est un bus car il utilise deux fils, c'est une transmission sérielle car tous les bits sont transmis sur un seul fil.

On a donc un fil pour la DATA (appelé SDA) et un fil pour l'HORLOGE (appelé SCK).

Ce bus est de type maitre / esclave : un des composants pilote les autres, dirige les opérations (le maitre), les esclaves se contentant de 'répondre présent' quant on les sollicite. En particulier, le maitre est responsable de la génération de l'unique horloge, ainsi que du contrôle des transactions dans leur ensemble.

C'est un bus utilisé en mode "question / réponse" : le maitre ouvre une transaction désignant un esclave, lui envoi une commande, et attend immédiatement une réponse (si données demandées) ou tout au moins un acquitement de la prise en compte de la commande.

C'est un bus qui peut accueillir plusieurs composants en parallèle, chacun (les esclaves) disposant d'une ADRESSE (sur 7 bits, donc 128 possibilités).

Enfin c'est un bus qui peut avoir une longueur variable, mais qui est contraint par des charactéristiques techniques précises. Sa vitesse maximale de transmission (horloge variable, pouvant aller jusqu'à 100 Khz) et la capacitance maxi définie par la norme (400 pF environ) permettent de s'adapter à pas mal de situations...sans pour autant permettre tout et n'importe quoi ! Il existe des déclinaisons plus récentes qui autorisent l'horloge à monter à 400 Khz, et l'adressage à être étendu à 10 bits.


L'I2C pour faire quoi ?

Dans mon projet, je dois mettre en relation 5 micro-controleurs, l'un pilotant les 4 autres. Pourquoi autant ? Parce que chaque esclave se charge de gérer un équipement autonome, placé à 3 mètres d'une centrale de pilotage. Cette dernière gère la cohérence de l'ensemble et une programmation (voir le post à ce sujet dans les réalisations).

L'utilisation d'un PIC comme ceux d'une série 24 peut aussi amener le besoin de l'I2C : ces pics n'ont pas d'eeprom, et il faut leur en adjoindre une externe...via I2C !


Est-ce que les PIC sont tous pareils pour utiliser I2C ?

NON !

Les petits modèles (début de la série 16F notamment) ne peuvent pas directement être utilisés pour cela...bien qu'en fait cela reste possible via 2 ports que l'on programmerai enterrement à la main...mais dans ce cas on a pas d'assistance du processeur.

Dans les modèles plus gros, on a tout d'abord une grande partie de la gamme qui dispose d'un port SSP. Ce port permet d'utiliser le bus I2C mais, dans ce cas précis, il n'assiste réellement le programmeur que pour un fonctionnement esclave. La mise en oeuvre d'un mode maitre reste possible, mais il faut se le faire à la main !

Pour pouvoir avoir une gestion hardware complète d'I2C, il faut un PIC qui dispose d'un port MSSP minimum, comme par exemple la plupart des PICs de la série 18F.

Reste que la mise en œuvre du mode maitre n'est pas si compliqué que cela...mais est artisanale et imprécise en l'absence d'un port MSSP (mais elle marche !)


Mise en oeuvre du mode ESCLAVE (pic 16F690)

Le 16F690 utilise deux entrées du port B pour gérer l'I2C : B4 et B6.

Ces deux pins doivent recevoir une configuration particulière pour gérer les signaux : il faut les mettre en mode "INPUT", sans aucune option. A NOTER : le PIC, lors du reset, active par défaut tous les convertisseurs analogiques / numériques. Le 16F690 en comporte pas moins de 12, et le port B4 est justement...le douxième ! Il faut donc penser à désactiver les convertisseurs sinon B4 restera inopérant malgré l'activation du module SSP (j'ai cherché longtemps avant de trouver ce point de détail !)

En C (compilateur HiTec C), ça se traduit comme cela :

Code : Tout sélectionner

// Coupure potentielle des interruptions sur changement de A et B
RABIE = 0;
RABIF = 0;
// Configuration du port A en input, par défaut
PORTA = 0;
TRISA = 0;
// Désactivation des convertisseurs A/N
ADCON0 = 0;
ADCON1 = 0;
ANSEL = 0;
ANSELH = 0;
// Configuration du port B en input sur B4 et B6
PORTB = 0;
IOCB = 0;
WPUB = 0;
TRISB = 0b01010000;
Ensuite il faut configurer le module SSP en mode I2C esclave. Je prend ici le mode 7 bits d'adressage sans interruption sur STOP / START. Ca donne ça :

Code : Tout sélectionner

#define SLAVE_ADDRESS      0x20

// Désactive le SSP et reset les registres
SSPEN = 0;
SSPSTAT = 0;
SSPBUF = 0;
SSPOV = 0;
BF = 0;
// Active le mode 7 bits esclave sans interruption START / STOP
SSPCON = 0b00000110;
CKP = 1;
// Défini l'adresse du SLAVE
SSPADD = SLAVE_ADDRESS << 1;
// Active le module SSP
SSPEN = 1;
Une fois qu'on a fait ça, l'esclave doit être en mesure de recevoir des datas. A noter le fait qu'on décale l'adresse d'un bit dans le registre...mais ça dépend aussi de "comment le maitre est codé". Je veux dire par là que certains codes de pilotage considère l'adressage sur 8 bits, avec une adresse READ et une adresse WRITE pour un même chip...alors qu'en fait l'adresse est sur 7 bits mais qu'un bit est transmis AVEC l'adresse pour indiquer le sens du transfert...ce qui donne "un peu comme une double adresse sur 8 bits".

Pour recevoir les données, le plus simple c'est d'utiliser l'interruption du port SSP. Exemple d'activation de cette interruption :

Code : Tout sélectionner

SSPIF = 0;
SSPIE = 1;
PEIE = 1;
ei();
Dès lors, on va recevoir des interruptions quand un octet est présent dans le flux, si l'adresse de transfert correspond à la notre :

Code : Tout sélectionner

void interrupt monInterruption(void)
{
   if ((SSPIE) && (SSPIF))
   {
      if (BF)
      {
         // Ici, on a reçu un octet, peu importe lequel
         unsigned char value = SSPBUF;
         BF = 0;

         // Adresse ou donnée ?
         if (DA)
         {
            // Octet de données...le traiter !
            if (!RW)
            {
               // C'est une écriture, donc on RECOIT des octets depuis le master
            }
            else
            {
               // C'est une lecture...on va devoir envoyer des données au master
            }
         }
         else
         {
            // Octet d'adresse...normalement c'est NOTRE propre adresse, sauf si on a mis un masque qui autorise un ensemble d'adresses
         }
      }
   }
}
A noter dans ce code l'importance de lire systématiquement le registre SSPBUF quand le bit BF (buffer full) est positionné, car sans cela, le hardware se met en erreur. Donc même si on n'utilise pas la donnée, il faut lire SSPBUF. Si on ne le fait pas, à la transmission de l'octet suivant par le master, le hardware va se placer en overload (SSPOV). Il déclenche une interruption mais on perd un octet...et le port SSP s'arrete. Il faut le reconfigurer / relancer le dialogue. C'est une situation d'erreur manifeste.

Utilisation de l'I2C sur des PIC série 16F

Publicité

Publicité
 

AmadeusHF
Rang "3 LEDs"
Rang "3 LEDs"
Messages : 127
Enregistré le : 03 août 2009, 09:18

Re: Utilisation de l'I2C sur des PIC série 16F

Message non lu par AmadeusHF » 04 sept. 2009, 21:21

Je continu mes explications...

Voyons maintenant les pièges concernant la partie MASTER !

Comme je l'ai indiqué ci-dessus, un 16F690 ne sait pas nativement gérer une transmission en tant que master. Il faut pour cela un port MSSP, qui est piloté par une horloge et se charge de l'I2C niveau hardware.

Pour le faire en software, il faut commencer par ... câbler correctement tout ça !

Le bus nécessite de placer au niveau logique HAUT les deux lignes lorsqu'elles sont au repos. Pour cela, il faut disposer sur chaque ligne une résistance PULL-UP dont la valeur oscille entre 1.5 kOhms et 10 kOhms, en fonction des résultats recherchés. Plus la distance sera grande, plus il faut privilégier le transfert d'énergie et donc baisser la valeur de la résistance (augmenter l'intensité du courant lorsque la ligne commute).

Ceci étant fait, il faut ensuite configurer les ports, un peu comme pour le mode SLAVE. La différence principale réside dans le contenu du registre SSPCON qui reçoit une valeur (mode I2C) différente : mode maitre software SANS mode esclave (pas de multi-master), dans notre cas :

Code : Tout sélectionner

//
	// Disable all analog converter operations
	//

	ADCON0 = 0;
	ADCON1 = 0;
	ANSEL = 0;
	ANSELH = 0;

	//
	// Setup port A as full input
	//

	PORTA = 0;
	TRISA = 0xFF;

	//
	// Enable port B bits 4 and 6 for input, for I2C operations
	//

	RABIE = 0;
	RABIF = 0;
	IOCB = 0x00;
	WPUB = 0x00;
	PORTB = 0x00;
	TRISB = 0xFF;

	//
	// Now enable I2C bus
	//

	SSPEN = 0;
	SSPCON = 0;
	SSPSTAT = 0;
	SSPBUF = 0;
	BF = 0;
	SSPOV = 0;

	SSPCON = 0b00001011;
	CKP = 1;
	SSPEN = 1;
Noter que, dans ce mode en particulier, on a pas réellement besoin d'utiliser des interruptions puisqu'on va généralement piloter le bus de manière synchrone...reste que ça peut être utile si le PIC communique avec autre chose en parallèle (par le port série par exemple).

Une fois tout cela fait, on a besoin d'une librairie de fonctions pour implémenter le mode MASTER.

On trouve pas mal d'exemples sur le NET...mais généralement conçus pour des PICs plus complets. Le problème c'est que les PICs de type 16F ont une pile maximale de 8 niveau (hardware), et ces librairies de fonctions ont tendance à utiliser trop de niveaux d'appels.

Pour cette raison, je me suis efforcé d'en constituer une version réduisant le nombre d'appels. Idéalement, je l'avais fait très poussée, avec des DEFINE qui transformaient les principales opérations "low level" en macros, et donc alongeaient le code mais réduisaient le nombre d'appels...sauf que le compilateur Hitec semble passablement limité et que rien de tout cela n'était correctement compilé (en apparence oui, mais le code ne marchait pas) alors que tout était parfaitement bien codé. Je suis donc revenu en arrière (tout n'est pas optimal) tout en dupliquant le code pour la gestion des délais...de sorte que les appels imbriqués soient eux aussi limités.

A NOTER : ce code est "conçu" pour tourner à 8 Mhz...si la fréquence du PIC est différente, (par exemple les 4 Mhz de défaut), il faut modifier la macro DELAY_LNG pour qu'elle calcule le bon nombre de NOP à faire pour une microseconde.

Comment savoir ?

Le pic divise la fréquence d'horloge par 4 (FOSC / 4) pour définir la fréquence de base du CPU. Donc à 8 Mhz, un cycle est en fait équivalent à 2 Mhz. Donc pour "perdre" une microseconde, il faut perdre 2 cycles. Comme une passe de la boucle NOP prend environ 4 cycles, on fait une pause de 2 µs par boucle. On peut donc rapidement voir qu'il faut N / 2 boucles pour faire une pause de N µs. Il faut donc modifier la division (le décalage à droite de 1 = division par deux) si la fréquence est différente..Et bien sur on perd alors en précision. A 4 Mhz, il faut diviser par 4 (>> 2), etc...

Voici le code de la lib que j'utilise pour mes tests :

Code : Tout sélectionner

#include "ahf_i2c.h"

//================================================================================
//
// Bus delay specifications (in µSec)
//
//================================================================================

#define I2CSTARTDELAY 	50
#define I2CSTOPDELAY  	50
#define I2CDATASETTLE 	20
#define I2CCLOCKHIGH  	100
#define I2CHALFCLOCK  	50
#define I2CCLOCKLOW   	100
#define I2CACKWAITMIN 	50

//================================================================================
//
// Define some internal tools depending on the target architecture
//
//================================================================================

#define I2CLOW  0
#define I2CHIGH 1

#define DELAY_LNG(A) (A>>1)

//--------------------------------------------------------------------------------
// PIC 16F690
//--------------------------------------------------------------------------------

#ifdef _16F690

#define SCL	RB6
#define SCL_TRIS	TRISB6
#define SDA	RB4
#define SDA_TRIS	TRISB4

#endif

//================================================================================
//
// Main low level operations
//
//================================================================================

//--------------------------------------------------------------------------------
// I2CSTART : Start a transmition on the bus (master operation)
//--------------------------------------------------------------------------------

static void i2cStart()
{
 	// 
	// Ensure both clock and data are at pull-up level, for a minimal time
	//

 	SDA_TRIS = I2CHIGH;
 	SCL_TRIS = I2CHIGH;
	for (int _DC_ = DELAY_LNG(I2CSTARTDELAY);_DC_ > 0;_DC_--){ NOP();}

 	// 
	// Generate the start condition
	//

 	SDA_TRIS = I2CLOW;
	for (int _DC_ = DELAY_LNG(I2CSTARTDELAY);_DC_ > 0;_DC_--){ NOP();}

 	SCL_TRIS = I2CLOW;
	for (int _DC_ = DELAY_LNG(I2CCLOCKLOW);_DC_ > 0;_DC_--){ NOP();}
}

//--------------------------------------------------------------------------------
// I2CSTOP : Stop an bus operation (master operation)
//--------------------------------------------------------------------------------

static void i2cStop()
{
 	SDA_TRIS = I2CLOW;
 	SCL_TRIS = I2CHIGH;
	for (int _DC_ = DELAY_LNG(I2CSTOPDELAY);_DC_ > 0;_DC_--){NOP();}

 	SDA_TRIS = I2CHIGH;
}

//--------------------------------------------------------------------------------
// I2CCLOCK : Make the clock go ahead (one step) for data transmission
//--------------------------------------------------------------------------------

static void i2cClock()
{
	//
	// Ensure any previous clock level is long enough
	//

	for (int _DC_ = DELAY_LNG(I2CDATASETTLE);_DC_ > 0;_DC_--){NOP();}

	//
	// Switch clock to HIGH (release the line)
	//

 	SCL_TRIS = I2CHIGH;
	for (int _DC_ = DELAY_LNG(I2CCLOCKHIGH);_DC_ > 0;_DC_--){NOP();}

	//
	// Now go back to LOW
	//

 	SCL_TRIS = I2CLOW;
	for (int _DC_ = DELAY_LNG(I2CCLOCKLOW);_DC_ > 0;_DC_--){NOP();}
}

//--------------------------------------------------------------------------------
// I2CSENDACK : Send the ACK bit to the slave. Must be done for all bytes but the
// last in a whole sequence.
//--------------------------------------------------------------------------------

static void i2cSendAck()
{
	//
	// Give the other device a little time to prepare
	//

 	SDA_TRIS = I2CLOW;
	for (int _DC_ = DELAY_LNG(I2CDATASETTLE);_DC_ > 0;_DC_--){NOP();}

	// 
	// Do one clock pulse
	//

 	i2cClock();

	//
	// Release ACK line
	//

 	SDA_TRIS = I2CHIGH;
	for (int _DC_ = DELAY_LNG(I2CDATASETTLE);_DC_ > 0;_DC_--){NOP();}
}

//================================================================================
//
// HIGH LEVEL PUBLIC OPERATIONS
//
//================================================================================

//--------------------------------------------------------------------------------
// i2cGetAck : Read the ACK bit from the slave
//--------------------------------------------------------------------------------

unsigned char i2cGetAck(void)
{
	//
	// Prepare bus lines, ensuring data line state is released
	//

 	SDA_TRIS = I2CHIGH;

	//
	// Switch clock to HIGH and wait for half a pulse length
	//

 	SCL_TRIS = I2CHIGH;
	for (int _DC_ = DELAY_LNG(I2CHALFCLOCK);_DC_ > 0;_DC_--){NOP();}

	//
	// Sample the ACK signal
	//

 	if (SDA)
 	{
		//
		//  No ACK, protocol failure !
		//

  		return 0;
 	}

	//
	// Finalize the clock full cycle
	//

	for (int _DC_ = DELAY_LNG(I2CHALFCLOCK);_DC_ > 0;_DC_--){NOP();}

 	SCL_TRIS = I2CLOW;
	for (int _DC_ = DELAY_LNG(I2CCLOCKLOW);_DC_ > 0;_DC_--){NOP();}

 	return 1;
}

//--------------------------------------------------------------------------------
// i2cReadBit : Read a single bit (data) from the slave
//--------------------------------------------------------------------------------

unsigned char i2cReadBit(void)
{
	unsigned char buffer = 0;

	//
	// Let opposite device prepare the bit
	//

	for (int _DC_ = DELAY_LNG(I2CDATASETTLE);_DC_ > 0;_DC_--){NOP();}
    
	//
	// Start the clock cycle and wait for half its length
	//

 	SCL_TRIS = I2CHIGH; 
	for (int _DC_ = DELAY_LNG(I2CHALFCLOCK);_DC_ > 0;_DC_--){NOP();}

	//
	// READ in the data bit
	//

 	if (SDA !=0 ) 
	{
		buffer = 1;
	}

	//
	// Wait for half a clock cycle
	//

	for (int _DC_ = DELAY_LNG(I2CHALFCLOCK);_DC_ > 0;_DC_--){NOP();}

	//
	// finalize the clock cycle
	//

 	SCL_TRIS = I2CLOW;    
	for (int _DC_ = DELAY_LNG(I2CCLOCKLOW);_DC_ > 0;_DC_--){NOP();}

 	return buffer;
}

//--------------------------------------------------------------------------------
// i2cSendByte : Send a single byte to the slave
//--------------------------------------------------------------------------------

char i2cSendByte(const unsigned char value)
{
	char count;

 	//SDA = I2CLOW;
 	//SCL = I2CLOW;

	//
	// Minimum Clock Low Time
	//

	for (int _DC_ = DELAY_LNG(I2CCLOCKLOW);_DC_ > 0;_DC_--){NOP();}

	//
	// Send all bits
	//

	unsigned char buffer = value;

 	for (count=0;count<8;count++)
 	{
  		if ((buffer & 0x80)==0)
  		{
   			SDA_TRIS = I2CLOW;
  		}
  		else
  		{
   			SDA_TRIS = I2CHIGH;
  		}
		
		//
		// Go to next bit
		//

  		buffer = buffer<<1;

		//
		// Pulse clock (transmit the bit)
		//

  		i2cClock();
 	}

	//
	// Release data pin for ACK
	//

	SDA_TRIS = I2CLOW;

 	return 1;
}

//--------------------------------------------------------------------------------
// i2cReadByte : Read a single byte from the slave
//--------------------------------------------------------------------------------

unsigned char i2cGetByte(void)
{
	//
	// Minimum Clock Low Time
	//

	for (int _DC_ = DELAY_LNG(I2CCLOCKLOW);_DC_ > 0;_DC_--){NOP();}

	unsigned char buffer = 0;

	//
	// Read all 8 bits
	//

 	for (int count=0;count<8;count++)
 	{
  		buffer = buffer<<1;

		//
		// Release SDA so that the bit is recieved
		//

  		SDA_TRIS = I2CHIGH;            

  		if (i2cReadBit())
  		{
   			buffer |= 1;
  		}
 	}

 	return buffer;
}

//--------------------------------------------------------------------------------
// i2cSendBytes : Send a whole sequence of bytes
//--------------------------------------------------------------------------------

char i2cSendBytes(const unsigned char address, const unsigned char *data, const unsigned char count)
{
	i2cStart();

 	//
	// Send the address. BIT 0 is always set to 0 for a WRITE operation
	//

 	i2cSendByte((address & 0x7F) << 1);

 	if (!i2cGetAck())
 	{
  		i2cStop();
  		return(0);
 	}

	int index = 0;
 	while(index < count)
 	{
  		i2cSendByte(data[index++]);

  		if (!i2cGetAck())
  		{
   			i2cStop();
   			return 0;
  		}
 	}

 	i2cStop();
 	return 1;
}

//--------------------------------------------------------------------------------
// i2cReadBytes : Read a sequence of bytes from the slave
//--------------------------------------------------------------------------------

char i2cReadBytes(const unsigned char address,unsigned char *data,const unsigned char count)
{
	i2cStart();

 	//
	// Send the address. Ensure BIT 0 is set to 1 (READ)
	//

	i2cSendByte(((address & 0x7F) << 1) | 0x01);

 	if (!i2cGetAck())
 	{
  		i2cStop();
  		return 0;
 	}

	unsigned char index = 0;
 	while(index < count)
 	{
  		data[index++] = i2cReadByte();

  		if(index < count)
  		{
   			i2cSendAck();
  		}
 	}

 	i2cStop();

 	return 1;
}
Ce fichier .c appele un fichier inclu ahf_i2c.h qui contient quelques bricoles pour le code final d'utilisation (prototypes notamment) :

Code : Tout sélectionner

#ifndef _AHF_I2C_INCLUDED_
#define _AHF_I2C_INCLUDED_

#include <htc.h>

extern unsigned char i2cGetAck(void);
extern unsigned char i2cReadBit(void);

extern char i2cSendByte(const unsigned char value);
extern unsigned char i2cReadByte(void);

extern char i2cSendBytes(const unsigned char address,const unsigned char *data,const unsigned char count);
extern char i2cReadBytes(const unsigned char address,unsigned char *data,const unsigned char count);

#endif
et voici ce que ça donne pour envoyer une séquence d'octets à l'esclave depuis un programme maitre (on peut difficilement faire plus simple) :

Code : Tout sélectionner

unsigned byte content[2] = { 0 , 1 };
int d = 1;
int v = 1;

while (1)
	{
		//
		// Envoi une valeur oscillant de 1 à 20, avec une pause entre chaque envoi. On émet aussi un octet contenant "0" avant la valeur...pour le fun.
               // Une LED est branchée sur C1 pour visualiser le "hit" quand on émet...pour le debug et le fun !
		//
		
		for (int i=0;i<1000;i++)
		{
			_delay(2000);
		}

		PORTC = 1;

		content[1] = v;

		i2cSendBytes(0x20,content,2);

		v+=d;
		if (v == 20)
			d = -1;
		if (v == 1)
			d = +1;
		
		PORTC = 0;
	}

Termi87
Administrateur
Administrateur
Messages : 3628
Enregistré le : 09 juil. 2006, 21:20
Localisation : Limousin

Re: Utilisation de l'I2C sur des PIC série 16F

Message non lu par Termi87 » 04 sept. 2009, 21:50

Euhh :), alors là, super boulot, et super initiative ;), je te félicite. Tu à posté cela exactement là où il faut. Merci. J'ai pas lu encore :D (j'ai vraiment très peut le temps de me consacrer au forum... en rentrant tout les soirs à 20h30 ^^... mais je suis en vacances 3 semaines dans 1 jours : D)
Merci

jC_Omega
Rang "6 LEDs"
Rang "6 LEDs"
Messages : 904
Enregistré le : 10 août 2008, 09:45
Contact :

Re: Utilisation de l'I2C sur des PIC série 16F

Message non lu par jC_Omega » 05 sept. 2009, 19:27

merci bcp, j'ajoute ca direct a mes favories

je vais essayer de trouver un peu de temps pour refaire des tests
Mes projets perso :
Image

orpheedulogis
Rang "1 LEDs"
Rang "1 LEDs"
Messages : 30
Enregistré le : 13 déc. 2010, 01:37

Re: Utilisation de l'I2C sur des PIC série 16F

Message non lu par orpheedulogis » 13 déc. 2010, 01:47

Bonjour

Excellente initiative que de s'intéresser au PIC16F690 en maître/esclave. J'ai eu beau chercher j'ai pas encore trouvé "le truc"

Pour moi, le gros avantage est que cela permettrait de réaliser un composant I2C pour alléger le travail du microprocesseur général.
En l'occurrence, il s'agirait ici de réaliser un compteur de vitesse I2C : un capteur hall qui envoie des impulsions, le PIC esclave qui détermine la vitesse et est interrogé ultérieurement par un autre PIC plus "costaud".

Malheureusement j'ai essayé de reproduire ce qui était écrit ici en Mikrobasic mais ça ne fonctionne pas pour l'instant. Seul le code de l'esclave pose problème (le PIC maitre envoie bien les commandes en I2C, je l'ai vérifié en plaçant -sur simulateur Proteus- une horloge I2C et un afficheur Lcd )
Une âme généreuse pourrait-elle me dire ce qui ne va pas ? merci.
Pour l'instant je ne veux que vérifier que l'esclave reconnait une commande du maître (clignotement de LED)

Code : Tout sélectionner

program slave
'SDA=RB4
'SCL=RB6

symbol LedCtrl = PORTC.6

' Declarations section 
const AdressePic16F690=%11110000 'adresse I2C (7 bits + RW)

dim x1,y1 as byte
dim FlagTransferer as boolean

'------------------------------------------------------------

sub procedure ClignoterLedCtrl( dim Nbre as byte)
dim xcl as byte

for xcl=1 to Nbre
    if LedCtrl=1 then Ledctrl=0 else LedCtrl=1 end if
    delay_ms(300)
next xcl

end sub


'------------------------------------------------------------

sub procedure interrupt
  if SSPIE and SSPIF=1 then
     SSPBUF.BF=0
     if SSPSTAT.D=1 then '(1= Data, 0= adresse)
        if SSPSTAT.R=1 then 'reçoit
            FlagTransferer=true
        else ' écrit
            FlagTransferer=true
        end if
     else 'octet d'adresse (adresse du PIC esclave)
        FlagTransferer=true
     end if
     'SSPIF.clear
  end if
end sub

'------------------------------------------------------------

main:
'   Main program
'désactivationd e la partie analogique
ADCON0.ADON=0
ADCON1.ADON=0
ANSEL = 0
ANSELH = 0

'désactivation analogique
ADCON0 = 0
ADCON1 = 0
ANSEL = 0
ANSELH = 0
'désactivation interrupt on change
IOCB = 0
'pas de pull up sur le port B
WPUB=0

'configuration des ports
PORTA=0
TRISA=0
PORTB=0
TRISB=%01010000
PORTC=0
TRISC=0

'configuration module SSP (I2C slave)
SSPSTAT=0
SSPBUF.BF=0
SSPCON.SSPOV=0
SSPCON = %00000110 ' mode 7 bits slave sans interruption START / STOP
SSPCON.CKP=1
SSPADD=AdressePic16F690 '<<1  'transfert de l'adresse I2C
SSPCON.SSPEN = 1 ' activation module SSP

'départ d'autorisation interruption
PIR1.SSPIF=0
PIE1.SSPIE=1
INTCON.PEIE=1

while true
   if FlagTransferer then
      ClignoterLedctrl(1)
      FlagTransferer=false
   end if
wend

end.


zeltron
Rang "6 LEDs"
Rang "6 LEDs"
Messages : 3295
Enregistré le : 15 oct. 2006, 20:05
Localisation : planète terre
Contact :

Re: Utilisation de l'I2C sur des PIC série 16F

Message non lu par zeltron » 13 déc. 2010, 20:18

S-O-S Suicide !

orpheedulogis
Rang "1 LEDs"
Rang "1 LEDs"
Messages : 30
Enregistré le : 13 déc. 2010, 01:37

Re: Utilisation de l'I2C sur des PIC série 16F

Message non lu par orpheedulogis » 13 déc. 2010, 20:30

Termi87 a écrit :Ce n'est pas de mon ressort :) !désolé
C'est pas grave . Merci quand même d'avoir répondu

J'avance doucement quand même et j'espère y arriver bientôt ;)

orpheedulogis
Rang "1 LEDs"
Rang "1 LEDs"
Messages : 30
Enregistré le : 13 déc. 2010, 01:37

Re: Utilisation de l'I2C sur des PIC série 16F

Message non lu par orpheedulogis » 14 déc. 2010, 02:05

mise à jour : apparemment on ne peut pas faire fonctionner le Mikrobasic avec un pic en esclave :(

Termi87
Administrateur
Administrateur
Messages : 3628
Enregistré le : 09 juil. 2006, 21:20
Localisation : Limousin

Re: Utilisation de l'I2C sur des PIC série 16F

Message non lu par Termi87 » 14 déc. 2010, 20:55

Je ne sais pas ce qu'en dise les pro de la programmation :p .... Il n'y aurait pas possibilité d'arriver au même résultat, en passant par d'autres composants ?

Répondre