Цифро-аналоговые преобразователи (ЦАП) - предназначены для преобразования
цифровых сигналов в аналоговые. Такое преобразование необходимо, например, при
восстановлении аналогового сигнала, предварительно преобразованного в цифровой
для передачи на большое расстояние или хранения (таким сигналом, в частности,
может быть звук). Другой пример использования такого преобразования - получение
управляющего сигнала при цифровом управлении устройствами, режим работы которых
определяется непосредственно аналоговым сигналом, например при управлении
электродвигателями.
В отличии от АЦП, 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);
}