Hasta ahora, pude obtener dos salidas con la resolución correcta (35 kHz-75 kHz con una resolución no inferior a 0,7 kHz) usando el siguiente código. Me pregunto, ahora, cómo puedo obtener un cambio de fase entre las dos salidas PWM (que usan el Timer1 y el Timer1 de 16 bits ICR1
).
Intenté escribir la línea TCNT1 += 1/freq/4;
entre la última y la penúltima línea de código ( OCR1A=...
y OCR1B =...
), pero no funcionó.
//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;
Si su aplicación solo requiere formas de onda con un ciclo de trabajo del 50 %, entonces puede usar los modos de salida de comparación alterna para generar un par de señales con cambio de fase ajustable entre ellas.
Los modos de alternancia alternarán su salida respectiva cada vez que haya una coincidencia de comparación, por lo que al ajustar los 2 registros de comparación de salida entre sí, cambia la relación de fase. Ajustas la frecuencia de ambas señales juntas cambiando el TOP del contador.
¿Tener sentido?
Aquí hay un código de demostración para un Arduino Uno. Emitirá ondas cuadradas de 50 KHz en los pines 9 y 10 de Arduino, y alternará cambios de fase de 0, 90 y 180 grados, haciendo una pausa en cada uno durante un segundo.
// 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);
}
Aquí hay algunos rastros de alcance de los cambios de 0, 90 y 180 grados respectivamente...
uint8_t v = digitalRead();
y luego enviar ese valor al puerto serie usando algo como Serial.writeln(v);
dentro de su archivo loop()
.No puede tener un cambio de fase entre múltiples señales en modo PWM usando un solo temporizador. Cada turno tendrá que estar en un temporizador separado, y deberá compensar cada contador en la cantidad adecuada.
delay()
et alia).Este código aún no es del todo correcto: el cambio de fase se complica cada vez más a medida que disminuye la frecuencia, y también tendré que agregar un preescalador para cualquier cosa por debajo de 62 kHz, ¡pero a 75 kHz funciona!
Supongo que ahora la pregunta no es "¿Cómo obtengo un retraso entre los dos PWM?" sino más bien, "¿Cómo consigo que la demora se escale con la frecuencia?"
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 le muestra cómo tener ondas cuadradas de cambio de fase de un solo temporizador (por ejemplo, 50% de servicio). Un temporizador de 16 bits (Nano, Uno, 2560) utiliza un modo que no es PWM pero brinda una verdadera capacidad de cambio de fase. Los cambios en la frecuencia no afectan la relación de fase que configure.
El código para ello se incluye en el siguiente enlace...
Tenga en cuenta que el generador de forma de onda del temporizador de 16 bits utiliza un modo de alternancia; por lo tanto, la salida de frecuencia es la mitad de la frecuencia del ciclo del temporizador.
gran josh
usuario3753934
gran josh