Цифро-аналоговые преобразователи (ЦАП) - предназначены для преобразования
цифровых сигналов в аналоговые. Такое преобразование необходимо, например, при
восстановлении аналогового сигнала, предварительно преобразованного в цифровой
для передачи на большое расстояние или хранения (таким сигналом, в частности,
может быть звук). Другой пример использования такого преобразования - получение
управляющего сигнала при цифровом управлении устройствами, режим работы которых
определяется непосредственно аналоговым сигналом, например при управлении
электродвигателями.
В отличии от АЦП, Arduino не содержит встроенных ЦАП-ов. Но его легко можно
создать, используя резисторы.
На прошлом уроке мы преобразовали аналоговый сигнал в цифровой:
010 101 111 101 001 000 011 110 101 100 010 011 110
Попробуем теперь преобразовать этот код обратно в аналоговый сигнал.
Для этого соберем простейший ЦАП, который представляет собой набор резисторов:
Так как при преобразовании аналогового сигнала в цифровой мы использовали три
разряда, то и для обратного преобразования наш ЦАП имеет три разряда
(входы in1, in2, in3).
Смоделируем эту схему в Tinkercad и посмотрим как будет работать наш
резисторный ЦАП. Сигнал будем формировать с помощью платы Ardiuno, задействовав
пины D0-D3, и добавим в схему еще три светодиода, для индикации состояния
соответствующих пинов. Сразу загрузим простую программу, которая будет перебирать
последовательно все возможные состояния на этих пинах: 000, 001, 010, 011, 100
101, 110, 111. Таких состояний возможно 8 (2 в третьей степени, так как у нас
три разряда). Программу не привожу, так как она очень простая и вы легко сможете
написать её сами.
void setup() { pinMode(0, OUTPUT); pinMode(1, OUTPUT); pinMode(2, OUTPUT); } void loop() { digitalWrite(0,0); digitalWrite(1,1); digitalWrite(2,0); delay(1); digitalWrite(0,1); digitalWrite(1,0); digitalWrite(2,1); delay(1); digitalWrite(0,1); digitalWrite(1,1); digitalWrite(2,1); delay(1); digitalWrite(0,1); digitalWrite(1,0); digitalWrite(2,1); delay(1); digitalWrite(0,1); digitalWrite(1,0); digitalWrite(2,0); delay(1); digitalWrite(0,0); digitalWrite(1,0); digitalWrite(2,0); delay(1); digitalWrite(0,1); digitalWrite(1,1); digitalWrite(2,0); delay(1); digitalWrite(0,0); digitalWrite(1,1); digitalWrite(2,1); delay(1); digitalWrite(0,1); digitalWrite(1,0); digitalWrite(2,1); delay(1); digitalWrite(0,0); digitalWrite(1,0); digitalWrite(2,1); delay(1); digitalWrite(0,0); digitalWrite(1,1); digitalWrite(2,0); delay(1); digitalWrite(0,1); digitalWrite(1,1); digitalWrite(2,0); delay(1); digitalWrite(0,0); digitalWrite(1,1); digitalWrite(2,1); delay(1); }
Сигнал на экране осциллографа полностью соответствует нашему оцифрованному
сигналу, а чтобы он был больше похож на исходный аналоговый сигнал - добавим на
выходе простой фильтр, из резистора и конденсатора:
Конечно, сигнал не очень точно повторяет исходный сигнал, но чем больше разрядов
будет использовано при преобразовании, и чем выше будет скорость преобразования,
тем больше восстановленный сигнал будет соответствовать исходному.
Достоинство данной схемы в том, что для создания многоразрядного ЦАП,
используются резисторы всего двух номиналов. На заре компьютерной эры именно
эта схема получила широкое распространение, заменяя собой дорогие звуковые
карты. Восемь бит (или один байт) - это стандартный размер параллельного порта,
к которому и подключался covox.
Точно также смоделируем эту схему в Tinkercad:
У этого ЦАП уже 256 состояний выхода (2 в восьмой степени, так как теперь у нас
восемь разрядов). С помощью этого ЦАП уже можно довольно точно воспроизводить
оцифрованный сигнал, но для этого он должен быть соответствующим образом и
оцифрован. Конечно, писать программу для такого ЦАП, используя функцию
digitalWrite(); довольно громоздко, поэтому применим другой способ.
Сначала разберемся с таким понятием как "порт". Порт - это совокупность пинов. В ардуино есть три такие совокупности:
void setup() { pinMode(0, OUTPUT); pinMode(1, OUTPUT); pinMode(2, OUTPUT); pinMode(3, OUTPUT); pinMode(4, OUTPUT); pinMode(5, OUTPUT); pinMode(6, OUTPUT); pinMode(7, OUTPUT); }И это будет работать, но можно сделать проще:
void setup() { DDRD = B11111111; // все 8 пинов порта D программируем на выход }Или даже так:
void setup() { DDRD = 0xFF; // FF - это шестнадцатеричное написание бинарного 11111111 }DDRx - регистр направления порта, в нашем случае все пины на выход (8 единиц в двоичной системе исчисления), но если бы нам нужно было установить все пины порта на вход, то потребовалось бы восемь нулей. Можно также часть пинов запрограммировать на вход, а часть на выход, например:
DDRD = B11111110; // назначает выводы Arduino 1-7 выходными, вывод 0 - входнымТеперь нужно записать в регистр порта данные. Сделать это можно командой PORTx - регистр данных порта, например так:
PORTD = B10101011; // Выводим значение 10101011 в порт DВместо конкретного значения можно вывести в порт также значение какой-либо переменной. Рассмотрим следующую программу:
uint8_t value = 0; // Переменная типа Byte, можно было бы написать byte value = 0; void setup() { DDRD = 0xFF; // все 8 пинов порта D программируем на выход } void loop() { PORTD = value++; // Выводим значение переменной value в порт D delay(100); }Введенная переменная value, может принимать значения от 0 до 255, в порт последовательно выводятся все значения переменной по порядку, увеличиваясь каждый раз на единицу.
uint8_t value = 0; // Переменная типа Byte void setup() { DDRD = 0xFF; // все 8 пинов порта D на выход } void loop() { PORTD = B00000010; // можно написать PORTD = 2; выводим значения в порт D delay(10); PORTD = B00000101; // можно написать PORTD = 5; выводим значения в порт D delay(10); PORTD = B00000111; // можно написать PORTD = 7; выводим значения в порт D delay(10); PORTD = B00000101; // можно написать PORTD = 5; выводим значения в порт D delay(10); PORTD = B00000001; // можно написать PORTD = 1; выводим значения в порт D delay(10); PORTD = B00000000; // можно написать PORTD = 0; выводим значения в порт D delay(10); PORTD = B00000011; // можно написать PORTD = 3; выводим значения в порт D delay(10); PORTD = B00000110; // можно написать PORTD = 6; выводим значения в порт D delay(10); PORTD = B00000101; // можно написать PORTD = 5; выводим значения в порт D delay(10); PORTD = B00000100; // можно написать PORTD = 4; выводим значения в порт D delay(10); PORTD = B00000010; // можно написать PORTD = 2; выводим значения в порт D delay(10); PORTD = B00000011; // можно написать PORTD = 3; выводим значения в порт D delay(10); PORTD = B00000110; // можно написать PORTD = 6; выводим значения в порт D delay(10); }