В данной статье рассмотрен вопрос работы ПЛИС с периферийными устройствами, например с 8-канальным 12-битным АЦП ADC128S102 ( datasheet ). Использование этого устройства в любых проектах обусловленно простотой ( АЦП опрашивается по SPI интерфейсу и по нему же АЦП выдает данные с каждого из каналов) и малыми затратами программистов на работу с данным устройством.
В чем же заключается простота работы с SPI переферией?
Дело в том, что достаточно просто подать клок (тактовую частоту), изменить чипселект (CS), тем самым дать переферийному устройству понять, что с ним сейчас работают и подать данные.
На примере нашего ADC128S102 процесс работы происходит следующим образом…
В самом начале ПЛИС выставляет выходную ножку чипселекта (CS) в логическую 1, клок (SCLK) не подается [на самом деле не имеет значения — подается тактовая частота или нет — АЦП не реагирует на нее, т.к. CS свидетельствует о том, что с устройством сейчас не работают], данные по DOUT не идут.
В нашей программе идет циклический опрос каналов АЦП с записью данных в память и последующей отправкой полученных данных по UART на ПК [об этом — в следующей статье].
[php]
module adc(
input fab_clk_8MHz, // Тактовая частота 8 МГц
output reg SENSE_CS = 1, // Два ЦАП
input SENSE_DIN, // Данные, приходящие с АЦП
output reg SENSE_DOUT, // Данные с ПЛИС на АЦП (выбор канала)
output sclk // Тактовая частота для управления АЦП
);
assign sclk = fab_clk_8MHz;
// Переменные
reg [1:0] state = 2’b00;
reg [7:0] data_2adc [8:0];
reg [3:0] cnt_frame= 0;
reg [9:0] cnt = 0;
reg [11:0] data_from_adc [8:0];
reg [11:0] data_adc_save [8:0];
// Начальная инициализация данных для отправки данных на АЦП (по документации — важны лишь
// 6,5,4 биты.
initial
begin
data_2adc[0] = 8’b00_000_000; // none
data_2adc[1] = 8’b11_000_111; // IN0
data_2adc[2] = 8’b11_001_111; // IN1
data_2adc[3] = 8’b11_010_111; // IN2
data_2adc[4] = 8’b11_011_111; // IN3
data_2adc[5] = 8’b11_100_111; // IN4
data_2adc[6] = 8’b11_101_111; // IN5
data_2adc[7] = 8’b11_110_111; // IN6
data_2adc[8] = 8’b11_111_111; // IN7
end
always @ (negedge fab_clk_8MHz)
begin
case (state)
2’b00 : begin
state <= 2’b01;
cnt_frame <= 0;
end
2’b01 : begin
if (cnt_frame < 8)
begin
cnt_frame <= cnt_frame + 1;
state <= 2’b10;
cnt <= 0;
end
else
state <= 2’b00;
end
2’b10 : begin
if (cnt <=50)
begin
cnt <= cnt + 1;
if (cnt <= 15 )
SENSE_CS <= 0;
if (cnt >15 && cnt <= 50)
SENSE_CS <= 1;
end
else
begin
state <= 2’b01;
cnt <= 0;
end
end
endcase
end
// Отправка данных на АЦП
always @ (negedge fab_clk_8MHz)
begin
if (cnt > 0 && cnt <= 7)
SENSE_DOUT <= data_2adc[cnt_frame][7-cnt];
end
// Чтение и запись данных с АЦП
always @ (posedge fab_clk_8MHz)
begin
if (cnt >= 5 && cnt <= 16)
data_from_adc [cnt_frame][16-cnt] <= SENSE_DIN;
else
begin
if (cnt == 18)
data_adc_save[cnt_frame] <= data_from_adc[cnt_frame];
end
end
endmodule
[/php]
Промоделировав код в ModelSim получим результат:
Как видно из рисунка данные SENSE_DOUT отправляются согласно datasheet’у… На данном фрагменте
ПЛИС запрашивает данные канала IN0 (первый канал)…
В более развернутом масштабе получим следующую картинку:
Между опросами я ввел некоторую задержку, ее можно отрегулировать в коде, меняя cnt (сейчас оно равно 50)
Файл с кодом можно скачать(.rar) -> adc