Как я могу адаптировать этот код для ATTiny85?

Поэтому я настроил схему Arduino, чтобы при нажатии кнопки воспроизводился звуковой эффект и активировался светодиод, пока я не перестану нажимать кнопку. Поскольку я не использовал модуль карты micro sd, мне пришлось использовать wav2c для преобразования звука в числовые значения, которые я затем использовал. код здесь для реализации звука. Числовые значения для звука были помещены в заголовочный файл, на который есть ссылка в коде. Код и моя схема работают, но я хочу посмотреть, смогу ли я использовать один из моих новых ATTiny85 для запуска кода и выполнения тех же операций, что и Arduino. Я настроил свой Arduino Mega 2560 для программирования ATTiny85. Я протестировал его с помощью Blink, и он работает . Однако после того, как я переместил контакты динамика, светодиода и кнопки на ATTin85 и отредактировал соответствующий код, он не будет загружаться в ATTiny85. Он выдает сообщения об ошибках, что определенные переменные не были объявлены в этой области. Одной из таких является «TCCR1B», и когда я ее комментирую, она утверждает, что в этой области не объявлена другая переменная. Любая идея, как я могу преодолеть это и возможно ли вообще использовать ATTiny в этом качестве?

Код, который я пытался загрузить в ATTiny85

#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

#define SAMPLE_RATE 20000
#include "Test.h"

int ledPin = 0;
int speakerPin = 1; // Может быть 3 или 11, два выхода ШИМ подключены к Таймеру 2
const byte pinSwitch1 = 2;
volatile uint16_t sample;
byte lastSample;
int buttonState = 0;
const byte interruptPin = 2;
volatile byte state = LOW;

void stopPlayback()
{
    digitalWrite(ledPin, LOW);
    // Запретить прерывание воспроизведения по семплам.
    TIMSK1 &= ~_BV(OCIE1A);

    // Полностью отключить таймер для выборки.
    TCCR1B &= ~_BV(CS10);

    // Отключить таймер PWM.
    TCCR2B &= ~_BV(CS10);

    digitalWrite(speakerPin, LOW);
}

// Это вызывается на частоте 8000 Гц для загрузки следующего семпла.
ISR(TIMER1_COMPA_vect) {
    if (sample >= sounddata_length) {
        if (sample == sounddata_length + lastSample) {
            stopPlayback();
        }
        else {
            if(speakerPin==11){
                // Уменьшить до нуля, чтобы уменьшить щелчки в конце воспроизведения.
                OCR2A = sounddata_length + lastSample - sample;
            } else {
                OCR2B = sounddata_length + lastSample - sample;                
            }
        }
    }
    else {
        if(speakerPin==11){
            OCR2A = pgm_read_byte(&sounddata_data[sample]);
        } else {
            OCR2B = pgm_read_byte(&sounddata_data[sample]);            
        }
    }

    ++sample;
}

void startPlayback()
{
    digitalWrite(ledPin, HIGH);
    pinMode(speakerPin, OUTPUT);

    // Настройте Таймер 2 для выполнения широтно-импульсной модуляции на динамике
    // приколоть.

    // Использовать внутренние часы (данные стр. 160)
    ASSR &= ~(_BV(EXCLK) | _BV(AS2));

    // Установить быстрый режим ШИМ (стр. 157)
    TCCR2A |= _BV(WGM21) | _BV(WGM20);
    TCCR2B &= ~_BV(WGM22);

    if(speakerPin==11){
        // Делаем неинвертирующий ШИМ на выводе OC2A (стр. 155)
        // На Arduino это контакт 11.
        TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
        TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
        // Без предделителя (стр. 158)
        TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);

        // Установить начальную ширину импульса на первую выборку.
        OCR2A = pgm_read_byte(&sounddata_data[0]);
    } else {
        // Выполнить неинвертирующий ШИМ на выводе OC2B (стр. 155)
        // На Arduino это контакт 3.
        TCCR2A = (TCCR2A | _BV(COM2B1)) & ~_BV(COM2B0);
        TCCR2A &= ~(_BV(COM2A1) | _BV(COM2A0));
        // Без предделителя (стр. 158)
        TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);

        // Установить начальную ширину импульса на первую выборку.
        OCR2B = pgm_read_byte(&sounddata_data[0]);
    }





    // Настройте Таймер 1 на отправку выборки при каждом прерывании.

    cli();

    // Установка режима CTC (сброс таймера при совпадении сравнения) (стр. 133)
    // Необходимо установить OCR1A *после*, иначе он сбрасывается на 0!
    TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
    TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));

    // Без предделителя (стр. 134)
    TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);

    // Установить регистр сравнения (OCR1A).
    // OCR1A — это 16-битный регистр, поэтому мы должны сделать это с помощью
    // прерывания отключены для безопасности.
    OCR1A = F_CPU / SAMPLE_RATE;    // 16е6/8000 = 2000

    // Разрешить прерывание, когда TCNT1 == OCR1A (стр. 136)
    TIMSK1 |= _BV(OCIE1A);

    lastSample = pgm_read_byte(&sounddata_data[sounddata_length-1]);
    sample = 0;
    sei();
        // цифровая запись (ledPin, HIGH);
        // задержка (1000);
        // цифровая запись (ledPin, LOW);
}


void setup()
{
    pinMode( pinSwitch1, INPUT );
    pinMode(ledPin, OUTPUT);
    pinMode(interruptPin, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(interruptPin), stopPlayback, CHANGE);

}

void loop()
{
//начатьвоспроизведение();
// задержка (5000);
// остановить воспроизведение();
buttonState = digitalRead(pinSwitch1);


stopPlayback();

if (buttonState == LOW) {
    startPlayback();
    delay(1150);

}

}

Заголовочный файл со звуком

#ifndef _HEADERFILE_H    // Поместите эти две строки в начало файла.
#define _HEADERFILE_H    // (Используйте подходящее имя, обычно основанное на имени файла.)


const int sounddata_length=32000;
//const int sounddata_sampleRate=20000;

const unsigned char sounddata_data[] PROGMEM = {
  15,1,49,0,150,0,138,0,219,255,133,0,176,0,15,1,210,

//Здесь намного больше чисел для генерации звука, но я вырезал большую часть для экономии места

};

#endif // _HEADERFILE_H // Поместите эту строку в конец вашего файла.

, 👍0

Обсуждение

ATTINY85 не имеет того же набора таймеров, что и плата, для которой она была написана. Это похоже на код для UNO (ATMEGA328) или Mega (ATMEGA2560). ATTINY85 не имеет таймера, соответствующего 16-битному Timer1 на этих чипах. Вы действительно не можете использовать это на 85., @Delta_G


1 ответ


1

Как отметил @delta_G, этот код написан специально для доступа к оборудованию таймера на чипе, а ATTINY85 имеет другое оборудование таймера.

Было бы нетрудно изменить этот код для работы с вашим, просто нужно вывести следующий шаг в списке аудио значений 8000 таймеров в секунду.

Есть два способа сделать это...

  1. Вы можете найти соответствующие настройки для аппаратного таймера ATTINY85 и использовать их вместо этого. Регистры таймера задокументированы в таблицах данных ATTINY85, и вы можете найти в Google много информации о том, как установить эти регистры, но это сложно.

  2. Вы можете передавать аудиоданные через цифровой контакт. Bing banging означает, что вы пишете код, который вручную устанавливает высокий или низкий уровень контакта в соответствующее время.

Поскольку не похоже, что вам нужно делать что-то еще во время воспроизведения звука, я думаю, что более простым подходом здесь будет бит-бэг.

Выполняйте битовые удары, вы избавляетесь от функций начала и окончания воспроизведения, а в инсталляции есть функция playSound(), которая выглядит примерно так...

void playSound() {
    pinMode(speakerPin, OUTPUT);  // Это действительно относится к setup()
    for(unsigned sample=0; sample<sounddata_length; sample++) {      // шагаем по аудиосэмплам
        sample_value=pgm_read_byte(&sounddata_data[sample])/2;    // получаем следующее значение из массива во flash

        // Обратите внимание, что мы делим значение на 2. Это потому, что наша целевая скорость
        // 8000 выборок в секунду, что составляет 125 мкс на выборку. Каждое значение выборки равно 0-255, поэтому /2 дает нам
        // значение 0-127. Если мы потратим около 127 мкс на каждый образец, то мы находимся на правильном уровне.
        // для скорости воспроизведения (хорошо, немного медленно).


        digitalWrite( speakerPin , 1);     // одиночный цикл битбанга бедняка PWM
        _delay_us( sample_value );
        digitalWrite( speakerPin , 0);
        _delay_us( 127-sample_value );

     }

    }
}

Это всего лишь псевдокод, и я не проверял, компилируется ли он вообще, но вы должны уловить идею.

Это тоже хак, поэтому не учитывается, сколько времени занимает digitalWrite(). или накладные расходы цикла. Он также явно растягивает время выборки со 125 мкс до 127 мкс для упрощения математических вычислений и скорости (деление 0-255 на 2 выполняется очень быстро). Все это, вероятно, сделает звук воспроизведения медленным и низким, но я не уверен, насколько сильно.

Я также не учитываю, что воспроизведение может прерваться, что приведет к сбоям в звуке. Вы можете исправить это, поместив cli()/sei() вокруг кода playSound().

Наконец, каждый семпл получает только один цикл ШИМ, поэтому я не ожидаю, что звук будет иметь качество CD, но, возможно, достаточно хорошее. Если вам нужно больше циклов ШИМ, то есть способы сделать это, но тогда, вероятно, лучше использовать аппаратный таймер.

ЛМК, если у вас получится (выложите видео!) или у вас есть вопросы!

,