|
//Le 20/05/2022//
|
|
//Mis à jour le 31/05/2022
|
|
|
|
// Programme pour générer une PWM de période 22ms avec un état haut varialble (de 1ms à 2ms) en changeant le diviseur du Timer 1 et en utilisant le mode Fast PWM 14 (c'est-à-dire TOP=ICR1)//
|
|
//===========================================================================================================================================================================================//
|
|
// 2ms à l'état haut c'est le mode marche avant;
|
|
//1.5 ms à l'état haut c'est le mode arrêt;
|
|
//1 ms à l'état haut c'est le mode marche arrière;
|
|
|
|
//===========
|
|
//Constantes:
|
|
//===========
|
|
|
|
//uint16_t diviseur[6] = {0,1,8,64,256,1024};
|
|
uint8_t flag;
|
|
|
|
|
|
//==========
|
|
//Variables:
|
|
//==========
|
|
// on compte jusqu'à 44000 (bin:1010 1011 1110 0000) pour avoir la période 22ms
|
|
|
|
byte VALEUR_DE_ICR1H=0b10101011; // limite "haute" de comptage pour le Timer 1 est de 65535,et la valeur de ICR1 doit être comprise entre 0 et 65535.
|
|
byte VALEUR_DE_ICR1L=0b11100000; //Le registre ICR1 est commun pour les pin D9 et D10;
|
|
|
|
//====================================================================================================================//
|
|
// pour avoir 2ms à l'état haut => OCR1B=OCR1A= 4000 en décimal; (bin: 0000 1111 1010 0000 ) : mode marche avant;
|
|
// pour avoir 1.5ms à l'état haut => OCR1B=OCR1A= 2990 en décimal; (bin: 0000 1011 1010 1110 ) : mode arrêt;
|
|
// pour avoir 1ms à l'état haut => OCR1B=OCR1A= 1980 en décimal; (bin: 0000 0111 1011 1100 ) : mode marche arrière;
|
|
//====================================================================================================================//
|
|
// La pin D9 est sur le port OC1A (PB1)
|
|
|
|
byte VALEUR_DE_OCR1AH=0b00001111; //Valeur de temps à l'état haut du signal PWM à générer sur la sortie OC1A,
|
|
byte VALEUR_DE_OCR1AL=0b10100000 ; // séparée en bits du poid fort et bits du poid faible
|
|
|
|
|
|
// La pin D10 est sur le port OC1B (PB2)
|
|
|
|
byte VALEUR_DE_OCR1BH=0b00001111; //Valeur du temps à l'état haut du signal PWM à générer sur la sortie OC1B;
|
|
byte VALEUR_DE_OCR1BL=0b10100000; // séparée en bits du poid fort et bits du poid faible;
|
|
|
|
|
|
void setup(){
|
|
|
|
// put your setup code here, to run once:
|
|
|
|
// Déclaration des pin D9 et D10 en Sortie (pins PWM):
|
|
|
|
pinMode(9,OUTPUT);
|
|
pinMode(10,OUTPUT);
|
|
Serial.begin(19200);
|
|
|
|
|
|
//===============================================
|
|
//Configuration_PWM => Timer 1 (Sortie D9 et D10)
|
|
//===============================================
|
|
|
|
//Paramétrage du Timer 1 en mode de fonctionnement "FAST PWM 14" c'est à dire TOP=ICR1 :
|
|
|
|
bitSet(TCCR1B,WGM13); //Mise de WGM13 à 1
|
|
bitSet(TCCR1B,WGM12); //Mise de WGM12 à 1
|
|
bitSet(TCCR1A,WGM11); //Mise de WGM11 à 1
|
|
bitClear(TCCR1A,WGM10); //Mise de WGM10 à 0
|
|
|
|
//Paramétrage du prédiviseur du Timer 1 à 8
|
|
//NOTE: le prédiviseur du Timer 1 est configuré par défaut sur 64
|
|
|
|
bitClear(TCCR1B,CS12); // Mise de CS12 à 0
|
|
bitSet(TCCR1B,CS11); // Mise de CS11 à 1
|
|
bitClear(TCCR1B,CS10); // Mise de CS11 à 0
|
|
|
|
|
|
//Acquisition du signal capteur( Sortie de la génératrice)//
|
|
//=================================================================================================================//
|
|
//ici pour récupérer le signal du capteur qui est analogique on doit agir sur les registres de l'ADC du
|
|
//mirocontroleur pour avoir la précision etl'échontillonnage voulus. La fonction analogRead est beaucoup trop
|
|
//lente pour effectuer des numérisations échantillonnées à plusieurs kHz;On utilisera donc l'API Atmel, et l'accès
|
|
//direct aux registres pour certaines opérations.
|
|
//=================================================================================================================//
|
|
|
|
|
|
|
|
// Configuration de l'échantillonage:
|
|
//===================================
|
|
|
|
noInterrupts(); // On désactive les interruptions pour commencer
|
|
|
|
// Paramétrage du Timer 0 en mode de fonctionnement "CTC" (Clear Timer on Compare Match);
|
|
//pour qu'il déclenche une interruption à chaque fois que sa valeur sera égale à celle qu'on aura indéqué dans le registre OCR0A
|
|
|
|
bitClear(TCCR0B,WGM02); //Mise de WGM02 à 0
|
|
bitSet(TCCR0A,WGM01); //Mise de WGM01 à 1
|
|
bitClear(TCCR0A,WGM00); //Mise de WGM00 à 0
|
|
|
|
// Paramétrage du prédiviseur du Timer 0 à 1 //
|
|
//==============================================================================================================================================================//
|
|
|
|
//le prédiviseur du Timer 0 est configuré par défaut sur 64//
|
|
|
|
bitClear(TCCR0B,CS02); // Mise de CS02 à 0
|
|
bitClear(TCCR0B,CS01); // Mise de CS01 à 0
|
|
bitSet(TCCR0B,CS00); // Mise de CS01 à 1
|
|
|
|
//==============================================================================================================================================================//
|
|
|
|
//Activer l'interruption du Timer 0, qui est en permanence s'il y a égalité entre la valeur courante du Timer et la valeur
|
|
//stockée dans un registre de comparaison.En pratique, pour ce faire, on va mettre à 1 le bit OCIE0A dans le registre TIMSK0;
|
|
// afin qu'une interruption soit générée, à chaque fois que la valeur du Timer 0 sera égale à la valeur qu'on aura renseigné dans le registre OCR0A;
|
|
|
|
|
|
bitSet(TIMSK0,OCIE0A); // on metle bit OCIE0A à 1 ( contenu dans le registre TIMSK0)
|
|
|
|
//==============================================================================================================================================================//
|
|
|
|
//On met le compteur à zéro, on entre la valeur déclenchant l'interruption, et on réactive les interruption
|
|
|
|
TCNT0=0; //// Mise à zéro du Timer 0
|
|
OCR0A=120; // valeur correspond à 0,0075ms (120*0,062µs =0,0075ms) et 0.062µs provient du calcul(1/16MHz*1),
|
|
//avec 16MHz la fréquence du quartz de l'ATmega328P, "1" le réglage de prédiviseur du Timer 0 fait précedement;
|
|
|
|
interrupts(); //réactiver les interruptions
|
|
|
|
|
|
//Configuration de L'ADC de l'ATmega328P:
|
|
//======================================
|
|
|
|
// Désactivons l'ADC pour l'instant
|
|
|
|
bitClear(ADCSRA, ADEN); //Mise à 0 de ADEN
|
|
// le bit ADEN de ADCSRA permet d'activer ou désactiver la convertion analogique/numérique
|
|
|
|
|
|
//Activer le mode de fontionnement de l'ADC
|
|
//L'ADC configuré en mode "Timer/Counter0 compare match A",
|
|
// pour que je puisse introduire le timer 0 pour gérer l'échantillonnage
|
|
|
|
bitClear( ADCSRB,ADTS2); //Mise de ADTS2 à 0
|
|
bitSet( ADCSRB,ADTS1); //Mise de ADTS1 à 1
|
|
bitSet( ADCSRB,ADTS0); //Mise de ADTS0 à 1
|
|
|
|
|
|
// On sélectionne notre pin (A0)
|
|
|
|
bitClear(ADMUX,MUX3); //Mise de MUX3 à 0
|
|
bitClear(ADMUX,MUX2); //Mise de MUX2 à 0
|
|
bitClear(ADMUX,MUX1); //Mise de MUX1 à 0
|
|
bitClear(ADMUX,MUX0); //Mise de MUX0 à 0
|
|
|
|
//Définition de la référence de tension (ici, équivalent de DEFAULT: 5V pour Arduino UNO)
|
|
|
|
bitClear(ADMUX,REFS1); // Mise de REFS1 à 0
|
|
bitSet(ADMUX,REFS0); // Mise de REFS0 à 1
|
|
|
|
|
|
// échantillonnage rapide!
|
|
//l'ADC de l'Arduino travaille avec une fréquence plus basse que celle de l’Arduino, afin d’améliorer sa précision.
|
|
//Donc il faut trouver un compromis entre la vitesse d’exécution et la précision
|
|
|
|
// Choix de la division du prescaler de l'ADC (ici, facteur 2)
|
|
|
|
bitClear(ADCSRA,ADPS2); //Mise de ADPS2 à 0
|
|
bitClear(ADCSRA,ADPS1); //Mise de ADPS1 à 0
|
|
bitSet(ADCSRA,ADPS0); //Mise de ADPS0 à 1
|
|
|
|
|
|
// Ce bit doit être passé à 1 pour prendre en compte le mode "Timer/Counter0 compare match A" ou les autres modes
|
|
|
|
bitSet(ADCSRA, ADATE);
|
|
|
|
// Demande d'une interruption à la fin de la conversion (activer les interruptions)
|
|
|
|
bitSet(ADCSRA, ADIE);
|
|
|
|
// Réactivons l'ADC
|
|
|
|
bitSet(ADCSRA, ADEN);
|
|
|
|
// On lance la première conversion
|
|
|
|
bitSet(ADCSRA, ADSC);
|
|
|
|
// Ne pas oublier d'activer les interruptions matérielles
|
|
|
|
sei();
|
|
|
|
// end of setup
|
|
}
|
|
|
|
//Il y a aussi une interruption COMPA générée par le Timer 0 (curieusement, il faut exécuter cette interruption pour que l'ADC fonctionne).
|
|
//Dans la fonction d'interruption, on fait alterner la sortie PD3???, ce qui permettra de contrôler le fonctionnement de l'échantillonneur à;
|
|
//l'oscilloscope. Cette sortie est reliée à la borne 3 sur l'Arduino UNO, à la borne 18 sur l'arduino MEGA.
|
|
|
|
ISR(TIMER0_COMPA_vect){
|
|
|
|
// Faire quelques chose!
|
|
|
|
if (flag==0) {
|
|
flag = 1;
|
|
PORTD &= ~(1<<PORTD3); // sortie 3 sur Arduino UNO
|
|
}
|
|
else {
|
|
flag = 0;
|
|
PORTD |= (1<<PORTD3);
|
|
}
|
|
|
|
}
|
|
|
|
//Interruption: pour détecter une nouvelle mesure de l’entrée analogique
|
|
|
|
ISR(ADC_vect) {
|
|
|
|
// Code à exécuter lors d'une mesure analogique
|
|
int Resultat_CAN = (ADCH << 8) | ADCL ;
|
|
|
|
}
|
|
|
|
|
|
//=======================
|
|
//Boucle Principale//
|
|
//=======================
|
|
|
|
void loop() {
|
|
|
|
//Boucle infinie
|
|
//Si on utilise les interruptions,on quitte cette boucle
|
|
//pour aller exécuter le code en haut avant de revenir ici
|
|
|
|
// put your main code here, to run repeatedly:
|
|
|
|
|
|
//==============================
|
|
// Génération des signaux PWM//
|
|
//==============================
|
|
|
|
// Générer le signal PWM sur la sortie D9( connectée à OC1A du microcontrolleur):
|
|
// PWM en mode "non inversé":
|
|
//sortie D9 mise à 0 lorsque la valeur de timer 1 atteint la valeur de OCR1A;
|
|
//sortie D9 mise à 1 à chaque remise à zéro du timer 1 c'est à dire TOP=ICR1;
|
|
|
|
bitSet(TCCR1A,COM1A1); // Mise de COM1A1 à 1
|
|
bitClear(TCCR1A,COM1A0); // Mise de COM1A0 à 0
|
|
ICR1H=VALEUR_DE_ICR1H; //ICR1 va compter de 0 jusqu'à atteindre la valeur "VALEUR_DE_ICR1"
|
|
ICR1L=VALEUR_DE_ICR1L;
|
|
OCR1AH=VALEUR_DE_OCR1AH; // OCR1A va compter de 0 jusqu'à atteindre la valeur "VALEUR_DE_OCR1A"
|
|
OCR1AL=VALEUR_DE_OCR1AL;
|
|
|
|
|
|
// Générer le signal PWM sur la sortie D10( connectée à OC1B du microcontrolleur):
|
|
// PWM en mode "non inversé":
|
|
//sortie D10 mise à 0 lorsque la valeur de timer 1 atteint la valeur de OCR1B;
|
|
//sortie D10 mise à 1 à chaque remise à zéro du timer 1 c'est à dire TOP=ICR1;
|
|
|
|
bitSet(TCCR1A,COM1B1); // Mise de COM1B1 à 1
|
|
bitClear(TCCR1A,COM1B0); // Mise de COM1B0 à 0
|
|
ICR1H=VALEUR_DE_ICR1H; //ICR1 va compter de 0 jusqu'à atteindre la valeur "VALEUR_DE_ICR1"
|
|
ICR1L=VALEUR_DE_ICR1L;
|
|
OCR1BH=VALEUR_DE_OCR1BH; // OCR1B va compter de 0 jusqu'à atteindre la valeur "VALEUR_DE_OCR1B"
|
|
OCR1BL=VALEUR_DE_OCR1BL;
|
|
|
|
|
|
|
|
|
|
//Récupérer le résultat de la conversion analogique numérique:
|
|
|
|
int Resultat_CAN = (ADCH << 8) | ADCL; //opération bit à bit (décalage à gauche de 8 bits de ADCH et un masque pour les bits de ADCL)
|
|
//car "Resultat_CAN" est un entier stocké sur 16 bits
|
|
|
|
float tension_volt= Resultat_CAN *(5.0/1024.0);
|
|
|
|
|
|
//Serial.print("Vitesse\n");
|
|
Serial.print("Vitesse,consigne\n");
|
|
Serial.print(tension_volt);
|
|
Serial.print(",");
|
|
Serial.print(5);
|
|
Serial.print("\n");
|
|
|
|
}
|
|
|
|
|
|
//remarque:
|
|
// dans le graphe visualisé par le traceur série j'ai remarqué la présence d'une tension minimale (tension de seuil peut être!) de 1.7 volts malgré que le moteur n'ai pas alimenté!!
|
|
//
|
|
//
|
|
//
|