Правильная реализация Timer1 для генерации ШИМ

На Atmel ATmega328P (техническая спецификация) для генерации ШИМ доступно три таймера (таймер 0, таймер 1 и таймер 2).

У меня уже есть то, что мне нужно, используя 8-битный таймер 2, я просто обеспокоен использованием другого таймера вместо таймера 2, потому что таймер 2 используется в различных библиотеках, и я хотел бы иметь больше детализации. Таким образом, я хотел бы использовать 16-битный таймер1.

Вот то, что я использую, чтобы сгенерировать переменный коэффициент заполнения 25 кГц, используя timer2. Для этого примера давайте рассмотрим 35% -ный рабочий цикл:

void setup()
{

/*
16*10^6/ [prescalar] / ([OCR2A]) / 2 = [desired frequency]
16*10^6/ 8 / [OCR2A] / 2 = 25*10^3

Prescalar table for Timer2 (from datasheet section 17-9):
CS22    CS21    CS20
0     0       0       = No clock source (Timer/couter stopped)
0     0       1       = clkT2S/(No prescaling)
0     1       0       = clkT2S/8 (From prescaler)
0     1       1       = clkT2S/32 (From prescaler)
1     0       0       = clkT2S/64 (From prescaler)
1     0       1       = clkT2S/128 (From prescaler)
1     1       0       = clkT2S/256 (From prescaler)
1     1       1       = clkT2S/1024 (From prescaler)
*/

pinMode(3, OUTPUT);
TCCR2B = _BV(WGM22) | _BV(CS21);
TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(COM2B0) | _BV(WGM20);
OCR2A = 40;
OCR2B = 16; //40*0.35=16
}

void loop()
{
}

Чтобы получить тот же результат, используя timer1, должно быть невероятно просто, однако я не знаком с этими регистрами.
Я искал объяснения за пределами таблицы. Я нашел этот пост: Секреты Arduino PWM, однако он охватывает только использование timer2.

Я попробовал следующее в соответствии с предложением Стефана, однако это просто приводит к тому, что оба выхода (D9 и D10) удерживаются в ВЫСОКОМ состоянии:

void setup()
{

pinMode(9, OUTPUT); //D9
pinMode(10, OUTPUT); //D10

// Set GPIO for timer1 output for OC1A and OC1B
//DDRB |= (1 << DDB1) | (1 << DDB2);

ICR1 = 0xFFFF;

// 25% duty cycle
OCR1A = 0x0009;

// 75% duty cycle
//OCR1B = 0xBFFF;

//20.14.1, pg170
// set none-inverting mode
TCCR1A |= ((1 << COM1A1) | (1 << COM1B1));

//Table 20-6, pg171
// Fast PWM mode
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM12) | (1 << WGM13);

// START the timer with no prescaler
TCCR1B |= (1 << CS10);

}

void loop()
{
}

Я попытался изменить все (ICR1, OCR1A, TCCR1A), но единственной комбинацией, которая сделала что-то другое, была следующая, которая дает частоту 25 кГц на D10, и D9 удерживал HIGH, но длительность HIGH застряла на 4 мкс независимо от регистров. (Я только что догадался и проверил с OCR1A, чтобы получить 25 кГц. Я не уверен, почему это работает.)

void setup()
{

pinMode(9, OUTPUT);
pinMode(10, OUTPUT);

// Set GPIO for timer1 output for OC1A and OC1B
//DDRB |= (1 << DDB1) | (1 << DDB2);

ICR1 = 0xFFFF;

// 25% duty cycle
OCR1A = 0x0009;

// 75% duty cycle
//This line causes both outputs to be held HIGH
//OCR1B = 0xBFFF;

//20.14.1, pg170
// set none-inverting mode
TCCR1A |= ((1 << COM1A1) | (1 << COM1B1));

//Table 20-6, pg171
// Fast PWM mode
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM12) | (1 << WGM13);

// START the timer with no prescaler
TCCR1B |= (1 << CS10);

}

void loop()
{
}

Я использую плату Arduino Nano для прототипирования, с D9 и D10 в качестве выходных контактов timer1:

Arduino Nano контакты выделены для B1 и B2
(Изображение из https://bigdanzblog.wordpress.com)

Я пытался сменить доску, но у меня тот же результат.

Вот соответствующая информация из таблицы:

таблица atmega28p с 20-3 по 20-5

таблица atmega328p 20-6

atmega328p Таблица 20-7 Таймер Прескалер

0

Решение

Таймер 1 имеет 2 выхода, OC1A а также OC1B, Оба запускают один и тот же аппаратный таймер и поэтому синхронизируются. Таймер может работать в трех различных режимах: режим быстрого ШИМ, режим ШИМ с коррекцией фазы и режим с коррекцией фазы и частоты. Вам нужно будет выбрать правильный режим для вас, а также правильный прескалер таймера, который подходит для вашего приложения. Ниже приведен пример.

// Timer1 Resolution 16-bit
// Timer1 A output at 25% Duty Cycle, Fast PWM Mode
// Timer1 B output at 75% Duty Cycle, Fast PWM Mode

#include <avr/io.h>

int main(void)
{
// Set GPIO for timer1 output for OC1A and OC1B
DDRB |= (1 << DDB1) | (1 << DDB2);

ICR1 = 0xFFFF;

// 25% duty cycle
OCR1A = 0x3FFF;

// 75% duty cycle
OCR1B = 0xBFFF;

// set none-inverting mode
TCCR1A |= ((1 << COM1A1) | (1 << COM1B1));

// Fast PWM mode
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM12)|(1 << WGM13);

// START the timer with no prescaler
TCCR1B |= (1 << CS10);

while (1);
}
0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]