Генерация импульса 200 кГц на Arduino Uno в обычном режиме
Мне нужно получить 200 кГц от Arduino Uno. Я использую Timer0
. Я настроил его для работы в обычном режиме, а предделитель установлен на clk/8 (тактовая частота Arduino составляет 16 МГц). TCNT0
установлен на 0xFB
, а код переключает 2 контакта PORTB
, когда устанавливается флаг переполнения таймера.
Вот мой код (я использую Atmel Studio 7):
#include <avr/io.h>
int main(void)
{
DDRB = 0x03;
PORTB |= 0x02;
while (1)
{
TCNT0 = 0xFB;
TCCR0A = 0X00;
TCCR0B = 0x02;
while ( (TIFR0 & 0x01) == 0 );
PORTB ^= 0x03;
TCCR1A = 0x00;
TCCR1B = 0x00;
TIFR0 = 0X01;
}
}
Когда я проверяю на осциллографе, он показывает, что импульс составляет 166 кГц вместо 200 кГц. Хотя я могу использовать режим Fast PWM и прерывания, мне просто интересно, почему это не работает? Я сделал какую-то ошибку?
@Prabhat Narang, 👍2
1 ответ
Лучший ответ:
Это достаточно просто — вы забыли, что всему нужно время.
Ваша временная шкала:
- Настроить таймер
- Установить количество
- Дождитесь переполнения счетчика (33 тика)
- Переключить штифт
- Сделайте что-нибудь с T1 (зачем...?)
- Очистить бит переполнения
- Вернуться к 1.
Вот как это выглядит в сборе:
90: 3b ef ldi r19, 0xFB
92: 42 e0 ldi r20, 0x02
94: 23 e0 ldi r18, 0x03
96: 91 e0 ldi r25, 0x01
while (1) {
98: 36 bd out 0x26, r19 ; TCNT0 = 0xFB;
9a: 14 bc out 0x24, r1 ; TCCR0A = 0X00;
9c: 45 bd out 0x25, r20 ; TCCR0B = 0x02;
while ( (TIFR0 & 0x01) == 0 );
9e: a8 9b sbis 0x15, 0
a0: fe cf rjmp .-4 ; 0x9e <setup+0xe>
PORTB ^= 0x03;
a2: 85 b1 in r24, 0x05
a4: 82 27 eor r24, r18
a6: 85 b9 out 0x05, r24
a8: 10 92 80 00 sts 0x0080, r1
ac: 10 92 81 00 sts 0x0081, r1
b0: 95 bb out 0x15, r25
}
b2: f2 cf rjmp .-28
Итак, предположим, что ожидание переполнения таймера займет 8*4+ 1=33 такта, и ссылаясь на техническое описание, сколько времени занимает каждая инструкция, мы можем сложить все в основном цикле while:
while (1) {
98: 36 bd out 0x26, r19 ; 1
9a: 14 bc out 0x24, r1 ; 1
9c: 45 bd out 0x25, r20 ; 1
while ( (TIFR0 & 0x01) == 0 );
9e: a8 9b sbis 0x15, 0 ; 2 (at end)
a0: fe cf rjmp .-4 ; 33 (while running)
PORTB ^= 0x03;
a2: 85 b1 in r24, 0x05 ; 1
a4: 82 27 eor r24, r18 ; 1
a6: 85 b9 out 0x05, r24 ; 1
a8: 10 92 80 00 sts 0x0080, r1 ; 2
ac: 10 92 81 00 sts 0x0081, r1 ; 2
b0: 95 bb out 0x15, r25 ; 1
}
b2: f2 cf rjmp .-28 ; 2
Вы можете видеть, что это занимает 48 тактов (нас не волнует, что находится во внутреннем while
, за исключением случаев, когда речь идет о завершении, а это только инструкция SBIS
). Для 200 кГц вам нужно 40 тактов. У вас есть 48. 16000000/48 = 333333. Разделите на два для переключения контактов, и вы получите 166666 Гц.
Если вы хотите использовать этот метод, вам необходимо убедиться, что таймер работает совершенно автономно, а флаг переполнения устанавливается с регулярным интервалом, а не только через 32 тика после его запуска.
Вы должны работать в режиме CTC, чтобы он считал от 0 до целевого значения, а затем сбрасывался обратно на 0, устанавливая флаг переполнения. Тогда ваш цикл будет выглядеть так:
while (1) {
if ((TIFR0 & 0x01) == 0x01) {
PORTB ^= 0x03;
}
}
Вся настройка таймера выполняется до цикла, и он просто работает и работает, пока вы не укажете иное.
- ATmega328P - проблема с использованием таймера 2 для генерации тона
- максимальная частота ШИМ на основе прерываний при 500 Гц
- Заставить TCNT оставаться ниже OCRxA на ATmega328P
- Может ли Arduino uno регулировать входную квадратную волну, фазу и частоту только с помощью таймера-счетчика?
- Об управлении светодиодом с помощью кнопки с помощью ATmega328P
- Генерация стабильной частоты
- Можно ли сгенерировать точный тактовый импульс 15 кГц с помощью ардуино?
- Заменить предохранители Arduino Uno (может ли Arduino Uno заменить свои собственные предохранители?)