Контроллер WeMos XI имеет на борту два 8-битных цифро-аналогового преобразователя (ЦАП) и это одно из его преимуществ перед популярным контроллером Arduino Pro Mini, который цифро-аналоговых преобразователей не имеет. В этой статье мы подробно разберём работу с ЦАП на контроллере WeMos XI и в качестве примера научимся синтезировать сигналы различной формы.
Все настройки и подготовительные действия для работы с контроллером WeMos XI в среде разработки Arduino IDE подробно описаны в материале «Программирование» этой Wiki-документации.
DAC0 и DAC1
Первый ЦАП (DAC0) находится на выводе D4 контроллера WeMos XI, а второй (DAC1) — на выводе E5. Оба цифро-аналоговых преобразователя имеют разрешение 8 бит, это означает, что при их помощи можно изменять выходной сигнал на 256 значений (от 0 до 255).
В качестве опорного можно использовать встроенный калиброванный (±1%) источник напряжения 1,25 или 2,56 В.
Генератор пилообразного напряжения
Знакомство с работой цифро-аналоговых преобразователей контроллера WeMos XI мы начнём с примера создания генератора пилообразного напряжения. Для начала полный код скетча WeMos XI DAC Saw example:
/* WeMos XI DAC Saw example (based on standard example) (c)2020 Electromicro License: GNU GPL 2.1 WeMos XI DAC Saw example */ byte value = 0; void setup() { Serial.begin(115200); Serial.println(F("WeMos XI DAC Saw example start...")); analogReference(INTERNAL2V56); pinMode(DAC0, ANALOG); } void sawRight() { value += 1; } void sawLeft() { value -= 1; } void freqReg() { delayMicroseconds(10); } void loop() { analogWrite(DAC0, value); //freqReg(); sawRight(); //sawLeft(); }
Теперь подробно разберём его работу. Вначале мы объявляем переменную, которая будет содержать текущий уровень сигнала на выходе цифро-аналогового преобразователя DAC0 (вывод D4 rонтроллерf WeMos XI).
byte value = 0;
Переменная value объявляется типа byte, то есть может изменяться от 0 до 255. Поскольку переменная беззнаковая, то при переполнении, значение 256 будет записываться как 0 и т. д.
Далее идёт функция setup(), инициализация Serial и вывод приветственного сообщения о старте нашего скетча:
Serial.begin(115200); Serial.println(F("WeMos XI DAC Saw example start..."));
Вот результат этих действий на скриншоте:
Затем при помощи функции analogReference() устанавливается опорное напряжение 2,56 В для нашего ЦАП.
analogReference(INTERNAL2V56);
И вывод DAC0 (D4) переводится в режим работы ЦАП.
pinMode(DAC0, ANALOG);
Далее следует функция loop(), в которой и происходят основные действия по формированию пилообразного сигнала на выходе DAC0.
void loop() { analogWrite(DAC0, value); //freqReg(); sawRight(); //sawLeft(); }
Некоторые строки закомментированы, они нам понадобятся несколько позже. В этом варианте скетча работают только две строки, первая:
analogWrite(DAC0, value);
устанавливает уровень напряжения на выходе DAC0 в соответствии со значением переменной value, а вторая
sawRight();
Вызывает функцию sawRight(), которая и формирует пилообразную форму сигнала, изменяя значение переменной value по определённому закону
void sawRight() { value += 1; }
просто прибавляя по единице на каждом цикле. Когда значение переменной value переполняется, то весь процесс повторяется снова от 0 до 255. И вот результат работы нашего скетча на скриншоте экрана осциллографа, подключённого к контакту DAC0 (D4) контроллера WeMos XI.
На скриншоте хорошо видна пилообразная форма генерируемого сигнала, частота сигнала 866 Гц и автоматически измеренная амплитуда 2,44 вольта.
Все параметры генерируемого сигнала можно менять, просто изменяя код нашего скетча. Например, можно уменьшить амплитуду сигнала, уменьшить или увеличить его частоту, а также можно изменить его форму.
Левая пила
Для примера давайте изменим наш скетч и вместо правой пилы будем генерировать левую, для этого изменим функцию loop():
void loop() { analogWrite(DAC0, value); //freqReg(); //sawRight(); sawLeft(); }
Мы закомментировали функцию sawRight() и раскомментировали функцию sawLeft():
sawLeft();
И вместо добавления единицы на каждом цикле, будем вычитать её из значения переменной value.
void sawLeft() { value -= 1; }
Результат наших действий на экране осциллографа:
Изменение частоты
Теперь давайте попробуем уменьшить частоту генерируемого сигнала, для этого снова изменим функцию loop():
void loop() { analogWrite(DAC0, value); freqReg(); sawRight(); //sawLeft(); }
Для этого раскомментируем функцию freqReg(), в которой введём задержку в 10 микросекунд в каждом цикле изменения переменной value.
void freqReg() { delayMicroseconds(10); }
Результат полностью соответствует нашим ожиданиям, частота сигнала изменилась с 866 Гц до 321 Гц.
Генератор треугольного напряжения
Теперь давайте создадим генератор треугольного напряжения, тем более, что учитывая полученные знания, это совсем несложно. Полный код скетча WeMos XI DAC Triangle example:
/* WeMos XI DAC Triangle example (based on standard example) (c)2020 Electromicro License: GNU GPL 2.1 WeMos XI DAC Triangle example */ byte value = 1; int step = 1; void setup() { Serial.begin(115200); Serial.println(F("WeMos XI DAC Triangle example start...")); analogReference(INTERNAL2V56); pinMode(DAC0, ANALOG); } void triangle() { value += step; if (value > 254 || value == 0) {step = -step;} } void loop() { analogWrite(DAC0, value); triangle(); }
Поскольку код скетча генерации треугольного сигнала похож на код генератора пилообразного напряжения, то остановимся здесь только на их отличиях.
Объявляем переменную step для приращения изменений сигнала и присваеваем ей значение 1.
int step = 1;
Переменная знаковая, поскольку нам понадобится изменять её значение с 1 на -1 для изменения направления приращения переменной value.
И функция генерации треугольного сигнала:
void triangle() { value += step; if (value > 254 || value == 0) {step = -step;} }
Принцип её работы прост: когда значение переменной value достигает предельных значений, переменная step меняет своё значение на противоположное.
Результат работы нашего скетча:
Генератор меандра
Теперь мы создадим скетч, который генерирует прямоугольные импульсы (т. н. «меандр»). Код скетча WeMos XI DAC Мeander example:
/* WeMos XI DAC Мeander example (based on standard example) (c)2020 Electromicro License: GNU GPL 2.1 WeMos XI DAC Мeander example */ byte value = 0; byte counter = 0; void setup() { Serial.begin(115200); Serial.println(F("WeMos XI DAC Мeander example start...")); analogReference(INTERNAL2V56); pinMode(DAC0, ANALOG); } void meander(byte porosity) { counter += 1; if (counter < porosity) {value = 0;} else {value = 255;} } void loop() { analogWrite(DAC0, value); meander(127); }
Здесь генерацией формы сигнала занимается функция meander()
void meander(byte porosity) { counter += 1; if (counter < porosity) {value = 0;} else {value = 255;} }
Принцип её работы: если текущее значение переменной counter превышает значение переменной porosity (скважность), то устанавливается максимальный уровень сигнала, иначе — устанавливается нулевой уровень.
Функция meander() вызывается со значением скважности 127 (то есть половины от 255).
meander(127);
Результат работы скетча WeMos XI DAC Мeander example :
Изменение скважности
Теперь давайте попробуем изменить скважность генерируемого прямоугольного сигнала. Вместо 127 зададим число 63 (четверть от полного периода сигнала).
meander(63);
Результат наших изменений:
И три четверти от полного периода сигнала (191).
meander(191);
Результат:
Генератор синусоидального сигнала
От генерации простых сигналов перейдём к генерации более сложных и для примера синтезируем широко используемый в различных приложениях синусоидальный сигнал. Код скетча WeMos XI DAC Sinus example:
/* WeMos XI DAC Sinus example (based on standard example) (c)2020 Electromicro License: GNU GPL 2.1 WeMos XI DAC Sinus example */ byte value = 1; float rad = 0.0; void setup() { Serial.begin(115200); Serial.println(F("WeMos XI DAC Sinus example start...")); analogReference(INTERNAL2V56); pinMode(DAC0, ANALOG); } void sinus() { rad += 0.02; if (rad > 6.28) {rad = 0;} value = (sin(rad) + 1) * 127; } void loop() { analogWrite(DAC0, value); sinus(); }
Здесь за генерацию формы и частоты сигнала отвечает функция sinus()
void sinus() { rad += 0.02; if (rad > 6.28) {rad = 0;} value = (sin(rad) + 1) * 127; }
Смысл её в том, чтобы сформировать ряд значений переменной rad (выраженной в радианах) и затем подать его на вход стандартной Arduino-функции sin(), которая и выдаст полный синусоидальный цикл значений для нашего цифро-аналогового преобразователя DAC0.
В строке
value = (sin(rad) + 1) * 127;
синусоидальный сигнал сдвигается на единицу (чтобы избавиться от отрицательных значений) и растягивается по вертикали умножением на 127 (половину от максимальной величины 255).
В результате на выходе ЦАП контроллера WeMos XI мы можем наблюдать отличный синусоидальный сигнал.
Если изменить приращение переменной rad с 0,02 до 0,01
rad += 0.01;
то изменится частота генерируемого сигнала и увеличится точность его соответствия идеальному синусу:
А если увеличить приращение до 0,5
rad += 0.5;
то станет заметна грубая ступенчатость генерируемого сигнала:
Генерация сигналов специальной формы
Таким образом, понимая принцип и используя несложные приёмы, описанные в этой статье, можно формировать сигналы любой формы. Давайте для примера синтезируем сигнал с произвольной формой, геометрия которой будет задаваться математическими выражениями в скетче.
Код скетча WeMos XI DAC Special example:
/* WeMos XI DAC Special example (based on standard example) (c)2020 Electromicro License: GNU GPL 2.1 WeMos XI DAC Special example */ byte value = 1; byte counter = 0; void setup() { Serial.begin(115200); Serial.println(F("WeMos XI DAC Special example start...")); analogReference(INTERNAL2V56); pinMode(DAC0, ANALOG); } void special() { counter += 1; if (counter < 63) {value = 63;} else if (counter >= 64 && counter < 127) {value = 127;} else if (counter >= 128 && counter < 191) {value = 0;} else if (counter >= 192) {value = 255;} } void loop() { analogWrite(DAC0, value); special(); }
Здесь форма генерируемого сигнала задаётся функцией special()
void special() { counter += 1; if (counter < 63) {value = 63;} else if (counter >= 64 && counter < 127) {value = 127;} else if (counter >= 128 && counter < 191) {value = 0;} else if (counter >= 192) {value = 255;} }
Результат работы этого скетча:
Генерация двух сигналов одновременно
Ну и напоследок, рассмотрим пример одновременной генерации двух сигналов. Это будет несложно, поскольку контроллер WeMos XI имеет как раз два цифро-аналоговых преобразователя.
Код скетча WeMos XI DAC Together example:
/* WeMos XI DAC Together example (based on standard example) (c)2020 Electromicro License: GNU GPL 2.1 WeMos XI DAC Together example */ byte value1 = 32; byte value2 = 0; byte counter = 0; void setup() { Serial.begin(115200); Serial.println(F("WeMos XI DAC Together example start...")); analogReference(INTERNAL2V56); pinMode(DAC0, ANALOG); pinMode(DAC1, ANALOG); } void ch1() { value1 += 1; } void ch2() { counter += 1; if (counter > 255) {counter = 0;} if (counter < 127) {value2 = 0;} else {value2 = 255;} } void loop() { analogWrite(DAC0, value1); analogWrite(DAC1, value2); ch1(); ch2(); }
Особых пояснений давать не будем, поскольку, если вы внимательно читали статью, а ещё лучше самостоятельно экспериментировали с приведёнными здесь примерами, то всё происходящее в этом скетче должно быть для вас просто и понятно.
Результат работы этого скетча, мы можем наблюдать одновременную генерацию дух разных сигналов:
Заключение
Цифро-аналоговые преобразователи контроллера WeMos XI являются незаменимым инструментом для решения различных задач в ваших проектах, а также отличным пособием по освоению электроники, микроконтроллерной техники и программирования.
Ссылки по теме
Спецификации и подключение WeMos XI
Где купить?
WeMos XI в магазине «Electromicro»
Техническая поддержка
Мы внимательно относимся к потребностям наших клиентов и осуществляем техническую поддержку всей продаваемой продукции. Вы можете написать нам письмо с вашим вопросом или позвонить по телефону и специалист нашей компании проконсультирует вас и поможет решить вашу проблему.
- Емейл для вопросов по нашей продукции: electromicro@bk.ru
- Наш телефон: +7 (495) 997-37-74