« Utiliser les PIC 16F et 18F/Exercices/Interruption timer0 en langage C » : différence entre les versions
Ligne 79 : | Ligne 79 : | ||
{{Solution|contenu= |
{{Solution|contenu= |
||
1°) Pas grand chose de changé : |
|||
⚫ | |||
<source lang="c"> |
<source lang="c"> |
||
void interrupt decalage(void) { |
void interrupt decalage(void) { |
||
Ligne 86 : | Ligne 86 : | ||
PORTB = (PORTB << 1) ; |
PORTB = (PORTB << 1) ; |
||
if (PORTB == 0x00) PORTB = 0x01; |
if (PORTB == 0x00) PORTB = 0x01; |
||
TMR0IF_bit = 0; // acquittement interruption (changé ici) |
TMR0IF_bit = 0; // acquittement interruption (** changé ici **) |
||
} |
} |
||
unsigned char nb; |
unsigned char nb; |
||
Ligne 92 : | Ligne 92 : | ||
TRISA = 0xF9; // 6 entrees, 2 sorties pour A |
TRISA = 0xF9; // 6 entrees, 2 sorties pour A |
||
TRISB = 0x00; // 8 sorties pour B |
TRISB = 0x00; // 8 sorties pour B |
||
OPTION_REG = 0x07; // prescaler 256 , entree sur quartz (changé ici) |
OPTION_REG = 0x07; // prescaler 256 , entree sur quartz (** changé ici **) |
||
INTCON = 0xA0; // autorise l'interruption timer |
INTCON = 0xA0; // autorise l'interruption timer |
||
PORTB = 0x01; // une seule diode allumee |
PORTB = 0x01; // une seule diode allumee |
||
Ligne 106 : | Ligne 106 : | ||
N'oubliez pas la division par 16 qui est réalisée avec le if (!(nb % 16)) dans l'interruption. |
N'oubliez pas la division par 16 qui est réalisée avec le if (!(nb % 16)) dans l'interruption. |
||
<u>Calcul précis</u> : 50 MHz / 4*(256*256*16) = 11,92 Hz (la division par 4 a toujours lieu). |
|||
3°) "if (!(nb % 16))" est une façon pas très efficace de calculer le reste de la division par 16. J'ignore la technique utilisé par le compilateur, mais ce calcul est forcément long puisqu'il n'y a pas d'instruction de division sur le 16F84. |
3°) "if (!(nb % 16))" est une façon pas très efficace de calculer le reste de la division par 16. J'ignore la technique utilisé par le compilateur, mais ce calcul est forcément long puisqu'il n'y a pas d'instruction de division sur le 16F84. |
||
Ligne 115 : | Ligne 117 : | ||
</source> |
</source> |
||
4°) Décalage tout simple d'une LED vers les poids forts. |
4°) Décalage tout simple d'une LED vers les poids forts. |
||
5°) OPTION_REG = 0x84; = 1000 0011 => poids faible = 3 => division par 2 puissance 4 = 16 |
|||
4 000 000 / 4*16*(256-96)*400 = 0,9765625 Hz Soit pratiquement 1 Hz |
|||
Un meilleurs calcul serait : |
|||
4 000 000 / 4*16*(256-98)*400 = 0,988924051 Hz (à cause des temps de latence) Cette connaissance n'est pas demandée aux étudiants. |
|||
⚫ | |||
<source lang="c"> |
|||
#include <pic1684.h> |
|||
//#include <htc.h> serait-il mieux ? |
|||
void interrupt decalage(void); |
|||
unsigned char nb; |
|||
main(void) { |
|||
TRISA = 0xF9; // 6 entrees, 2 sorties pour A |
|||
TRISB = 0x00; // 8 sorties pour B |
|||
OPTION = 0x07; // prescaler 256 , entree sur quartz |
|||
INTCON = 0xA0; // autorise l'interruption timer |
|||
PORTB = 0x01; // une seule diode allumee |
|||
TMR0 = 0x00 ; |
|||
nb=0; |
|||
while(1) { |
|||
// on ne fait rien que recopier sur 2 segments la valeur de SW1 |
|||
if ((PORTA & 0x01) == 1) PORTA = 0x06; |
|||
} |
|||
} |
|||
void interrupt decalage(void) { |
|||
nb++; |
|||
//TMR0 = 0x00; //c'est fait car ici par overflow |
|||
if (!(nb % 16)) |
|||
PORTB = (PORTB >> 1) ; // *** changé ici **** |
|||
if (PORTB == 0x00) PORTB = 0x80; // *** changé ici **** |
|||
T0IF = 0; // acquittement interruption |
|||
} |
|||
</source> |
|||
}} |
}} |
||
Version du 24 janvier 2011 à 15:06
Exercice 1 (Hitech C et Mikro C)
Si vous voulez savoir comment est réalisé pratiquement ce qui est décrit dans cet exercice, visitez le chapitre Embarquer un PIC 16F84 d'un autre livre.
Un PIC16F84 est enfoui dans un FPGA. Sa seule particularité est de fonctionner à 50MHz contre 10 (resp. 20 MHz) de fréquence maximale d'horloge pour les PIC 16F84 (resp. 16F84A). Il exécute le programme suivant (écrit avec le compilateur Hitech C) :
#include <pic1684.h>
//#include <htc.h> serait-il mieux ?
void interrupt decalage(void);
unsigned char nb;
main(void) {
TRISA = 0xF9; // 6 entrees, 2 sorties pour A
TRISB = 0x00; // 8 sorties pour B
OPTION = 0x07; // prescaler 256 , entree sur quartz
INTCON = 0xA0; // autorise l'interruption timer
PORTB = 0x01; // une seule diode allumee
TMR0 = 0x00 ;
nb=0;
while(1) {
// on ne fait rien que recopier sur 2 segments la valeur de SW1
if ((PORTA & 0x01) == 1) PORTA = 0x06;
}
}
void interrupt decalage(void) {
nb++;
//TMR0 = 0x00; //c'est fait car ici par overflow
if (!(nb % 16))
PORTB = (PORTB << 1) ;
if (PORTB == 0x00) PORTB = 0x01;
T0IF = 0; // acquittement interruption
}
Remarquez comment est écrit une interruption avec ce compilateur.
1°) Repérer et modifier les lignes de ce programmes pour qu'il fonctionne avec le compilateur MikroC.
2°) Calculer si le chenillard réalisé par ce programme est visible à l'œil humain (fréquence de changement de position des LEDs inférieure à 20 Hz).
3°) Comment peut-on écrire l'instruction "if (!(nb % 16))" pour plus d'efficacité.
4°) Quelle est la suite des états (LEDs allumées) réalisée par ce programme.
Les deux questions suivantes sont issues du Devoir Surveillé de Juin 2010.
5°) Le programme suivant est donné comme exemple du compilateur MikroC et tourne dans un PIC 16F84 qui a un quartz de 4 MHz.
unsigned cnt;
void interrupt() {
if (TMR0IF_bit) {
cnt++; // increment counter
TMR0IF_bit = 0; // clear TMR0IF
TMR0 = 96;
}
}
void main() {
OPTION_REG = 0x84; // Assign prescaler to TMR0
ANSEL = 0; // Configure AN pins as digital
ANSELH = 0;
C1ON_bit = 0; // Disable comparators
C2ON_bit = 0;
TRISB = 0; // PORTB is output
PORTB = 0xFF; // Initialize PORTB
TMR0 = 96; // Timer0 initial value
INTCON = 0xA0; // Enable TMRO interrupt
cnt = 0; // Initialize cnt
do {
if (cnt >= 400) {
PORTB = ~PORTB; // Toggle PORTB LEDs
cnt = 0; // Reset cnt
}
} while(1);
}
Quelle est la fréquence de clignotement des LEDs reliées au PORTB ?
6°) Modifier l'interruption pour qu'elle réalise un chenillard d'une LED se déplaçant vers les poids faibles.
1°) Pas grand chose de changé :
void interrupt decalage(void) {
nb++;
if (!(nb % 16))
PORTB = (PORTB << 1) ;
if (PORTB == 0x00) PORTB = 0x01;
TMR0IF_bit = 0; // acquittement interruption (** changé ici **)
}
unsigned char nb;
main(void) {
TRISA = 0xF9; // 6 entrees, 2 sorties pour A
TRISB = 0x00; // 8 sorties pour B
OPTION_REG = 0x07; // prescaler 256 , entree sur quartz (** changé ici **)
INTCON = 0xA0; // autorise l'interruption timer
PORTB = 0x01; // une seule diode allumee
TMR0 = 0x00 ;
nb=0;
while(1) {
// on ne fait rien que recopier sur 2 segments la valeur de SW1
if ((PORTA & 0x01) == 1) PORTA = 0x06;
}
}
2°) Je l'ai réalisé dans un FPGA et à vue de nez la fréquence est entre 5 et 10 Hz.
N'oubliez pas la division par 16 qui est réalisée avec le if (!(nb % 16)) dans l'interruption.
Calcul précis : 50 MHz / 4*(256*256*16) = 11,92 Hz (la division par 4 a toujours lieu).
3°) "if (!(nb % 16))" est une façon pas très efficace de calculer le reste de la division par 16. J'ignore la technique utilisé par le compilateur, mais ce calcul est forcément long puisqu'il n'y a pas d'instruction de division sur le 16F84.
Comme la division se fait par 16 qui est une puissance de deux, on peut utiliser un masque pour faire ce calcul bien plus rapidement :
// langage C
if (!(nb & 0x0F)) // idem à if (!(nb % 16)) mais plus rapide
4°) Décalage tout simple d'une LED vers les poids forts.
5°) OPTION_REG = 0x84; = 1000 0011 => poids faible = 3 => division par 2 puissance 4 = 16
4 000 000 / 4*16*(256-96)*400 = 0,9765625 Hz Soit pratiquement 1 Hz
Un meilleurs calcul serait :
4 000 000 / 4*16*(256-98)*400 = 0,988924051 Hz (à cause des temps de latence) Cette connaissance n'est pas demandée aux étudiants.
6°)
#include <pic1684.h>
//#include <htc.h> serait-il mieux ?
void interrupt decalage(void);
unsigned char nb;
main(void) {
TRISA = 0xF9; // 6 entrees, 2 sorties pour A
TRISB = 0x00; // 8 sorties pour B
OPTION = 0x07; // prescaler 256 , entree sur quartz
INTCON = 0xA0; // autorise l'interruption timer
PORTB = 0x01; // une seule diode allumee
TMR0 = 0x00 ;
nb=0;
while(1) {
// on ne fait rien que recopier sur 2 segments la valeur de SW1
if ((PORTA & 0x01) == 1) PORTA = 0x06;
}
}
void interrupt decalage(void) {
nb++;
//TMR0 = 0x00; //c'est fait car ici par overflow
if (!(nb % 16))
PORTB = (PORTB >> 1) ; // *** changé ici ****
if (PORTB == 0x00) PORTB = 0x80; // *** changé ici ****
T0IF = 0; // acquittement interruption
}
Exercice 2
Une partie matérielle est constituée de deux afficheurs sept segments multiplexés. Les sept segments sont commandés par le PORTC, tandis que les commandes d'affichages sont réalisée par les bits b0 et b1 du PORTB. Un schéma de principe est donné ci-après.
1°) A l'aide de la documentation calculer les valeurs dans un tableau "unsigned char SEGMENT[] = {0x3F,...};" pour un affichage des chiffres de 0 à 9.
2°) réaliser une fonction responsable du transcodage :
unsigned char Display(unsigned char no) {
unsigned char Pattern;
unsigned char SEGMENT[] = {0x3F,....
3°) Réaliser le programme main() responsable de l'initialisation de l'interruption qui doit avoir lieu toutes les 10ms (avec un quartz de 4MHz) et qui compte de 00 à 99 toutes les secondes environ (avec un "Delay_ms(1000);")
4°) Réaliser enfin l'interruption qui affichera tantôt les dizaines, tantôt les unités.
2°)
unsigned char Display(unsigned char no) {
//unsigned char Pattern;
unsigned char SEGMENT[] = {0x3F,....
return SEGMENT[no];