Пока что мне удалось получить два выхода при правильном разрешении (35 кГц-75 кГц при разрешении не хуже 0,7 кГц), используя приведенный ниже код. Теперь мне интересно, как я могу получить фазовый сдвиг между двумя выходами PWM (которые оба используют 16-битный Timer1 и ICR1
).
Я попытался написать строку TCNT1 += 1/freq/4;
между последней и предпоследней строками кода ( OCR1A=...
и OCR1B =...
), но это ничего не дало.
//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;
Если вашему приложению требуются сигналы только с коэффициентом заполнения 50 %, вы можете использовать переключатель сравнения режимов вывода, чтобы сгенерировать пару сигналов с регулируемым фазовым сдвигом между ними.
Режимы переключения будут переключать свои соответствующие выходные данные каждый раз при совпадении сравнения, поэтому, настраивая два выходных регистра сравнения относительно друг друга, вы изменяете соотношение фаз. Вы настраиваете частоту обоих сигналов вместе, изменяя TOP для счетчика.
Есть смысл?
Вот некоторый демонстрационный код для Arduino Uno. Он будет выводить прямоугольные волны 50 кГц на контакты 9 и 10 Arduino и циклически изменять фазовые сдвиги на 0, 90 и 180 градусов с паузой на каждую секунду.
// 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);
}
Вот некоторые следы прицела сдвига на 0, 90 и 180 градусов соответственно...
uint8_t v = digitalRead();
, а затем вывести это значение на последовательный порт, используя что-то вроде Serial.writeln(v);
вашего файла loop()
.У вас не может быть фазового сдвига между несколькими сигналами в режиме ШИМ с использованием одного таймера. Каждая смена должна быть на отдельном таймере, и вам нужно будет сместить каждый счетчик на соответствующую сумму.
delay()
и др.).Этот код по-прежнему не совсем корректен — фазовый сдвиг становится все более и более запутанным по мере уменьшения частоты, и мне также придется добавить предварительный масштабатор для всего, что ниже 62 кГц, — но на 75 кГц он работает!
Я предполагаю, что теперь вопрос не в том, «Как мне получить задержку между двумя PWM?» а скорее: «Как мне заставить задержку масштабироваться с частотой?»
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 показывает вам, как получить прямоугольные импульсы с одновременным фазовым сдвигом (например, 50%-ная нагрузка). 16-разрядный таймер (Nano, Uno, 2560) использует режим без ШИМ, но дает возможность истинного фазового сдвига. Изменения частоты не влияют на установленное вами соотношение фаз.
Код для него включен по следующей ссылке...
Обратите внимание, что 16-разрядный генератор сигналов таймера использует режим Toggle, поэтому выходная частота составляет 1/2 частоты цикла таймера.
большой джош
пользователь3753934
большой джош