Часть 4: Генерация звука через таймер CTC (без библиотеки tone())

Стандартная tone() использует таймер 2 и дает неплохой звук, но у нее есть ограничения: нельзя играть больше одной ноты и сложно менять громкость.

Хак: Используем Timer1 в режиме CTC (Clear Timer on Compare Match). Это дает нам:

  • Генерацию прямоугольного сигнала с частотой до 4 МГц.
  • Возможность менять скважность (громкость) через ШИМ.
  • Независимость от других таймеров (таймер 0 можно оставить для millis()).

Код для генерации звука на пине 9 (OC1A):

void setupSound() {
    // Настраиваем пин 9 как выход
    DDRB |= (1 << PB1);  // PB1 = пин 9
    
    // Настройка Timer1 в режим CTC
    TCCR1A = 0;
    TCCR1B = 0;
    
    // Устанавливаем частоту: 440 Гц (нота Ля)
    // Частота = F_CPU / (2 * прескалер * (OCR1A + 1))
    // Для 16 МГц и прескалера 64: OCR1A = 16000000/(2*64*440) - 1 = 283
    OCR1A = 283;
    
    // Включаем режим CTC (WGM12 = 1)
    TCCR1B |= (1 << WGM12);
    
    // Включаем прескалер 64 (CS10 и CS11 = 1)
    TCCR1B |= (1 << CS10) | (1 << CS11);
    
    // Включаем выход на пине OC1A (сравнение сбрасывает пин в 0)
    TCCR1A |= (1 << COM1A0);
}

// Изменяем частоту (ноту) в любой момент
void setFrequency(uint16_t frequency) {
    if (frequency == 0) {
        TCCR1A &= ~(1 << COM1A0);  // Выключаем звук
        return;
    }
    OCR1A = (F_CPU / (2 * 64 * frequency)) - 1;
    TCCR1A |= (1 << COM1A0);  // Включаем звук
}

void loop() {
    // Играем гамму!
    uint16_t notes[] = {262, 294, 330, 349, 392, 440, 494, 523}; // До-Ре-Ми...
    for (int i = 0; i < 8; i++) {
        setFrequency(notes[i]);
        delay(300);
    }
    setFrequency(0);
    delay(1000);
}

Фишка: Если подключить к пину 9 динамик через конденсатор 100 мкФ, получите чистый звук без щелчков, который можно использовать как сирену для сигнализации или простой синтезатор.

Вам также может понравиться

About the Author: ардуинчиков

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *