Bisher konnte ich mit dem folgenden Code zwei Ausgänge mit der richtigen Auflösung (35 kHz-75 kHz bei einer Auflösung von nicht schlechter als 0,7 kHz) erhalten. Ich frage mich jetzt, wie ich eine Phasenverschiebung zwischen den beiden PWM-Ausgängen erreichen kann (die beide den 16-Bit-Timer1 und verwenden ICR1
).
Ich habe versucht, die Zeile TCNT1 += 1/freq/4;
zwischen der letzten und vorletzten Codezeile ( OCR1A=...
und OCR1B =...
) zu schreiben, aber das hat nichts gebracht.
//set port B to output
DDRB |= 0xFF;
// wgm mode 1110 (fast pwm w/ TOP = ICR1
TCCR1A |= 1<<WGM11 ;
TCCR1B |= 1<<WGM12 | 1<<WGM13;
//set OC1/A and OC1B on compare match w/ ICR1 , clear them at bottom
TCCR1A |= 1<<COM1A1 | 1<<COM1A0| ;
TCCR1A |= 1<<COM1B1 | 1<<COM1B0 ;
//pre-scaler = 1
TCCR1B |= 1<<CS10;
ICR1 = 16000000/freq; // input compare value = (clock freq) / (desired freq)
// 50% duty cycle on OCR1A/B
OCR1A = ICR1/2;
//TCNT1 += 1/freq/4; //this line did not do anything
OCR1B = ICR1/2;
Wenn Ihre Anwendung nur Wellenformen mit einem Arbeitszyklus von 50 % erfordert, können Sie die Umschalt-Vergleichs-Ausgangsmodi verwenden, um ein Signalpaar mit einstellbarer Phasenverschiebung zwischen ihnen zu erzeugen.
Die Toggle-Modi schalten ihren jeweiligen Ausgang jedes Mal um, wenn es eine Vergleichsübereinstimmung gibt, so dass Sie die Phasenbeziehung ändern, indem Sie die 2 Ausgangsvergleichsregister relativ zueinander anpassen. Sie stellen die Frequenz beider Signale gemeinsam ein, indem Sie den TOP für den Zähler ändern.
Sinn ergeben?
Hier ist ein Demo-Code für ein Arduino Uno. Es gibt 50-kHz-Rechteckwellen an den Arduino-Pins 9 und 10 aus und durchläuft Phasenverschiebungen von 0, 90 und 180 Grad – mit jeweils einer Pause von einer Sekunde.
// This code demonstrates how to generate two output signals
// with variable phase shift between them using an AVR Timer
// The output shows up on Arduino pin 9, 10
// More AVR Timer Tricks at http://josh.com
void setup() {
pinMode( 9 , OUTPUT ); // Arduino Pin 9 = OCR1A
pinMode( 10 , OUTPUT ); // Arduino Pin 10 = OCR1B
// Both outputs in toggle mode
TCCR1A = _BV( COM1A0 ) |_BV( COM1B0 );
// CTC Waveform Generation Mode
// TOP=ICR1
// Note clock is left off for now
TCCR1B = _BV( WGM13) | _BV( WGM12);
OCR1A = 0; // First output is the base, it always toggles at 0
}
// prescaler of 1 will get us 8MHz - 488Hz
// User a higher prescaler for lower freqncies
#define PRESCALER 1
#define PRESCALER_BITS 0x01
#define CLK 16000000UL // Default clock speed is 16MHz on Arduino Uno
// Output phase shifted wave forms on Arduino Pins 9 & 10
// freq = freqnecy in Hertz ( 122 < freq <8000000 )
// shift = phase shift in degrees ( 0 <= shift < 180 )
// Do do shifts 180-360 degrees, you could invert the OCR1B by doing an extra toggle using FOC
/// Note phase shifts will be rounded down to the next neared possible value so the higher the frequency, the less phase shift resolution you get. At 8Mhz, you can only have 0 or 180 degrees because there are only 2 clock ticks per cycle.
int setWaveforms( unsigned long freq , int shift ) {
// This assumes prescaler = 1. For lower freqnecies, use a larger prescaler.
unsigned long clocks_per_toggle = (CLK / freq) / 2; // /2 becuase it takes 2 toggles to make a full wave
ICR1 = clocks_per_toggle;
unsigned long offset_clocks = (clocks_per_toggle * shift) / 180UL; // Do mult first to save precision
OCR1B= offset_clocks;
// Turn on timer now if is was not already on
// Clock source = clkio/1 (no prescaling)
// Note: you could use a prescaller here for lower freqnencies
TCCR1B |= _BV( CS10 );
}
// Demo by cycling through some phase shifts at 50Khz
void loop() {
setWaveforms( 50000 , 0 );
delay(1000);
setWaveforms( 50000 , 90 );
delay(1000);
setWaveforms( 50000 , 180 );
delay(1000);
}
Hier sind einige Oszilloskopspuren der 0-, 90- bzw. 180-Grad-Verschiebungen ...
uint8_t v = digitalRead();
und diesen Wert dann mit etwas wie Serial.writeln(v);
in Ihrer loop()
.Sie können keine Phasenverschiebung zwischen mehreren Signalen im PWM-Modus mit einem einzigen Timer haben. Jede Schicht muss auf einem separaten Timer sein, und Sie müssen jeden Zähler um den entsprechenden Betrag versetzen.
delay()
et alia).Dieser Code ist immer noch nicht ganz korrekt – die Phasenverschiebung wird mit abnehmender Frequenz immer mehr durcheinander gebracht, und ich muss auch einen Prescaler für alles unter 62 kHz hinzufügen – aber bei 75 kHz funktioniert es!
Ich denke, jetzt ist die Frage nicht "Wie bekomme ich eine Verzögerung zwischen den beiden PWMs?" sondern "Wie bekomme ich die Verzögerung mit der Frequenz zu skalieren?"
int main ()
{
//************************* Timer 0 *********************************
// wgm mode 111 (fast pwm w/ TOP = OCRA)
TCCR0A |= 1<<WGM00 | 1<<WGM01;
TCCR0B |= 1<<WGM02;
//set COM0x1 (non-inverting mode)
TCCR0A |= 1<<COM0A1 ;
TCCR0A |= 1<<COM0B1 ;
//pre-scaler = 1
TCCR0B |= 1<<CS00;
// arbitrary frequency
OCR0A = 220; //counts until TCNT = OCR0A then resets
OCR0B = OCR0A/2; //on until TCNT = OCR0B then off
// turn on pin D5
DDRD |= 1<<PIND5;
TCNT2 += OCR0A/4 ; //add a 90 degree delay to TCNT2... or something like that
//**************************** Timer 2 ****************************************
// wgm mode 111 (phase-corrected pwm w/ TOP = OCRA)
TCCR2A |= 1<<WGM20 | 1<<WGM21;
TCCR2B |= 1<<WGM22;
//set COM0x1 (non-inverting mode)
TCCR2A |= 1<<COM2A1 ;
TCCR2A |= 1<<COM2B1 ;
//pre-scaler = 1
TCCR2B |= 1<<CS20;
// same frequency as above pwm on timer 0
OCR2A = OCR0A;
OCR2B = OCR2A/2;
// turn on pin D3
DDRD |= 1<<PIND3;
}
Runtime Micro zeigt Ihnen, wie Sie Single-Timer-phasenverschiebbare Rechteckwellen (z. B. 50% Einschaltdauer) haben. Ein 16-Bit-Timer (Nano, Uno, 2560) verwendet einen Nicht-PWM-Modus, bietet aber eine echte Phasenverschiebungsfunktion. Frequenzänderungen wirken sich nicht auf die von Ihnen eingestellte Phasenbeziehung aus.
Der Code dafür ist unter folgendem Link enthalten...
Beachten Sie, dass der 16-Bit-Timer-Wellenformgenerator einen Toggle-Modus verwendet - daher ist die Ausgangsfrequenz 1/2 der Zyklusfrequenz des Timers.
Großer Josch
Benutzer3753934
Großer Josch