Постоянная частота дискретизации АЦП на ESP8266 или ESP32

Я хочу сэмплировать ввод звука с помощью ESP8266 (предпочтительнее) или ESP32. Поскольку я все равно использую фильтр нижних частот, частота дискретизации 1000-2000 Гц была бы вполне приемлемой. Что более важно, так это то, что она постоянна. Могу ли я использовать DMA для этого? Если да: как? Или я должен идти через прерывания таймера? Я знаю о проблеме с Wi-Fi на ESP8266, но я намерен просто отключить Wi-Fi, так как он мне не нужен для этого проекта.

, 👍2


2 ответа


0

Вы смотрели пример "BlinkWithoutDelay" на Arduino? Наиболее ценной его частью является следующее:

void loop() {

    unsigned long currentMillis = millis();   // ПРИМЕЧАНИЕ: 1

    if (currentMillis - previousMillis >= interval) {  // ПРИМЕЧАНИЕ: 2
         // сохранить время последнего мигания светодиодом
         previousMillis = currentMillis;  //ЗАМЕТКА 3
         .. do your thing here ..
    }
}

Учитывайте места от ПРИМЕЧАНИЕ 1 до ПРИМЕЧАНИЕ 3, которые я отметил в комментариях:

ПРИМЕЧАНИЕ 1:

Первое, что вы делаете в цикле, где вы в основном работаете, — это текущее время в миллисекундах. Для достижения этого не требуется больших усилий от Arduino.

ПРИМЕЧАНИЕ 2:

Во-вторых, сравнение: ушло ли достаточно времени, чтобы сделать вашу вещь (= чтобы взять образец) или нет? Если это неверно, вы делаете еще один раунд. Если правда, сделай! Опять же, без больших усилий с компьютера.

ПРИМЕЧАНИЕ 3:

Здесь вы сбрасываете таймер, ДО чего-либо еще. Таким образом, ваши «интервальные часы выборки» довольно точны, они состоят из трех маленьких шагов внутри компьютера и должны работать с точностью до долей секунды (оценка времени = (1 / тактовая частота Arduino) * некоторые машинные команды).

Другие команды: взятие образца, его сохранение или отправка должны выполняться в пределах одной миллисекунды, оставшейся от этих команд. Обратите внимание: 1000 выборок дают время 1 мс, а 2000 выборок — 0,5 мс. Этот пример идеально подходит для случая с 1000 выборками, поскольку точность ваших часов составляет 1 мс. Я бы согласился с этим, если это возможно.

Оставшееся время, 1 мс, довольно велико для времени работы машины, но вы должны убедиться, что некоторые другие компоненты, такие как соединение WiFi, которое вы упомянули, могут быть вредными для этого подхода. Другая часть кода внутри цикла не может занимать более 1 мс, иначе это повлияет на постоянную скорость. Все, что меньше 1 мс, в порядке, этот алгоритм не делает разницы между 0,1 мс или 0,9 мс тяжелой работы внутри if, всегда сохраняется точность 1-10 машинных слов.

(Я точно не знаю, какова длина команды if и функции millis(). Если вы хотите быть очень точным, вы можете принять это во внимание, но, ИМХО, вы можете опустить их важность в расчетах: независимо от того, занимают они какое-то время или нет, в любом случае их выполнение может быть достаточно хорошим, занимая одно и то же в каждом раунде - за постоянное время!

ИСТОЧНИК:

https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay

,

и как именно вы хотите получить частоту выше 1000 Гц с помощью «миллисов»? : P это очень неточное решение., @Julian Finn

Это точно только для 1000 Гц, вы правы., @mico


5

У вас есть несколько вариантов управления частотой дискретизации.

Только программное обеспечение

Это самое простое, и предыдущий ответ дал почти правильный ответ. Здесь мое мнение:

const SAMPLE_INTERVAL = 500;  // 500 микросекунд

void loop() {
    static unsigned long last_sample_time;
    if (micros() - last_sample_time >= SAMPLE_INTERVAL) {  // примечание 1
         last_sample_time += SAMPLE_INTERVAL;  // заметка 2
         // Возьмите образец здесь.
    }
}

Примечание 1. На платах на базе AVR millis() нет точности миллисекунда. Обычно опаздывает, компенсируйте это прыжками. сразу на две миллисекунды. Для управления процессом, который должен происходит ровно каждую 1 мс, millis() явно нет подходящий. Я не знаю, насколько хороша millis() ESP8266. Это может хорошо быть лучше, чем версия AVR. Однако даже идеальный millis() не подойдет для чего-либо быстрее 1 кГц.

Решение этой проблемы довольно тривиально: просто посчитайте время в микросекунды вместо миллисекунд. Единственный недостаток использования micros() заключается в том, что вы не можете измерять продолжительность больше, чем примерно 71,6 минут, что, в свою очередь, означает, что вы не можете использовать его для семплирования на любая частота ниже 233 мкГц. Очевидно, это не то, что вы должны быть обеспокоены. Обратите внимание, что ролловер micros() не является проблема, если вы производите выборку быстрее, чем период пролонгации.

Примечание 2. Невозможно иметь постоянную частоту дискретизации, если она управляется программным обеспечением. Лучшее, на что вы можете надеяться, это жесткий контроль над средний период выборки. Это достигается обновлением last_sample_time как

last_sample_time += SAMPLE_INTERVAL;

Обратите внимание, что эта переменная содержит время, когда предполагался последний образец. быть взятым, а не время, когда оно было фактически взято. Образец, вероятно, был немного отстает от графика, так как все, что делает ЦП, требует времени. Обновление last_sample_time таким образом означает, что эти ошибки синхронизации являются не суммируется. Они будут кумулятивными, если вы сделаете это в «Мгновении». без промедления», а именно

previousMillis = currentMillis;

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

Программное обеспечение, запускаемое прерываниями

Я никогда не программировал таймер ESP, но если вы можете, он должен обеспечить у вас лучшие тайминги, чем в версии только для программного обеспечения. Есть таймер доставить прерывание в желаемый период выборки и использовать ISR для запуск АЦП. Вы можете дождаться результата внутри ISR, сохранить его в ОЗУ и установите флаг, чтобы сообщить основному циклу, что новый семпл доступно, как в

volatile int analog_sample;
volatile bool analog_sample_valid;

ISR(whatever_timer_interrupt)
{
    analog_sample = analogRead(ANALOG_PIN);
    analog_sample_valid = true;
}

void loop()
{
    if (analog_sample_valid) {
        int analog_sample_copy = analog_sample;  // примечание 1
        analog_sample_valid = false;
        // обрабатывать здесь Analog_sample_copy
    }
}

Примечание 1. На 8-битном Arduino вам нужно будет сделать это с помощью прерывания отключены. Вам не нужно отключать прерывания на 32 разрядная платформа.

Этот метод должен значительно уменьшить дрожание по сравнению с только программная версия. Теперь выборка не должна ждать программа для выполнения цикла и достижения точки, когда время проверено. Вместо этого, что бы ни делала программа, она будет немедленно прерван для проведения выборки. Ну, почти. Время от времени это произойдет, что прерывание сработает, когда программа работает с прерывания отключены. Это может быть в том случае, если он уже обслуживается другое прерывание. Когда это произойдет, отбор проб придется подождать. пока прерывания снова не будут разрешены. Вы получаете дрожание, которое большую часть времени небольшую долю микросекунды, а затем иногда образец запаздывает на несколько микросекунд.

Только оборудование

На AVR можно настроить автоматический запуск АЦП при таймер или даже автоматически запускать себя в так называемом «бесплатном рабочий режим». Я не знаю особенностей работы АЦП в ESP8266. Вы можете прочитать техническое описание и посмотреть, предлагает ли оно подобное функциональность. Если вы можете включить автозапуск АЦП, то у вас будет с точностью до цикла. Стабильность вашей выборки будет просто так же хорошо, как кварцевый кристалл, синхронизирующий ваш микроконтроллер. Нет программного решения может когда-нибудь приблизиться к этому уровню совершенства. Единственный недостаток этот метод заключается в том, что вам придется изучить техническое описание вашего ESP в чтобы реализовать это.

,