Bonjour,
Je débute avec les microcontrôleurs. Pour jouer avec les différentes fonctions qu'ils offrent et bien comprendre comment gérer ces petites bestioles, je suis en train de faire un petit métronome visuel (une LED pour indiquer les mesures et une pour les temps). J'utilise un PIC 18F46K22, qui dispose de beaucoup de fonctions différentes à tester.
Actuellement, le tempo n'est réglable que par une constante enregistrée dans le programme. J'en suis en fait à regarder comment se gèrent les interruptions.
Un bouton poussoir sert à modifier le nombre de temps par mesure (2tps, 3tps, 4tps). Il est branché sur une des broches INT0,1 ou 2. Le circuit étant réalisé sur un platine d'essai, je peux déplacer la broche de l'interrupteur sur l'une ou l'autre des entrées du µC.
Donc quand on appuie sur le BP, le PIC réagit comme ceci :
- interruption HP sur la patte INTx
- si le nombre de temps par mesure actuel est inférieur à 4, l'incrémenter, sinon, redescendra à des mesures de 2 temps.
- écriture du résultat dans l'EEPROM pour ne pas avoir à reparamétrer en cas de coupure, ceci sans attendre (fin d'interruption dès le début de l'écriture)
- interruption HP sur la fin d'écriture EEPROM
- relecture de la mémoire EEPROM et contrôle que la valeur correspond bien à la valeur qu'on voulait écrire
- si la valeur est bonne, confirmation par clignotement de la LED verte, sinon clignotement de la LED jaune.
- interruption LP sur le timer 0 pour incrémenter un compteur et mettre le PIC en sommeil à partir d'une certaine valeur
Dans l'ensemble, ceci fonctionne. Mes souci viennent de la gestion des interrupteurs :
- Sauf que si l'interruption se passe sur la patte INT0, ça fonctionne bien.
- si je déplace le BP sur INT1, plus rien ne se passe ! ?
- si je déplace le BP sur la patte INT2, il fait l'inverse de ce qu'il devrait : le nombre de temps par mesure est décrémenté au lieu d'être incrémenté. Je pense plutôt qu'au lieu de ne voir qu'un front, il en voit deux.
- Aussi j'ai essayé de créer des interruptions de type InterruptOnChange sur RB4 ou RB5 qui ne fonctionnent pas du tout (quelle que soit la patte utilisée). Pour faire des essais, le principe était simplement de faire clignoter la LED verte en cas d'appui et la LED jaune en cas de relâchement. Sauf que rien ne se passe du tout.
Voici le shéma de branchement de mes boutons poussoirs :
Je vous joint aussi le code complet de mon petit programme :Si une bonne âme pouvait m'expliquer ce que je fais mal ou ce que je n'ai pas compris, je lui en serait très reconnaissant.Code:/* * File: main.c - Metronome * Author: anard * * Created on June 13, 2016, 4:52 PM * Metronome à LEDs avec indication des mesures */ // PIC18F46K22 Configuration Bit Settings // 'C' source line config statements #include <xc.h> // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. // CONFIG1H #pragma config FOSC = HSMP // Oscillator Selection bits (HS oscillator (medium power 4-16 MHz)) #pragma config PLLCFG = OFF // 4X PLL Enable (Oscillator used directly) #pragma config PRICLKEN = ON // Primary clock enable bit (Primary clock is always enabled) // CONFIG2H #pragma config WDTEN = OFF // Watchdog Timer Enable bits (Watch dog timer is always disabled. SWDTEN has no effect.) // CONFIG3H #pragma config PBADEN = OFF // PORTB A/D Enable bit (PORTB<5:0> pins are configured as digital I/O on Reset) // CONFIG4L #pragma config LVP = ON // Single-Supply ICSP Enable bit (Single-Supply ICSP enabled if MCLRE is also 1) ///////////////// ------ PARAMETERS TO PROGRAM ------ ///////////////// #define BPS 180 // Tempo (bpm) #define TEMPS_AV_SLEEP 10 // Temps avant arrêt (s) #define TPS_ALLUME 30 // Temps d'allumage des LEDs (ms) ///////////////// ------ PARAMETERS TO PROGRAM ------ ///////////////// #define _XTAL_FREQ 8000000 // Osc frequency #define TEMPS PORTDbits.RD1 // Green LED #define MESURE PORTDbits.RD2 // Yellow LED #define NB_PASSAGES (((60000/BPS) - TPS_ALLUME)/95) // la durée d'un __delay_ms est limitée à ~95ms à 8MHz #define TPS_ETEINT (((60000/BPS) - TPS_ALLUME)%95) #define OVERFLOW_COUNTER TEMPS_AV_SLEEP*_XTAL_FREQ/262144 // Tmr0 overflow = 256*256*4 = 262144 unsigned char nb_tps; unsigned int iCount; // Appui sur un bouton et écriture dans la mémoire non volatile void high_priority interrupt UserSignal_and_EEWrite(void) { char k; // Se réveiller si en sommeil, sinon changement du nombre de temps par mesure et enregistrement dans l'EEPROM if (INTCONbits.INT0IF || INTCON3bits.INT1IF || INTCON3bits.INT2IF) { //if (INTCONbits.RAIF) { INTCONbits.INT0IF = INTCON3bits.INT1IF = INTCON3bits.INT2IF = 0; if (iCount < OVERFLOW_COUNTER) { // désactivation de smodifiactions en cas de réveil if (nb_tps < 4) nb_tps ++; else nb_tps = 2; // EEPROM Write EECON1bits.WREN = 1; INTCONbits.GIEL = INTCONbits.INT0IE = INTCON3bits.INT1IE = INTCON3bits.INT2IE = 0; EEADRH = EEADR = 0; EEDATA = nb_tps; EECON2 = 0x55; EECON2 = 0x0AA; EECON1bits.WR = 1; } // Reset sleep timer iCount = TMR0L = 0; } /* Essai sur les interruptions IOCB * Devrait faire cignoter 10 fois la LED jaune en cas d'appui ou la LED verte en cas de relâchement. * ////////////////// * NE FONCTIONNE PAS DU TOUT * ///////////////// * -------------------------- */ else if (INTCONbits.RBIF) { if (PORTBbits.RB4 == 1) { for (k=0; k<10; k++) { TEMPS = 1; __delay_ms(10); TEMPS = 0; __delay_ms(50); } } else { for (k=0; k<10; k++) { MESURE = 1; __delay_ms(10); MESURE = 0; __delay_ms(50); } } INTCONbits.RBIF = 0; } // Fin d'écriture EEPROM else if (PIR2bits.EEIF) { // Lecture de l'adresse courante EECON1bits.RD; // Contrôle de la correspondance de la lecture avec la valeur à écrire et confirmation par scintillement de la LED if (EEDATA == nb_tps) { // OK for (k=0; k<5; k++) { TEMPS = 1; __delay_ms(10); TEMPS = 0; __delay_ms(50); } } else { // ERREUR for (k=0; k<5; k++) { MESURE = 1; __delay_ms(10); MESURE = 0; __delay_ms(50); } } // Récativation des interruptions, fin d'écriture. INTCONbits.GIEL = INTCONbits.INT0IE = INTCON3bits.INT1IE = INTCON3bits.INT2IE = 1; EECON1bits.WREN = PIR2bits.EEIF = 0; } } // Incrémente le compteur de Sleep void low_priority interrupt SleepTimer(void) { if (INTCONbits.TMR0IF) { iCount++; INTCONbits.TMR0IF = 0; } } // ------------------ MAIN --------------- void main(void) { char i, j; // Initialisation ANSELA = ANSELB = ANSELC = ANSELD = ANSELE = 0; // All digital TRISB = 1; // Config PORTB en entrée TRISD = 0; // Config PORTD en sortie // EEPROM READING EECON1 = 0; // Situe les écritures dans l'EEPROM EEADRH = EEADR = 0; EECON1bits.RD = 1; // Read nb_tps = EEDATA; if (nb_tps > 4) nb_tps = 4; // INTERRUPTS // Enable prioritary interrupts RCONbits.IPEN = 1; // EEPROM Interrupts High Priority PIE2bits.EEIE = IPR2bits.EEIP = 1; // Tmr0 & Int0 & Rb4-OnChange INTCON = 0b00111000; // GIEH GIEL Tmr0IE INT0IE RBIE Tmr0IF INT0IF RBIF IOCB = 0b00010000; // Rb4 // Pullups, edges and priorities INTCON2 = 0b00000001; // Pull-upEnable INT0,1,2 onFallingEdge - Tmr0LowPriority - IOC HighPriority WPUB = 0xFF; // Pull-up enabled for all PortB // Int1 & Int2 High Priority INTCON3 = 0b11011000; // INT2IP INT1IP - INT2IE INT1IE - INT2IF INT1IF // Timer0 init T0CON = 0b01000111; // OFF 8bits onCLKOut - PrescalerEnabled PS=256 ==> Overflow on 256*256/2.000.000 = 32,768 ms TMR0L = iCount = 0; // Activation INTCONbits.GIEH = 1; INTCONbits.GIEL = 1; T0CONbits.TMR0ON = 1; // ---------- MAIN LOOP ---------- // while(1) { // Test prouvant que Rb4 est correctement reconnu. if (PORTBbits.RB4 == 1) // Si Rb4 est enfoncé, le code est bien ignoré. { // LED de mesure MESURE = 1; __delay_ms(TPS_ALLUME); MESURE = 0; for (j=0; j<NB_PASSAGES; j++) __delay_ms(95); __delay_ms(TPS_ETEINT); // LED de temps for (i=0; i<nb_tps-1; i++) { TEMPS = 1; __delay_ms(TPS_ALLUME); TEMPS = 0; for (j=0; j<NB_PASSAGES; j++) __delay_ms(95); __delay_ms(TPS_ETEINT); } // Go to sleep... // Fonctionne aussi dans l'interruption du Tmr0 (en basse priorité) mais, ici, on attend une fin de mesure pour la mise en sommeil if (iCount >= OVERFLOW_COUNTER) { SLEEP(); NOP(); } } } }
Merci beaucoup.
-----