Необычный аудио АЦП с использованием ПЛИС. Продолжение

Первая часть статьи

Разберем схему чуть глубже.


Любому АЦП требуется источник опорного напряжения.

В этой схеме используется ADR4550, который я получил в качестве бесплатного образца. Этот источник рекомендуют для работы совместно с этой серией АЦП.
Заявленные параметры достаточно высокие: малый шум, высокая точность, температурная стабильность, выдает ток 10 мА.
АЦП допускает диапазон опорного напряжения от 2.5 до 5.0 В.
Регулировку опорного напряжения сделал на делителе из переменного и постоянного резистора по 1 кОм.


Буферный усилитель ADA4940 выполняет две функции:
  • Обеспечивает нагрузочную способность на емкость для обеспечения совместной работы с устройством выборки-хранения АЦП. Appnotes говорит о том, что для обеспечения захвата на частоте 768 кГц, выходная RC-цепочка должна пропускать около 3,6 МГц. Расчет выдал 18 Ом и 2700 пФ.
  • ФВЧ ниже 20 Гц и ФНЧ выше 20 кГц.

Поскольку АЦП не биполярный, выходное напряжение буфера должно быть приведено к входному диапазону АЦП. Этот буфер заточен специально под это. При этом на вход Vocm буферного усилителя нужно подать половину опорного напряжения, которое формируется делителем 2х10 кОм от опорного. Кроме того, к входам буфера можно подключать и дифференциальный и однопроводной сигнал, на выходе будут противофазные сигналы.

Плавно переходим к АЦП AD7982.
Заявленные характеристики:
Разрешение 18 бит;
Частота дискретизации 1Мс/с;
Потребление 7 мВт;
Динамический диапазон 99 дБ.
Картинка из даташита:

На картинке видно, что напряжение питания ядра 2.5 В, опорное 2.5-5.0 В, линии связи — 1.8 — 5 В.
Удобно, что линии связи можно запитать от целевого устройства тем напряжением, которое нужно. Я дополнил схему перемычкой выбора источника Vio, в случае внутреннего источника еще одна перемычка выбирает 2.5 или 3.3 В.

У прибора несколько вариантов обмена с приемником данных: 3х и 4х-проводной, с сигналом готовности и без него.

Я использовал 3х проводной режим без сигнала готовности данных. Из даташита берем максимальное время преобразования = 710 нС, начало передачи данных должно начинаться после окончания преобразования. Так можно сделать полностью синхронную схему.
Понятно, что на частоте 44100 Гц преобразование занимает мизерное время, но на 768 кГц все подойдет вплотную к максимальным границам.

Использование сигнала готовности позволяет сделать асинхронную схему. Я пока этот режим не использую, может позже.

Плавно переходим к написанию прошивки.
Прошивка по ситуации на настоящий момент состоит из трех блоков: тактирования, интерфейса АЦП и формирователя сигнала SPDIF.

Блок тактирования ClocksGen принимает на вход Clk45 сигнал с тактового генератора 45158400 Гц = 1024xFs и выдает необходимые сигналы, синхронизированные между собой, для других блоков:
  • сигнал начала преобразования SoC с частотой 44100 Гц = Fs;
  • тактовые импульсы для АЦП ADCClk с частотой 2822400 Гц = 64xFs;
  • тактовый сигнал для блока SPDIF SPDIFClk с частотой 5644800 Гц = 128xFs.


--@antonluba2014
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity ClocksGen is
    port ( 	
				Xtal45 :  in std_logic; --main system oscillators
				ADCClk : out std_logic; -- 3072000/2822400 = clk/16
				SPDIFClk: out std_logic; --6144000/5644000 = clk/8
				SoC	: out std_logic	-- 48000/44100 = clk/1024
		);
end ClocksGen;

architecture Clocks of ClocksGen is
signal counter : std_logic_vector (9 downto 0) :=  B"0000000000"; 
signal counter2: std_logic_vector (5 downto 0) :=  B"000000";

begin
ADCClk <= counter (3);
SPDIFClk <= counter(2);
counter2 <= counter (9 downto 4);

process (Xtal45)
begin
	if rising_edge(Xtal45) then
		counter <= counter + 1;
		if (counter2 = 0) then
			SoC <= '1';
		elsif
			(counter2 = 46) then
			SoC <= '0';
		end if;
	end if; --rising_edge(Xtal45)
end process;
end Clocks;

Основа блока ClocksGen — 10-разрядный счетчик counter, который увеличивается на 1 на каждом такте Clk45.
Выходные тактовые сигналы берутся как отдельные биты счетчика.
Сигнал начала преобразования SoC формируется отдельно, потому что он несимметричен. Высокий уровень (фаза преобразования) длится 46 тактов, низкий (выдача данных) — 18 тактов. Для этого сигнала из основного счетчика берутся 6 старших бит, которые образуют отдельный счетчик, по сигналу сравнения которого и происходит смена уровня сигнала SoC.


--@antonluba2014
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity AD7982FE is
    port ( 	
				SoC		: in	std_logic;
				Data_o_1	: out std_logic_vector(17 downto 0);-- 18 bits of data
				Data_o_2	: out std_logic_vector(17 downto 0);-- 18 bits of data
				clk_i		:	in std_logic;							-- main clock (64 tacts per conversion)
				adc_sdo_i_1:	in std_logic;
				adc_sdo_i_2:	in std_logic;
				adc_sdi_o:	out std_logic;
				adc_sclk_o	:	out std_logic;
				adc_cnv_o	:	out std_logic
		);
end AD7982FE;

architecture Beh of AD7982FE is
signal data1  : std_logic_vector (17 downto 0) := B"000000000000000000";
signal  data2 : std_logic_vector (17 downto 0) := B"000000000000000000";

begin
adc_sclk_o <=clk_i;
adc_sdi_o <= '1';
adc_cnv_o <= SoC;

process (clk_i)
begin
		-- на каждом такте clk_i
		if rising_edge (clk_i) then
			-- если сброшен cnv
			if (SoC = '0') then
				-- вдвигаем следующий бит
				data1 <= data1 (16 downto 0) & adc_sdo_i_1;
				data2 <= data2 (16 downto 0) & adc_sdo_i_2;
			end if;
		end if;
end process;

process(Soc)
begin
	if rising_edge(SoC) then
		Data_o_1 <= data1;
		Data_o_2 <= data2;
	end if;
end process;

end Beh;

Блок AD7982FE — интерфейс с АЦП.
Практически он состоит из 18-разрядного сдвигового регистра, в который при низком уровне сигнала SoC, по каждому фронту тактового сигнала вдвигается бит с порта ADC_SDO. Еще один процесс — выдача данных законченного преобразования при начале нового преобразования по фронту сигнала SoC.
Сигналы тактирования и начала преобразования просто передаются на АЦП.


--@antonluba2014
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity SampleDivider is
    port (
			Left_in, Right_in : in std_logic_vector (17 downto 0);
			Left_out, Right_out: out std_logic_vector (15 downto 0)
		);
end SampleDivider;

architecture Beh of SampleDivider is
begin
Left_out <= Left_in(17 downto 2);
Right_out <= Right_in (17 downto 2);
end Beh;

SampleDivider — еще один блок, который и блоком то не назовешь — обрезает от 18 бит 16 старших бит и передает на блок SPDIF.

Блок SPDIF использовал готовый, написан товарищем Karsakbayev на Verilog, а я читал книги только по VHDL.
K счастью, Квартус понимает много языков и проект можно составлять из различных блоков как из кирпичиков. Конечно, эти языки похожи, и все равно придется разобраться, как работает этот блок, но пока использовал как есть.

Запуск происходил поздно ночью. Дети спали.





Система состояла из iPad-а на котором играл flac -> небалансным, естественно, кабелем подключен к АЦП -> обычным тюльпанчиком с выхода SPDIF подключен к SPDIF входу TASCAM US-1800 -> выход на наушники AKG K271MK2.

Этот TASCAM мы используем на студии с Маком, но получить звук с цифрового входа под Windows7 x64 оказалось невероятно сложно. Наверное, большая часть времени ушла на борьбу с глюками и перезагрузки.

Вначале оказалось, что не та частота идет на SPDIF, ошибся с битом счетчика. Выяснил, выведя на внешнюю ножку и померяв осциллографом.
Потом оказалось, что данные с АЦП начинают идти до того, как сигнал SoC перейдет на низкий уровень. Оказалось, что adc_sdi, который отвечает за режим выдачи данных, остался в воздухе.

Потом оказалось, что сдвиговый регистр не вдвигает бит. И только после исправления
data1 <= data1 (17 downto 1) & adc_sdo_i_1; --ошибка
на
data1 <= data1 (16 downto 0) & adc_sdo_i_1;

в наушниках появился звук!

Это было просто супер! И хотя работы еще много, выявились недостатки аналоговых цепей, фон в одном канале, первый результат достигнут.

Буду двигаться вперед.
  • +10
  • 05 декабря 2014, 22:40
  • antonluba
  • 2
Файлы в топике: hard_Proteus.zip, soft_Quartus.zip

Комментарии (10)

RSS свернуть / развернуть
Очень интересный проект — слежу с удовольствием. Надеюсь все задуманное получится реализовать!

Простите за глупый вопрос, а как S/PDIF сигнал принять на компе с обычной встроенной картой? Я правильно понимаю что в обычно s/pdif на копм передают по usb с проприетарными дровами устройства?
Спасибо!
0
На материнке почти наверняка есть SPDIF-хедер, там и вход, и выход. Только там TTL, так что и для коаксиала, и для оптики потребуется PHY.
0
Простите за глупый вопрос, а как S/PDIF сигнал принять на компе с обычной встроенной картой? Я правильно понимаю что в обычно s/pdif на копм передают по usb с проприетарными дровами устройства?
Да, я сейчас использую внешнюю звуковуху m-audio transit, у нее есть оптический вход SPDIF. На некоторых материнках есть вход SPDIF.
Я этот формат выбрал потому, что другие способы ввода цифрового потока в компьютер требуют дополнительных устройств, микросхем или программирования, а мне на данном этапе нужно получить результат.
Но на будущее в планах сделать интерфейс USB. Поскольку в проекте есть ПЛИС, можно легко подключать дополнительные блоки и использовать разные стандарты.
Например, для тренировки я подключил ЦАП из этой статьи по I2S-RJ16.
0
Как продвигается проэкт? Есть ли какие новости, помимо тестов в RMAA?
0
Исправил несколько аппаратных ошибок, стабилизатор один заменил, конденсаторов допаял.
Уперся в неверную разводку печатной платы. Думаю над второй версией, возможно в четырех слоях. Осваиваю Альтиум, Квартус, Матлаб.
На частоте 768 кгц не работает так, как должен.
А еще, синтезировал дейимирующий фнч, вставил в проект, а он входы от ацп не использует. Говорит, эти входы никуда не подключены 8-0. На выходе все нули. Разбираюсь.
0
(Преждевременно Ctrl+Enter) ^^^

А если реализовать USB, то как со сторону прием получается? Нужно ли писать свои дрова, или есть какие-то стандартные решения?
0
Вроде стандартные решения есть, накачал проектов, вникаю потихоньку.
Есть реализации Usb микрофона, но там синхронизации нет, а в моем случае нужна асинхронная конечная точка, то есть тактирование от ацп. И вход по I2S. Думаю, на stm32 можно это реализовать, есть для экспериментов stm32f107 и f429. Разбираюсь потихоньку. Очень много информации надо переварить.
0
если опору делить резистивным делителем, то толку от неё почти никакого, из 2ppm/C просто сделали 100ppm/C. а с учётом ФВЧ от 20Гц с тем же успехом можно было питание поделить и отфильтровать, для аудио разницы вообще никакой от того что громкость плавно на доли процента меняться будет.
фильтр перед АЦП на 3МГц не нужен, если перед ним до 20кГц в усилителе полоса ограничена, можно было на те же 20кГц поставить, правда с мелкими ёмкостями чтоб паразиты ESR/ESL/поляризация были поменьше, а так только лишних шумов насобираете из 3МГц полосы, правда совсем немного, в фильтре усилителя они всё равно задавятся.
-1
  • avatar
  • _pv
  • 05 января 2015, 15:07
фильтр перед АЦП на 3МГц не нужен, если перед ним до 20кГц в усилителе полоса ограничена, можно было на те же 20кГц поставить, правда с мелкими ёмкостями чтоб паразиты ESR/ESL/поляризация были поменьше, а так только лишних шумов насобираете из 3МГц полосы, правда совсем немного, в фильтре усилителя они всё равно задавятся.
Руководствовался этой статьей

если опору делить резистивным делителем, то толку от неё почти никакого, из 2ppm/C просто сделали 100ppm/C. а с учётом ФВЧ от 20Гц с тем же успехом можно было питание поделить и отфильтровать, для аудио разницы вообще никакой от того что громкость плавно на доли процента меняться будет.
Исходил из того, что за время записи этими изменениями можно принебречь, зато одинарный переменник для любого количества каналов можно применить.
0
В тему — У меня давняя мечта сделать АЦП и ЦАП с большим динамическим диапазоном и высокой частотой дескретизации равной или более 500КГц. Далее кодек со сжатием без потери данных и писать на HD или SDD типа цифрового магнитофона для лампового High End комплекса.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.