Interruption TImer et fonctions i2c

Bonjour,
je découvre la programmation Arduino et j’ai un souci avec une interruption Timer avec les fonctions i2C (Wire).
J’utilise un Arduino Duemilanove et l’IDE Arduino 022.

L’arduino commande 4 afficheurs leds 16 segments. Deux ci i2c pcf8574 pilotent les leds et des sorties de l’Arduino pilotent les “Anodes communes” des afficheurs et la led “point” des afficheurs

Cela fonctionne très bien si je n’utilise pas d’interrruption et que je lance la fonction “timer_int” dans “loop”. Cela affiche la chaine “0123” sur les 4 digits.La fonction “timer_int” affiche un digit à tour de role pendant 3ms.

Mais si j’utilise une interruption TIMER, celle-ci bloque dès le premier appel à la fonction i2c “Wire.endTransmission();”
J’ai testé l’interruption TIMER2 avec l’exemple ci-dessous mais aussi avec le TIMER1 et la librairie fourni ici: http://www.arduino.cc/playground/Code/Timer1
Même en mettent un temps très long entre chaque INT, cela bloque quand même.
J’ai testé l’interruption Timer sans appel de la fonction i2c et cela fonctionne très bien aussi.

Je n’ai plus de piste ou chercher, je fais appel à vous
merci

Ci-dessous extrait du code.

...

char digits[5] = "0123" ;
volatile byte nodigit = 0 ;


char pcf8574lo_addr = 0x40 >> 1 ; 
char pcf8574hi_addr = 0x42 >> 1 ; 
byte ack ;

unsigned int tcnt2;  // Timer2 reload value

// ---------------------------------------------------------------------------
void writeDigit16Seg(uint16_t code_caractere)
{
	Wire.beginTransmission(pcf8574hi_addr);          // Octet haut 8 premiers segments.
	Wire.send( (byte) ~(code_caractere >> 8) );      // Inverse bit, bit à 1 dans table ascii mais à 0 pour allumer led !
	Wire.endTransmission();   

	Wire.beginTransmission(pcf8574lo_addr);          // Octet bas 8 derniers segments.
	Wire.send( (byte) ~code_caractere);              // Inverse bit, bit à 1 dans table ascii mais à 0 pour allumer led !
	Wire.endTransmission();   
}

// ---------------------------------------------------------------------------
void setDigitAC(byte nodigit, boolean etat)
{
	digitalWrite(ACpinsdigits[nodigit], etat) ;		// Des/Active anode commune digit nodigit (LOW = active).
}

// ---------------------------------------------------------------------------
void setDigitDotpoint(boolean etat)
{
	digitalWrite(DPpindigits, etat)	;			// Des/Active point digit nodigit (LOW = active).
}

// Fonction appelée par INT TIMER ----------------------------------------------------
void timer_int()
{
 	if (nodigit == 0) setDigitAC(3, OFF) ;		// Désactive digit précédent.
	else  setDigitAC(nodigit - 1, OFF) ;
	
	if (digits[nodigit] >= 0x80) 			// digit contient chaine ascii à afficher, code >= 0x80 signifie led point à allumer.
	{
		digits[nodigit] &= 0x7F ;
		setDigitDotpoint(ON) ;	                // Active led point du digit.
	}
	else setDigitDotpoint(OFF) ;

	writeDigit16Seg( pgm_read_word_near(ascii_table + (digits[nodigit] - 0x20)) ) ;	// Ecrit sur pcf8574 les 16bits des segments correspondant au caractère.
	setDigitAC(nodigit, ON) ;			// Active digit en cours.
	nodigit++ ;
	if (nodigit == 4) nodigit = 0 ;		        // Pointe sur digit suivant.
}

// Interruption INT TIMER ----------------------------------------------------
ISR(TIMER2_OVF_vect) 
{  
   timer_int() ;
   TCNT2 = tcnt2;       // Reload the timer
}  
   

// Setup ----------------------------------------------------------------------
void setup() 
{
	byte i ;
	
	delay(3000) ;
	Serial.begin(9600);
	Serial.println("Setup Arduino ...");

	Wire.begin();					// Ne doit être appelée qu'une fois

	Wire.beginTransmission(pcf8574lo_addr);		// Init pcf8574
	Wire.send(0xFF);
	ack = Wire.endTransmission();   
	if ( ack )
	{
		Serial.print("Erreur I2c pcf8574 #1, ") ;
		Serial.println(ack, HEX) ;
	}

	Wire.beginTransmission(pcf8574hi_addr);		// Init pcf8574
	Wire.send(0xFF);
	ack = Wire.endTransmission();   
	if ( ack )
	{
		Serial.print("Erreur I2c pcf8574 #1, ") ;
		Serial.println(ack, HEX) ;
	}

	pinMode(DPpindigits, OUTPUT) ;                              // Init. port commande led "Dot Point".
	for (i=0; i<4; i++)
        {
          pinMode(ACpinsdigits[i], OUTPUT);                         // Init. port commande "Anode Commune".
          digitalWrite(ACpinsdigits[i], HIGH); 
        } 
        	
   // Timer2 Settings:
   // First disable the timer overflow interrupt while we're configuring
   TIMSK2 &= ~(1<<TOIE2);  // disable interrupt
   
   // Configure timer2 in normal mode (pure counting, no PWM etc.)
   TCCR2A &= ~((1<<WGM21) | (1<<WGM20));  
   TCCR2B &= ~(1<<WGM22);  
   
   // Select clock source: internal I/O clock 
   ASSR &= ~(1<<AS2);  
   
   // Disable Compare Match A interrupt enable (only want overflow)
   TIMSK2 &= ~(1<<OCIE2A);  
   
   // Now configure the prescaler to CPU clock divided by 128 
   TCCR2B |= (1<<CS22)  | (1<<CS20); // Set bits  
   TCCR2B &= ~(1<<CS21);             // Clear bit  

   // Save value globally for later reload in ISR
   tcnt2 = 131;   
   
   // Finally load end enable the timer
   TCNT2 = tcnt2;  
   TIMSK2 |= (1<<TOIE2);  

	Serial.println("Init. Arduino OK.\n");
}


// Loop ----------------------------------------------------------------------
void loop() 
{
    //timer_int() ; / Utiliser pour tester sans interruption !
    //delay(3) ;
}

Bonjour,

C'est tout à fait normal que wire.xxx bloque dans une interruption. La librairie wire fonctionne grâce à une interruption or il est impossible d'exécuter une interruption dans une interruption.

Lorsque j'ai créé ma librairie pour le pcf8574 j'avais ce même probléme, pour le résoudre j'ai fait appelle à la fonction sei() pour réactiver les interruptions avant d'utilisé wire dans mon interruption puis cli() pour revenir en mode normal.

Tente de mettre un sei(); à la première ligne de ton interruption, puis un cli(); avant la fin de l'interruption.

Merci,
Effectivement cela fonctionne mieux en réactivant les interruptions lors de l’appel de la fonction Timer.

Par contre, je constate un problème inverse.
J’utilise une horloge i2c pcf8583 et mon interruption timer perturbe la lecture i2c de cette horloge.
J’ai des données corrompus.

Si l’interruption timer est désativée la lecture de l’horloge est de nouveau correcte.

En fait, la lecture i2c du pcf8583 est interrompu par l’écriture i2c des pcf8574. Ce qui évidemment perturbe la gestion i2c du premier.
Je peux désactiver l’interruption pendant la lecture de l’horloge, cela fonctionne correctement.
Le problème si je lit trop souvent l’horloge, l’affichage n’est plus synchro., les digit clignotent.

Je précise que cette afficheur 4 digits fait parti d’un tout comprenant une horloge i2c + l’ethernet schield + l’afficheur.

La boucle principale lit l’horloge et affiche à des secondes précise l’heure, les t° intérieure/extèrieure (reçus par le reseau), lit l’interface ethernet pour recevoir des message xPL (réception des t°, des maj de l’horloge ainsi que de messages texte à afficher sur les digit).

Ce programme fonctionne très bien avec un afficheur i2c à base de saa1064 (4 digit 7 segments) que je voulais remplacer par un afficheur 4 digits 16 segments mais géré par l’Arduino.

Bonjour

Au cas où.... repli éventuel vers une librairie I2C soft (sans interruption) ?

J'avais vu passer une librairie 'soft I2c' (en fait 2 variantes dont l'une n'utilise pas le module I2C du microcontroleur) Pas (encore) testé de mon côté, c'est juste un lien... à priori de confiance vu la source. http://forums.adafruit.com/viewtopic.php?f=25&t=13722

L'I2C (maître) en 'bit-banging' est présent également dans la librairie 'ports' de Jeelabs http://jeelabs.net/projects/cafe/wiki/Ports

Autre 'solution de repli' : laisser carrément tomber l'i2c pour attaquer les 16 segments au profit de deux HC595 en cascade comme içi : http://arduino.cc/en/Tutorial/ShiftOut

Remarque : le PCF8583 pourrait générer une interruption périodique sur sa broche INT en lieu et place d'un Timer interne. (J'ai prévu de reprendre prochainement sur Arduino une application ou j'utilisais le PCF8583 pour réveiller par interruption externe un PIC à intervalles de temps programmés. Si mes souvenirs sont bons on peut programmer le mode Alarm du PCF8583 pour obtenir une interruption à intervalles de temps réguliers (1 seconde par exemple) ou une interruption à une 'date' programmée. D'ailleurs un PCF8583 non initialisé ne sort-il pas un signal à 1Hz sur INT ?/i]

@al1fch merci pour toutes ces info. j'ai mis ces liens de coté d'autant que j'utilisais du soft i2c sur un autre uC, cela pourrai servir. Le ci 74HC595 est intéressant aussi, il faut que je fouille dans mes archives, j'en ai peut être un qui traine.

Sinon, j'ai trouvé une solution à mon problème. La lecture de l'horloge est dans la boucle principale mais je ne pouvais utiliser de "delay" sinon je pouvais passer à coté de messages réseau xPL. J'ai utilisé la fonction millis() pour ne lire l'horloge i2c que toutes les secondes. De cette façon, l'affichage n'est plus perturbé par cette lecture avec arrêt de l'int. timer.