32U4 имеет таймеры, идентичные 328P, на которых я тестировал вашу проблему. Я использовал Таймер 1, который предлагает лучшее разрешение. Этот таймер может работать в режиме CTC, а канал A может быть привязан к фиксированному выходному выводу в режиме переключения-сравнения-сопоставления. Это делает настройку чрезвычайно простой и не требует логики прерывания. Частотой можно управлять, просто записывая в OCR1A (этот регистр имеет двойную буферизацию, поэтому изменения частоты должны происходить без сбоев) *.
В режиме CTC Таймер 1 имеет выходную частоту:
f n x = f_cpu / (2 * n * (1 + x))
где n - значение предварительного масштабирования, а x - регистр сравнения переполнения. Изучение возможных диапазонов частот на тактовой частоте 16 МГц дает:
| N | f-min | f-max | r-min | r-max | x-100 | x-25k |
+-----+--------+-----------+-----------+-----------+-------+-------+
| 1 | 122.1 | 8,000,000 | 4,000,000 | 0.0019 | n/a | 319 |
| 8 | 15.3 | 1,000,000 | 500,000 | 0.00023 | 9,999 | 39 |
| 64 | 1.91 | 125,000 | 62,500 | 0.000029 | 1,249 | 4 |
| 256 | 0.49 | 31,250 | 15,625 | 0.0000073 | 311 | n/a |
|1024 | 0.12 | 7,812 | 3,906 | 0.0000018 | 77 | n/a |
где N - предварительная настройка масштаба, f-min и f-max - минимальная и максимальная достижимые частоты, r-min и r-max - минимальное и максимальное разрешение по частоте и, наконец, x-100 и x-25k - требуемые значения для OCR1A для Выход 100 Гц и 25 кГц соответственно.
В качестве законченного рабочего примера приведем программу, которая циклически переключает частоты 1 Гц, 2, 5, 10 ... 500 кГц с шагом в 2 секунды, что достаточно для наблюдения за работой осциллографа:
#include <avr/io.h>
#include <util/delay.h>
struct CTC1
{
static void setup()
{
// CTC mode with TOP-OCR1A
TCCR1A = 0;
TCCR1B = _BV(WGM12);
// toggle channel A on compare match
TCCR1A = (TCCR1A & ~(_BV(COM1A1) | _BV(COM1A0))) | _BV(COM1A0);
// set channel A bound pin to output mode
DDRB |= _BV(1); // PB1 on 328p, use _BV(5) for PB5 on 32U4
}
static void set_freq(float f)
{
static const float f1 = min_freq(1), f8 = min_freq(8), f64 = min_freq(64), f256 = min_freq(256);
uint16_t n;
if (f >= f1) n = 1;
else if (f >= f8) n = 8;
else if (f >= f64) n = 64;
else if (f >= f256) n = 256;
else n = 1024;
prescale(n);
OCR1A = static_cast<uint16_t>(round(F_CPU / (2 * n * f) - 1));
}
static void prescale(uint16_t n)
{
uint8_t bits = 0;
switch (n)
{
case 1: bits = _BV(CS10); break;
case 8: bits = _BV(CS11); break;
case 64: bits = _BV(CS11) | _BV(CS10); break;
case 256: bits = _BV(CS12); break;
case 1024: bits = _BV(CS12) | _BV(CS10); break;
default: bits = 0;
}
TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10))) | bits;
}
static inline float min_freq(uint16_t n)
{
return ceil(F_CPU / (2 * n * 65536));
}
};
void setup()
{
CTC1::setup();
}
void loop()
{
for (uint8_t x = 0; x < 6; ++x)
for (uint8_t y = 0; y < 3; ++y)
{
float k = y > 0 ? (y > 1 ? 5 : 2) : 1;
CTC1::set_freq(k * pow(10, x));
_delay_ms(2000);
}
}
int main()
{
setup();
for (;;)
loop();
}
Сигнал наблюдается на PB1 (цифровой вывод 9 на Arduino Uno). Обратите внимание, что на 32U4 канал-A привязан к PB5.
Как любезно прокомментировал Александр З., регистр OCR1A не имеет двойной буферизации в режиме CTC. При переключении частот это может привести к серьезным сбоям, например:
![введите здесь описание изображения](https://i.stack.imgur.com/yPgyu.png)
В зависимости от приложения это может быть легко исправлено зацикливанием занятости (хотя это может плохо работать для очень высоких частот или может вызывать неприемлемые задержки на очень низких частотах):
while (TCNT1 > x)
;
OCR1A = x;
Производство:
![введите здесь описание изображения](https://i.stack.imgur.com/0Efks.png)
person
marangisto
schedule
24.07.2016