Оглавление:
- Запасы
- Шаг 1: разметка макета
- Шаг 2: Оцените отношение сигнала к уровню шума
- Шаг 3: интегральная нелинейность и дифференциальная нелинейность
- Шаг 4: пропускная способность
- Шаг 5: заключительные мысли
Видео: Как сделать и протестировать лучший ЦАП с ESP32: 5 шагов
2024 Автор: John Day | [email protected]. Последнее изменение: 2024-01-30 11:48
ESP32 имеет 2 8-битных цифро-аналоговых преобразователя (ЦАП). Эти ЦАП позволяют создавать произвольные напряжения в определенном диапазоне (0–3,3 В) с разрешением 8 бит. В этом руководстве я покажу вам, как построить ЦАП и охарактеризовать его производительность, а также сравню его с ЦАП ESP32. Индексы производительности, на которые я буду смотреть, включают
- Уровень шума
- Пропускная способность
- Интегральная нелинейность
- Дифференциальная нелинейность
Для проверки этих показателей я буду использовать ADS1115.
Важно отметить, что ваша оценка всех этих показателей будет настолько точной, насколько точна ваше эталонное устройство (в данном случае ADS115). Например, ADS115 не имеет 16-разрядной точности, когда дело касается смещения напряжения и усиления. Эти ошибки могут достигать 0,1%. Для многих систем эти ошибки можно игнорировать, если абсолютная точность не имеет большого значения.
Запасы
- ADS1115
- Доска ESP32
- макет
- перемычки
- Резистор 5 кОм
- 1 керамический конденсатор Фарад
Шаг 1: разметка макета
Подключите следующие контакты
Между ESP32 и ADS1115
3v3 VDD
GND GND
GPIO22 SCL
GPIO21 SDA
На ADS1115
ADDR GND (ADS115)
Изготовление ЦАП
Есть много способов сделать ЦАП. Самый простой - это фильтр нижних частот для ШИМ-сигнала с помощью резистора и конденсатора. Я мог бы добавить сюда операционный усилитель в качестве буфера, но хотел, чтобы все было просто. Этот дизайн прост и дешев для реализации с любым микроконтроллером, поддерживающим ШИМ. Я не буду здесь останавливаться на теории конструкции (Google PWM DAC).
Просто подключите GPIO255 кОм резистор 1 мкФ конденсатор gnd
Теперь подключите перемычку от точки, где резистор встречается с конденсатором, к A0 на ADS115.
Шаг 2: Оцените отношение сигнала к уровню шума
Чтобы оценить уровень шума, просто запустите сценарий ниже. Чтобы оценить это, мы просто оставляем ЦАП на фиксированном значении и измеряем колебания напряжения во времени.
Из-за конструкции ЦАП шум будет самым большим, когда сигнал ШИМ имеет рабочий цикл 50%. Поэтому здесь мы и будем его оценивать. Мы также оценим ESP32 на том же уровне сигнала. Мы также отфильтруем ЦАП ESP32 с тем же фильтром нижних частот, чтобы измерения были сопоставимы.
Для меня вывод был очевиден. Конструкция PWM имела более высокий SNR на> 6 дБ (это в 2 раза лучше). Явная победа для нового ЦАП. Одна небольшая проблема заключается в том, что в АЦП встроены фильтры, которые определенно улучшают отношение сигнал / шум. Таким образом, может быть трудно интерпретировать абсолютные значения. Если бы я использовал фильтр второго порядка, этого не было бы.
В любом случае код ниже
#включают
#include Adafruit_ADS1115 ads; // библиотека adafruit для adc int16_t adc0; // установка void (void) {Serial.begin (115200); // Запускаем серийный номер ads.setGain (GAIN_TWO); // 2-кратное усиление +/- 2,048 В 1 бит = 0,0625 мВ ads.begin (); // начинаем adc float M = 0; // начальное среднее значение с плавающей запятой Mp = 0; // предыдущее среднее значение с плавающей запятой S = 0; // начальная дисперсия float Sp = 0; // предыдущая дисперсия const int reps = 500; // количество повторов int n = 256; // количество выборок ledcSetup (0, 25000, 8); // устанавливаем частоту ШИМ = 25000 Гц при разрешении 8 бит ledcAttachPin (25, 0); // устанавливаем шим на вывод 25 ledcWrite (0, 128); // установить половину рабочего цикла (самый большой шум) delay (3000); // ждем установления времени float snrPWM [reps]; // массив snrs для ШИМ float snrDAC [reps]; // массив snrs для DAC for (int i = 0; i <reps; i ++) {// цикл по повторениям for (int k = 1; k <(n + 1); k ++) {// цикл по выборкам adc0 = ads.readADC_SingleEnded (0); // получаем чтение M = Mp + (adc0 - Mp) / k; // вычисляем скользящее среднее Mp = M; // установить предыдущее среднее значение S = Sp + (adc0 - Mp) * (adc0 - M); // вычисляем скользящую дисперсию Sp = S; // установить предыдущую дисперсию} // snr в дБ snrPWM = 20 * log10 (3.3 / (sqrt (S / n) *.0625 *.001)); // сбросить значения M = 0; Mp = 0; S = 0; Sp = 0; } ledcDetachPin (25); // отключаем ШИМ от вывода 25 dacWrite (25, 128); // запись в ЦАП delay (3000); // ждем, пока установится (int i = 0; i <reps; i ++) {// то же, что и цикл ШИМ для (int k = 1; k <(n + 1); k ++) {adc0 = ads.readADC_SingleEnded (0); M = Mp + (adc0 - Mp) / k; Mp = M; S = Sp + (adc0 - Mp) * (adc0 - M); Sp = S; } snrDAC = 20 * log10 (3,3 / (sqrt (S / n) *.0625 *.001)); M = 0; Mp = 0; S = 0; Sp = 0; } // строим SNR на одном графике для (int i = 1; i <reps; i ++) {Serial.print ("PWM_SNR (dB):"); Serial.print (snrPWM ); Serial.print (","); Serial.print ("ESP32_SNR (дБ):"); Serial.println (snrDAC ); }} недействительный цикл (void) {}
Шаг 3: интегральная нелинейность и дифференциальная нелинейность
Интегральная нелинейность - это приблизительная мера отклонения между выходным напряжением вашего ЦАП и прямой линией. Чем больше это, тем хуже …
Дифференциальная нелинейность - это приблизительная мера того, насколько наблюдаемое изменение напряжения (от одного кода к другому) отклоняется от того, что можно было бы ожидать от прямой линии.
Результаты здесь были действительно интересными. Во-первых, оба имеют ошибку менее 0,5 фунта / кв. Дюйм (при 8-битном разрешении), что хорошо, но у ШИМ гораздо лучшая интегральная линейность. Оба имеют сравнимую дифференциальную нелинейность, но у ЦАП ESP32 есть очень странные шипы. Более того, метод ШИМ имеет некоторую структуру ошибок. По сути, он чередуется с перерегулированием и занижением правильного напряжения.
Я подозреваю, что это какая-то странная ошибка округления в том, как 8-битный сигнал ШИМ создается на ESP32.
Один из способов исправить это - быстро переключаться между двумя соседними кодами (например, 128, 129) с помощью ШИМ. С аналоговым фильтром нижних частот результирующие ошибки будут в среднем равны нулю. Я смоделировал это в программном обеспечении, и действительно, все ошибки исчезли. Теперь метод ШИМ имеет линейность с точностью до 16 бит!
Anywho код для генерации данных ниже. Вывод будет на последовательном мониторе в формате.csv. Просто скопируйте его в текстовый файл для дальнейшей обработки.
#включают
#include Adafruit_ADS1115 ads; / * Используйте это для 16-битной версии * / int16_t adc0; установка void (void) {Serial.begin (115200); ads.setGain (GAIN_ONE); // 2-кратное усиление +/- 2,048 В 1 бит = 1 мВ 0,0625 мВ ads.begin (); ledcSetup (0, 25000, 8); ledcAttachPin (25, 0); Serial.println («Ожидаемый, наблюдаемый»); ledcWrite (0, 2); задержка (3000); для (int я = 2; я <255; я ++) {ledcWrite (0, я); задержка (100); adc0 = ads.readADC_SingleEnded (0); ожидаемое число с плавающей точкой = (i / 256.0 * 3.3) / 4.096 * 32767; Serial.print (ожидается); Serial.print (","); Serial.println (adc0); }} недействительный цикл (void) {}
Шаг 4: пропускная способность
Я собираюсь определить здесь полосу пропускания как частоту, при которой выходной сигнал ЦАП падает на 3 дБ. Это условность и до некоторой степени произвольная. Например, в точке 6 дБ ЦАП по-прежнему будет выдавать сигнал с амплитудой примерно 50%.
Чтобы измерить это, мы просто пропускаем синусоидальные волны с возрастающей частотой от ЦАП к АЦП и измеряем их стандартное отклонение. Неудивительно, что точка 3 дБ находится на частоте 30 Гц (1 / (2 * pi * 5000 * 1e-6)).
ESP32 может делать 1 мегасэмпл в секунду. Это очевидная победа для ESP32. Его амплитуда вообще не уменьшается в тестовой области с полосой пропускания 100 Гц.
Приведенный ниже код может проверить пропускную способность ЦАП с ШИМ.
#включают
#include Adafruit_ADS1115 ads; / * Используйте это для 16-битной версии * / int16_t adc0; int16_t adc1; установка void (void) {float M; поплавок Mp = 0; поплавок S = 0; float Sp = 0; Serial.begin (115200); ads.setGain (GAIN_ONE); // 1x усиление +/- 4,096 В 1 бит = 2 мВ 0,125 мВ ads.begin (); ledcSetup (0, 25000, 8); ledcAttachPin (25, 0); задержка (5000); Serial.println («Частота, Амплитуда»); для (int я = 1; я <100; я ++) {беззнаковый длинный старт = миллис (); беззнаковое длинное T = millis (); Sp = 0; S = 0; M = 0; Mp = 0; int k = 1; норма поплавка; while ((T - начало) <1000) {int out = 24 * sin (2 * PI * i * (T - начало) / 1000.0) + 128; ledcWrite (0, вне); adc0 = ads.readADC_SingleEnded (0); M = Mp + (adc0 - Mp) / k; Mp = M; S = Sp + (adc0 - Mp) * (adc0 - M); Sp = S; Т = миллис (); k ++; } если (я == 1) {норма = sqrt (S / k); } Serial.print (i); Serial.print (","); Serial.println (sqrt (S / k) / norm, 3); k = 0; }} недействительный цикл (void) {}
И этот код проверит пропускную способность ESP32. Обязательно удалите конденсатор, иначе результаты будут одинаковыми для обоих методов.
#включают
#include Adafruit_ADS1115 ads; / * Используйте это для 16-битной версии * / int16_t adc0; int16_t adc1; установка void (void) {float M; поплавок Mp = 0; поплавок S = 0; float Sp = 0; Serial.begin (115200); ads.setGain (GAIN_ONE); // 1x усиление +/- 4,096 В 1 бит = 2 мВ 0,125 мВ ads.begin (); задержка (5000); Serial.println («Частота, Амплитуда»); для (int я = 1; я <100; я ++) {беззнаковый длинный старт = миллис (); беззнаковое длинное T = millis (); Sp = 0; S = 0; M = 0; Mp = 0; int k = 1; норма плавания; while ((T - начало) <1000) {int out = 24 * sin (2 * PI * i * (T - начало) / 1000.0) + 128; dacWrite (25, выход); adc0 = ads.readADC_SingleEnded (0); M = Mp + (adc0 - Mp) / k; Mp = M; S = Sp + (adc0 - Mp) * (adc0 - M); Sp = S; Т = миллис (); k ++; } если (я == 1) {норма = sqrt (S / k); } Serial.print (i); Serial.print (","); Serial.println (sqrt (S / k) / norm, 3); k = 0; }} недействительный цикл (void) {}
Шаг 5: заключительные мысли
Новая конструкция ЦАП выигрывает по линейности и шуму, но проигрывает по полосе пропускания. В зависимости от вашего приложения один из этих показателей может быть более важным, чем другой. С помощью этих процедур тестирования вы сможете объективно принять это решение!
Кроме того, я думаю, что здесь стоит указать, что, поскольку выход ШИМ имеет низкий уровень шума, с исключительной линейностью можно создать ЦАП с гораздо более высоким разрешением с выходом ШИМ (возможно, даже с 16-битной точностью). Это потребует некоторой работы. А пока я прощаюсь с вами!
Рекомендуемые:
Как сделать крутые часы своими руками - StickC - Сделать просто: 8 шагов
Сделай сам Как сделать классные часы - StickC - Легко сделать: в этом уроке мы узнаем, как запрограммировать ESP32 M5Stack StickC с Arduino IDE и Visuino для отображения времени на ЖК-дисплее, а также установить время с помощью кнопок StickC
Аудио ЦАП-усилитель-стример Raspberry Pi: 14 шагов
Raspberry Pi Audio Dac-Amp-Streamer: преобразование устаревшей голосовой шляпы Google AIY в специальное устройство для потоковой передачи стереозвука без наушников. Теперь, когда голосовым системам Google AIY приближается двухлетний возраст, вы, возможно, обнаружили, что новинка немного устарела. Или вам может быть интересно, просматриваете ли вы
ESP32: знаете ли вы, что такое ЦАП?: 7 шагов
ESP32: Знаете ли вы, что такое ЦАП? Сегодня мы поговорим о двух вопросах. Первый - это ЦАП (цифро-аналоговый преобразователь). Считаю это важным, потому что через него, например, мы делаем аудиовыход в ESP32. Вторая проблема, которую мы собираемся рассмотреть сегодня, - это осцил
Как сделать лучший удлинитель: 4 шага
Как сделать лучший удлинитель: Как сделать лучший удлинитель - Молниезащита - Шумоподавление - Поглощение скачков напряжения
Сделай сам USB-усилитель ЦАП !: 5 шагов (с изображениями)
Сделай сам USB-усилитель ЦАП !: Эй! В этом уроке я расскажу вам, как сделать свой собственный USB-ЦАП с усилителем внутри! Не ожидайте слишком многого от качества звука … Также прочтите мое другое творение: Самодельный USB-ЦАП с усилителем! ПРИМЕЧАНИЕ: Прослушивание на высоком уровне. объем на длительный период