Servo-Testprogramm Variante 2 mit Timer1-Interrupt:
Wie durch entsprechendes Setzen der Register ein Timer-Interrupt ausgelöst werden kann, findet ihr hier: Timer-Interrupt
Bevor ich das Testprogramm Variante 2 zeige, noch einmal kurz die Vorgangsweise:
- Der Modus muss im TCCR1B-Register auf CTC-Mode (Clear Timer on Compare Mode) eingestellt werden.
- Der Inhalt des 16-Bit TCNT1-Register wird mit dem - um den Faktor des Vorteilers (Prescaler) verminderten - Systemtakt erhöht und mit dem Inhalt des 16-Bit OCR1A-Registers verglichen.
- Bei Übereinstimmung wird, sofern das entsprechende Maskier-Bit im TIMSK1-Register und das globale Interrupt-Enable-Bit gesetzt sind, ein Interrupt ausgelöst.
Vorteiler und Vergleichswert (Inhalt des OCR1A-Register) für eine Periodendauer von 20 ms ermitteln:
Vorgaben:
- CPU-Frequenz Arduino Uno: 16.000.000 Hz
- Gewünschte Interruptfrequenz: 50 Hz (= 20 ms Periodendauer)
- Mögliche Vorteiler: 1, 8, 64, 256 oder 1024
Berechnung des Vergleichswertes:
Vorteiler = 1024:
Vergleichswert = (16.000.000 / (1024 * 50)) - 1 = 311,5
oder Vorteiler = 8:
Vergleichswert = (16.000.000 / (8 * 50)) - 1 = 39.999
Der errechnete Vergleichswert wird ins OCR1A-Register geschrieben.
Der Vorteiler muss im TCCR1B-Register gesetzt werden.
Achtung: Der errechnete Vergleichswert muss kleiner 65.536 (16-Bit) sein!!
Im nachfolgenden Programm habe ich den (gerundeten) Vergleichswert von 312 (Vorteiler = 1024) gewählt.
Hier nun zusammengefasst, wie die Register gesetzt werden müssen:
cli(); //Loesche globales Interrupt-Enable-Bit
//CTC-Mode aktivieren
TCCR1A = 0; //Loeschen des TCCR1A-Registers
TCCR1B = 0; //Loeschen des TCCR1B-Registers
TCCR1B |= (1 << WGM12); //Setze CTC-Mode (Waveform Generation Mode)
//Vorteiler (Prescaler) definieren (Vorteiler = 1024)
TCCR1B |= (1 << CS12) | (1 << CS10); //Setze CS10 und CS12 (Clock Select)
// Timer Counter Register und Output Compare Register setzen
TCNT1 = 0; //Timer Counter Register loeschen
OCR1A = 312; //Setzen des Vergleichswertes (gerundet)
// Timer/Counter Interrupt Mask Register setzen
TIMSK1 |= (1 << OCIE1A); //Bit Output Compare A Match Interrupt Enable setzen
sei(); //Setze globales Interrupt-Enable-Bit
Wird nun ein Interrupt ausgelöst, springt der Programmablauf in die Interrupt-Serviceroutine "ISR(TIMER1_COMPA_vect)"
Hier nun das vollständige Test-Programm in der Variante 2:
//Servo Testprogramm
//Test_Servo_IRQ_Timer1.ino
//Code fuer Arduino
//Author Retian
//Version 1.0
#define servoPin 2 //Servo auf Pin 2
#define potiPin 2 // Potentiometer auf Pin A2
int pwmTime = 0;
int potiWert = 0;
int pwmTimeMax = 2000; //maximale Impulszeit in Mikrosekunden
int pwmTimeMin = 1000; //minimale Impulszeit in Mikrosekunden
void setup() {
Serial.begin(115200);
pinMode(servoPin, OUTPUT);
digitalWrite(servoPin, 0);
cli(); // Loeche globales Interruptflag
TCNT1 = 0; //Timer Counter 1
TCCR1A = 0; //Timer Counter Controll Register A
TCCR1B = 0; //Timer Counter Controll Register B
OCR1A = 312; //Output Compare Register A
// Setze CS10 und CS12 - Clock Select Bit 10/12 (Prescaler 1024)
TCCR1B |= (1 << CS12) | (1 << CS10);
//CTC-Mode ein
TCCR1B |= (1 << WGM12); // CTC-Mode (Clear Timer and Compare)
//Timer/Counter Interrupt Mask Register
TIMSK1 |= (1 << OCIE1A); //Output Compare A Match Interrupt Enable
sei(); //Setze globales Interruptflag
}
void loop() {
potiWert = analogRead(potiPin);
pwmTime = map(potiWert, 0, 1023, pwmTimeMin, pwmTimeMax);
Serial.print("Potiwert: ");
Serial.print(potiWert);
Serial.print(" ");
Serial.print("Pulslaenge: ");
Serial.print(pwmTime);
Serial.println(" us");
}
ISR(TIMER1_COMPA_vect) //Interrupt-Serviceroutine
{
digitalWrite(servoPin, 1);
delayMicroseconds(pwmTime);
digitalWrite(servoPin, 0);
}