DDS синтез (Direct Digital Synthesizer)
Рассмотрим метод генерации аналоговых сигналов на примере генератора звуковой частоты на основе платы Arduin. Цель данного генератора демонстрации принципа работы DDS алгоритма. Прошивка написана на языке Си для Arduino без ассемблерных вставок, поэтому быстродействие скетча относительно невысокое и не реализует все возможности микроконтроллера Atmegs328.
Но даже этот простой генератор создает синусоидальный сигнал в диапазоне частот от нуля до 30 кГц, что перекрывает весь диапазон звуковых частот.
В качестве цифро-аналогового преобразователя используется простая и дешевая схема R2R на резисторах. Регулировка частоты осуществляется переменным резистором, подключенным к АЦП платы Arduino. Нужно иметь в виду, что в этом учебном генераторе качество выходного сигнала очень сильно зависит от точности подбора резисторов самодельного R2R преобразователя. Чем точнее будут номиналы резисторов, тем с меньшими искажениями будет синусоида на выходе.
Методы синтеза аналоговых сигналов
Генераторы сигналов низкой (звуковой) частоты в радиолюбительской практике необходимы для настройки различных самодельных устройств, таких как усилители мощности звуковой частоты, предварительные усилители, различные каскады, преобразователи и т.д. В электронике существует два основных метода генерации сигналов — аналоговый и цифровой.
В «доцифровую эпоху» практически все самодельные лабораторные генераторы были аналоговыми. Синусоидальные генераторы в основном строились на основе схемы с мостом Вина, либо по классической схеме с фазосдвигающей цепочкой а функциональные генераторы (которые кроме синуса могли выдавать также прямоугольный и треугольный сигнал) — на основе схемы «интегратор — компаратор«. Такая схема отлично производит прямоугольный и треугольный сигнал, но «синусоидальный» сигнал приходилось получать из треугольного с помощью нелинейных элементов (диодов или полевых транзисторов), что не позволяло создавать «синусоиду» с низким уровнем искажений.
В отличие от генератора «интегратор-компаратор», генератор на основе усилителя с мостом Вина в цепи обратной связи, и особенно генераторы с фазосдвигающей цепью позволят сравнительно простыми средствами сделать генератор синусоидального сигнала с малыми искажениями. Такой генератор может потребоваться например для измерения коэффициента гармоник УНЧ.
Цифровые генераторы сигналов
В наше время практически все лабораторные генераторы, которые есть на рынке, создаются на основе цифровых методов синтеза аналоговых сигналов. В качестве примера можно привести вот этот неплохой и недорогой лабораторный генератор сигналов. Все современные цифровые функциональные генераторы сигналов на рынке строятся на основе алгоритма DDS — прямого цифрового синтеза, о кортом и пойдет речь в этой статье далее.
Промышленные генераторы используют специализированные микросхемы DDS. В качестве примера можно привести популярный чип генератора AD9850. В промышленных условиях создание генератора только на одном микроконтроллере нецелесообразно . Однако это легко можно сделать в любительских условиях и получить очень дешевый и простой функциональный генератор.
Как работает АЦП (Аналого-Цифровой Преобразователь)
В общем виде способ оцифровки, обработки/хранения и последующего воспроизведения аналоговых сигналов называется PCM или импульсно — кодовая модуляция. Мы берем какой либо аналоговый сигнал, например наш голос с микрофона. и подаем его на вход специального устройства, которое называется Аналогово Цифровой Преобразователь, АЦП или ADC. На выходе этого преобразователя мы вместо звука имеем последовательность обычных чисел, которые следуют с определенной частотой, которая называется «Частота Дискретизации» или Sampling Rate или Sampling Frequency. Например в стандарте «компакт-диск» или CD-AUDIO принята частота дискретизации, равная 44100 Герц. В данном случае наш АЦП производит замеры уровня сигнала с нашего микрофона 44100 раз в секунду. Числа на выходе АЦП пропорциональны напряжению сигнала. Кроме частоты дискретизации Аналого-цифровой преобразователь имеет также такой параметр как «разрядность» или «разрешение». Этот параметр характеризует точность работы преобразователя. Например если разрядность АЦП равна 8 бит, то максимальному уровню входного напряжения будет соответствовать число равное 2 в степени 8. Получается что если на входе такого АЦП будет напряжение, равное нулю, то АЦП выдаст на выходе число 0. А если на входе будет максимально возможное для данного АЦП напряжение (пусть это будет 5 вольт в нашем примере), то на выходе АЦП выдаст число 255. Таким образом для 8 — разрядного АЦП у нас будет всего 256 различных уровней напряжения. То есть фактически непрерывный аналоговый сигнал на входе такой преобразователь превратит в последовательность ступенек напряжения с шагом 5/256 = 0.01953125В, то есть приблизительно 0.2B.
В стандарте CD-Audio принято разрешение 16 бит. То есть «ступенька» в данном случае будет равна 5/65536 = 0.00007В, и у нас будет уже не 256 а 65536 уровней измерения напряжения.
Цифро-Аналоговый преобразователь типа R2R
Обратное преобразование цифрового кода в аналоговый сигнал осуществляется специальным устройством, которое называется Цифро-Аналоговый Преобразователь, ЦАП или DAC. Существует несколько методов цифро-аналогового преобразования и огромное количество специальных микросхем. которые реализуют эти методы.
Однако самый простой цифро-аналоговый преобразователь — это схема R2R. Это параллельный преобразователь цифрового кода в аналоговый сигнал на основе простого резистивного делителя напряжения. Такой преобразователь легко сделать в радиолюбительских условиях, так как он состоит из резисторов одинакового номинала, которые легко приобрести и подобрать. Требуются резисторы всего двух номиналов, которые отличаются ровно в 2 раза. Например 10к (R) и 20к (2R). Мне нравятся номиналы 10 и 20 так как эти величины присутствуют в стандартной сетке номиналов. Можно сделать преобразователь из резисторов одинаковых номиналов, Предположим что у вас есть много резисторов на 10 к. Тогда в качестве резисторов 2R вы можете использовать одиночные резисторы на 10 к., а в качестве резисторов R — по два параллельно соединенных резистора 10к. суммарное сопротивление двух параллельно соединенных резисторов будет как раз 5к, что нам и требуется.
На рисунке ниже изображена схема четырехразрядного R2R преобразователя на резисторах номиналами 10к и 20к. Для такого преобразователя потребуется 4 выходных порта условного микроконтроллера и на выходе мы сможем получить 16 градаций аналогово напряжения.(2 в степени 4).
Рассматривая R2R преобразователь мы допускаем что КМОП ключи на выходах микроконтроллерах идеальны и в замкнутом состоянии имеют нулевое сопротивление. Однако на самом деле ключи не идеальны и сопротивление не нулевое. Небольшое (но не нулевое) сопротивление замкнутого ключа может повлиять на форму выходного сигнала. Для уменьшения влияния сопротивления ключей номиналы резисторов делителя преобразователя должны быть во много раз больше сопротивления кличей контроллера. Для того, чтобы влияние этих паразитных сопротивлений можно было не учитывать, рекомендуется выбирать сопротивление R в 2n (n — разрядность ЦАП) раз больше сопротивления замкнутого ключа. Сопротивление замкнутого ключа можно узнать из документации на используемый контроллер. В любом случае номиналы 1k/2k, 10k/20k или 5k/10k можно считать близким к идеалу.
Таким образом для построения четырехразрядного преобразователя нам потребуется 3 резистора на 10к и 6 резисторов на 20к. Для 8-разрядного преобразователя будет нужно 7 и 9 резисторов соответственно. В схеме нашего генератора мы будем использовать именно 8 разрядный ЦАП.
Принципиальная схема DDS генератора на Arduino
Я собрал этот учебный генератор на макетной плате breadbord на основе дешевого китайского клона Arduino Nano. Первый вариант был сделан с платой Arduino Uno, но потом я заменил Uno на Nano просто с целью экономии места. Nano удобна тем, что ее можно вставить непосредственно в Breadboard. Платы Arduino Uno и Nano совместимы между собой и практически отличаются только размерами (есть также небольшие схемные отличия, несущественные для нашего проекта). Так что вы можете использовать либо Nano либо Uno, в зависимости от того что есть в наличии.
Восьмиразрядный R2R ЦАП собран на резисторах R1-R16 номиналами 10 и 20к. R17 и C1 образуют фильтр, устраняющий высокочастотные помехи. Осциллограф подключаем к клеммам X1 и X2. Сюда можно также подключить например звуковой усилитель, чтобы услышать сигнал нашего генератора.
Для того чтобы нагрузка не влияла на работу делителя нашего ЦАП хорошо было бы на выходе использовать буферный повторитель на операционном усилителе. Однако для правильной работы обычного ОУ однополярное напряжение +5В недостаточно. Нужно либо использовать дополнительный источник питания для ОУ, либо использовать специальный Rail-to-Rail операционный усилитель, что выходит за рамки данной статьи.
Кнопка SB1 служит для включения и выключения режима установки частоты генератора. При этом включается или отключается встроенный в плату Arduino светодиод. Кнопка подключена к пину D8 Arduino Nano (порт PB0 микроконтроллера).
Потенциометр R18 подключен к аналоговому входу A0 Ардуино (PC0/ADC0 микроконтроллера). Потенциометр служит для регулировки выходной частоты нашего генератора. Напряжение с движка потенциометра поступает на вход аналого-цифрового преобразователя Ардуино и преобразуется программой в код для установки частоты. такая регулировка потенциометром очень удобна и наглядна, однако не позволяет точно установить частоту. Что собственно и не требуется для нашего учебного проекта
#include "PinChangeInterrupt.h" #include <CyberLib.h> #include <avr/interrupt.h> #define btnint 8 //------------------------------------------------------------ const uint8_t sinewave[] = { 0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae, 0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8, 0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5, 0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7, 0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc, 0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3, 0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83, 0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51, 0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27, 0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a, 0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08, 0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23, 0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c, 0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c };//------------------------------------------------------------ uint16_t PhaseShift; //код частоты uint16_t PhaseAccum; //аккумулятор фазы volatile int state = false; //флаг для переключения режима в прерывании //------------------------------------------------------------ void ADC_init(){ //НАСТРОЙКА АЦП // Сбрасываем регистр ADCSRB ADCSRB = 0; //Опорное напряжение - ИСТОЧНИК ПИТАНИЯ АРДУИНО: bitClear(ADMUX, REFS1); bitSet(ADMUX, REFS0); //формат результата bitSet(ADMUX, ADLAR); //8 бит ADCH + 2 бита ADCL //Выбираем КАНАЛ АЦП = AD0 bitClear(ADMUX, MUX3); bitClear(ADMUX, MUX2); bitClear(ADMUX, MUX1); bitClear(ADMUX, MUX0); //Режим АЦП ВКЛЮЧЕН bitSet(ADCSRA, ADEN); //Автозапуск ВКЛЮЧЕН bitSet(ADCSRA, ADATE); //запрещаем прерывания АЦП bitClear(ADCSRA, ADIE); //Предделитель на 128 bitSet(ADCSRA, ADPS2); bitSet(ADCSRA, ADPS1); bitSet(ADCSRA, ADPS0); //Преобразовение остановлено bitClear(ADCSRA, ADSC); }//------------------------------------------------------------ //Обработка прерывания по завершению преобразования АЦП ISR(ADC_vect){ PhaseShift = result; //обновляем код частоты }//------------------------------------------------------------ void setup() { // Настройка параметров микроконтроллера ADC_init(); // Все настройки АЦП вынесены в отдельную процедуру DDRD = B11111111; //порт D (DDS) на выход pinMode(btnint, INPUT_PULLUP); //пин кнопки прерывания - на вход pinMode(LED_BUILTIN, OUTPUT); //пин встроенного светодиода на вывод attachPCINT(digitalPinToPCINT(btnint), btnpressed, FALLING); //подключаем прерывания для кнопки PhaseAccum = 0; //обнуляем аккумулятор фазы PhaseShift = 12; //Код частоты в момент запуска генератора // Цикл вывода таблицы синуса по алгоритму DDS Start PORTD = sinewave[highByte(PhaseAccum)]; //отправляем значение в порт D PhaseAccum = PhaseAccum + (PhaseShift << 4)+1; //Наращиваем аккумулятор фазы End }//------------------------------------------------------------ //Прерывание по нажатию на кнопку void btnpressed(void) { state = !state; digitalWrite(LED_BUILTIN,state); if (state) { bitSet(ADCSRA, ADIE); bitSet(ADCSRA, ADSC); } else { bitClear(ADCSRA, ADIE);} }//------------------------------------------------------------ void loop() { }