Работа FPGA с SPI периферией

В данной статье рассмотрен вопрос работы ПЛИС с периферийными устройствами, например с 8-канальным 12-битным АЦП ADC128S102 ( datasheet ). Использование этого устройства в любых проектах обусловленно простотой ( АЦП опрашивается по SPI интерфейсу и по нему же АЦП выдает данные с каждого из каналов) и малыми затратами программистов на работу с данным устройством.

В чем же заключается простота работы с SPI переферией?

Дело в том, что достаточно просто подать клок (тактовую частоту), изменить чипселект (CS), тем самым дать переферийному устройству понять, что с ним сейчас работают и подать данные.

На примере нашего ADC128S102 процесс работы происходит следующим образом…

adc128s102-time

В самом начале ПЛИС выставляет выходную ножку чипселекта (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 получим результат:
1_%d0%ba%d0%b0%d0%bd%d0%b0%d0%bb
Как видно из рисунка данные SENSE_DOUT отправляются согласно datasheet’у… На данном фрагменте
ПЛИС запрашивает данные канала IN0 (первый канал)…
В более развернутом масштабе получим следующую картинку:
%d0%b2%d1%81%d0%b5_%d0%ba%d0%b0%d0%bd%d0%b0%d0%bb%d1%8b
Между опросами я ввел некоторую задержку, ее можно отрегулировать в коде, меняя cnt (сейчас оно равно 50)
Файл с кодом можно скачать(.rar) -> adc

Добавить комментарий