Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Реализация 1-wire
Форум разработчиков электроники ELECTRONIX.ru > Программируемая логика ПЛИС (FPGA,CPLD, PLD) > Работаем с ПЛИС, области применения, выбор
greyrus
Добрый день!
Есть такая задача, подключить к ПЛИС Xilinx XC3S50AN несколько датчиков температуры DS18b20 и считывать с них значение температуры.
С плис начал работать совсем недавно. Подскажите в какую сторону копать, может у кого есть опыт таких разработок. Буду благодарен за любую помощь.
Kuzmi4
Цитата(greyrus @ Mar 1 2012, 16:59) *
...Подскажите в какую сторону копать...

Например в эту.
(я так понимаю в гугуле завелись жёсткие админы которые банят biggrin.gif всех начинающих...)
alexadmin
Цитата(greyrus @ Mar 1 2012, 18:59) *
Добрый день!
Есть такая задача, подключить к ПЛИС Xilinx XC3S50AN несколько датчиков температуры DS18b20 и считывать с них значение температуры.
С плис начал работать совсем недавно. Подскажите в какую сторону копать, может у кого есть опыт таких разработок. Буду благодарен за любую помощь.


У Xilinx был готовый проект для чтения по 1-wire. Представлял он жуткий говнокод, но у него было неиспоримый плюс: он работал. Поищите на их сайте.

PS Первая же ссылка в гугле по запросу "xilinx 1-wire" ведет на XAPP198 с подробным описанием. Где-то рядом есть и готовый проект
BSACPLD
Делал на SV, но переписать на обычный Verilog не очень сложно.
CODE

`timescale 1 ns / 1 ps

module DS18X20_CTRL
#(
parameter [8:0] CLKDIV = 9'd80
)
(
input clk,
input read,
output reg [15:0] temp,
output error,
inout IO
) ;

localparam start = 3'd0, reset_1wire = 3'd1, check_termometer = 3'd2, write_cmd = 3'd3, wait_convert = 3'd4, read_scratchpad = 3'd5 ;
reg [2:0] state = start ;
reg [2:0] next_state = start ;

reg [8:0] divider = 0 ;
wire divider_cout ;
reg divider_cout_clk = 1'b0 ;
reg [8:0] timer = 0 ;
wire timer_cout ;
reg [8:0] toggle_cmp = 0 ;
wire toggle ;
wire read_1wire ;
reg [15:0] shift = 0 ;
reg [15:0] buffer = 0 ;
reg error_n = 1'b0 ;
reg [6:0] bit_cnt = 0 ;
wire bit_cnt_cout ;
reg out_reg_n = 1'b0 ;
reg [1:0] in_reg = 0 ;
reg [7:0] CRC = 0 ;
wire XOR0 ;


initial begin
temp <= 0 ;
end


assign error = ~error_n ;
assign IO = (out_reg_n)? 1'b0:1'bZ ;

assign divider_cout = (divider == 0) ;
assign timer_cout = (timer == 0) ;
assign toggle = (timer == toggle_cmp) ;
assign read_1wire = (timer == 9'd60) ;
assign bit_cnt_cout = (bit_cnt == 0) ;
assign XOR0 = CRC[0] ^ in_reg[1] ;


always_ff @(posedge clk) begin
if (read) temp <= buffer ;
end

always_ff @(posedge clk) begin
in_reg <= (in_reg << 1) | IO ;
end

always_ff @(posedge clk) begin
if (divider_cout) divider <= CLKDIV ;
else divider <= divider - 1'b1 ;

divider_cout_clk <= divider_cout ;
end


always_ff @(posedge clk) begin
if (divider_cout_clk) begin
case (state)
start:
if (timer_cout) begin
state <= reset_1wire ;
next_state <= wait_convert ;
timer <= 9'd500 ;
shift <= 16'h44CC ;
bit_cnt <= 7'd16 ;
out_reg_n <= 1'b1 ;
end
else begin
timer <= timer - 1'b1 ;
end

reset_1wire:
if (timer_cout) begin
state <= check_termometer ;
timer <= 9'd500 ;
toggle_cmp <= 9'd432 ;
out_reg_n <= 1'b0 ;
end
else begin
timer <= timer - 1'b1 ;
end

check_termometer:
if (timer_cout) begin
state <= write_cmd ;
end
else begin
timer <= timer - 1'b1 ;

if (toggle & in_reg[1]) begin
state <= start ;
error_n <= 1'b0 ;
end
end

write_cmd:
begin
if (timer_cout) begin
if (bit_cnt_cout) begin
state <= next_state ;
end
else begin
timer <= 9'd70 ;

if (shift[0]) toggle_cmp <= 9'd65 ;
else toggle_cmp <= 9'd5 ;

shift[15:0] <= {in_reg[1], shift[15:1]} ;
bit_cnt <= bit_cnt - 1'b1 ;
out_reg_n <= 1'b1 ;
end
end
else begin
timer <= timer - 1'b1 ;

if (toggle) out_reg_n <= 1'b0 ;
end
end

wait_convert:
begin
if (timer_cout) begin
if (next_state == read_scratchpad) begin
state <= reset_1wire ;
timer <= 9'd500 ;
shift <= 16'hBECC ;
bit_cnt <= 7'd16 ;
end
else begin
timer <= 9'd70 ;
toggle_cmp <= 9'd65 ;
end

out_reg_n <= 1'b1 ;
end
else begin
timer <= timer - 1'b1 ;

if (read_1wire & in_reg[1]) next_state <= read_scratchpad ;
if (toggle) out_reg_n <= 1'b0 ;
end
end

read_scratchpad:
begin
if (timer_cout) begin
if (bit_cnt_cout & (next_state == start)) begin
state <= start ;

if (CRC != 0) begin
error_n <= 1'b0 ;
end
else begin
error_n <= 1'b1 ;
buffer <= shift ;
end
end
else begin
timer <= 9'd70 ;

if (next_state != start) begin
bit_cnt <= 7'd71 ;
CRC <= 0 ;
end
else begin
bit_cnt <= bit_cnt - 1'b1 ;
end

out_reg_n <= 1'b1 ;
end

next_state <= start ;
end
else begin
timer <= timer - 1'b1 ;

if (read_1wire) begin
if (bit_cnt > 7'd55) shift[15:0] <= {in_reg[1], shift[15:1]} ;
CRC <= {XOR0, CRC[7:5], CRC[4] ^ XOR0, CRC[3] ^ XOR0, CRC[2:1]} ;
end

if (toggle) out_reg_n <= 1'b0 ;
end
end

default:
state <= start ;
endcase
end
end

endmodule
IEC
Посмотрите
http://www.ourdev.cn/bbs/bbs_content.jsp?b...amp;bbs_id=1029
Может поможет для начала.
Удачи.
ovs_pavel
Если поможет, такая реализация:

CODE

`timescale 1ns / 1ps
module iButton(
// --------------- Сигналы сброса и синхронизации. --------------- //
input LRESETn, // Асинхронный сброс (формируется после того, как уровни напряжения питания моста будут в допуске).
input wFtakt, // Тактовая частота 33 МГц.

// --------------- Внутренние сигналы. --------------- //
input [7:0] D_CPU_iBut, // Данные для выдачи по интерфейсу 1-Wire.
input [1:0] CMD_In, // Код операции.
input [3:0] Add_In, // Адрес регистра данных/команд со стороны адресного простанства процессора (Add_In = 4'b0101).
input WE, // Сигнал записи в регистр данных/команд (запускает автомат состояний).
input OE, // Сигнал подтверждения ("1") операции записи.
input CS, // Сигнал выбора кристалла ("0").
output [7:0] D_iBut_CPU, // Данные, принятые по интерфейсу 1-Wire.
output [1:0] CMD_Out, // Команда, записанная в приемный регистр команд.
output iBut_Busy, // Линия занята (сигнал логической "1" - наличие обмена данными).
output iBut_Pres, // Устройство присутствует на шине (сигнал логической "1" - устройство на шине).

// --------------- Интерфейс 1-Wire. --------------- //
input iButIn,
output iButOut
);


// --------------- Регистр команд. --------------- //
reg [1:0] RgCom;
always @(posedge WE or negedge LRESETn)
begin
if (~LRESETn)
RgCom <= 2'd0;
else if ((Add_In == 4'b1000) && OE && ~CS)
RgCom <= CMD_In;
end
assign CMD_Out = RgCom;

wire w_Res_iBut = (RgCom == 2'b00); // Инициирована диаграмма сброса с последующим определением устройства на шине.
wire w_Wr_iBut = (RgCom == 2'b01); // Инициирована диаграмма записи данных в устройство.


// --------------- Регистр наличия (уровень логической "1") устройства i-Button на шине. --------------- //
reg Rg_iB_PRS; // Регистр наличия устройства i-Button на шине.
reg Ena_Rg_iB_PRS; // Установка в "1" регистра наличия устройства i-Button на шине.
reg Clr_Rg_iB_PRS; // Сброс в "0" регистра наличия устройства i-Button на шине.
always @(posedge wFtakt or posedge Clr_Rg_iB_PRS)
begin
if (Clr_Rg_iB_PRS)
Rg_iB_PRS <= 1'b0;
else if (Ena_Rg_iB_PRS)
Rg_iB_PRS <= 1'b1;
end
assign iBut_Pres = Rg_iB_PRS;


// --------------- Регистр занятости (уровень логической "1") устройства i-Button на шине. --------------- //
reg Rg_iB_BSY; // Регистр занятости устройства i-Button на шине.
reg Clr_Rg_iB_BSY; // Сброс в "0" регистра занятости устройства i-Button на шине.
always @(posedge WE or posedge Clr_Rg_iB_BSY)
begin
if (Clr_Rg_iB_BSY)
Rg_iB_BSY <= 1'b0;
else if ((Add_In == 4'b1000) && OE && ~CS)
Rg_iB_BSY <= 1'b1;
end
assign iBut_Busy = Rg_iB_BSY;


// --------------- Строб запуска автомата состояний. --------------- //
/*
Импульс формируется из строба занятости устройства путем привязки к
основной тактовой частоте и задержки на соответствующее кол-во тактов.
Этот же импульс, записывает принятые данные в сдвиговый регистр выдачи данных.
*/
reg Rg_iB_BSY_Z1; // Задержка строба занятости на один такт.
reg Rg_iB_BSY_Z2; // Задержка строба занятости на два такта.
always @(posedge wFtakt or negedge LRESETn)
begin
if (~LRESETn)
begin
Rg_iB_BSY_Z1 <= 1'b0;
Rg_iB_BSY_Z2 <= 1'b0;
end
else
begin
Rg_iB_BSY_Z1 <= Rg_iB_BSY;
Rg_iB_BSY_Z2 <= Rg_iB_BSY_Z1;
end
end
wire Rg_Start_SM = Rg_iB_BSY_Z1 && ~Rg_iB_BSY_Z2;


// --------------- Приемный регистр команд/данных шины iButton. --------------- //
reg [7:0] Rec_Rg_CD;
always @(posedge WE or negedge LRESETn)
begin
if (~LRESETn)
Rec_Rg_CD <= 8'd0;
else if ((Add_In == 4'b1000) && OE && ~CS)
Rec_Rg_CD <= D_CPU_iBut;
end


// --------------- Сдвиговый регистр выдачи команд/данных шины iButton. --------------- //
/*
Данные выдаются на линию в следующей последовательности: D[0], D[1], D[2]...D[6], D[7].
*/
reg [7:0] Rg_CD; // Сдвиговый регистр выдачи команд/данных шины iButton.
reg Ena_Rg_CD; // Разрешение сдвига данных регистра 'Rg_CD'.
always @(posedge wFtakt or negedge LRESETn)
begin
if (~LRESETn)
Rg_CD <= 8'd0;
else if (Rg_Start_SM)
Rg_CD <= Rec_Rg_CD;
else if (Ena_Rg_CD)
begin
Rg_CD[6:0] <= Rg_CD[7:1];
Rg_CD[0] <= Rg_CD[1];
end
end


// --------------- Мультиплексор управления выдачей на линию данных iButton либо "1", либо "0", либо разряда данных. --------------- //
reg [1:0] Upr_MUX;
reg Rg_iB_Out; // Регистр выдачи данных.
always @ (posedge wFtakt)
begin
case (Upr_MUX)
2'd0 : Rg_iB_Out <= 1'b1;
2'd1 : Rg_iB_Out <= 1'b0;
2'd2 : Rg_iB_Out <= Rg_CD[0];
default : Rg_iB_Out <= 1'b1;
endcase
end
assign iButOut = Rg_iB_Out;


// --------------- Cчетчик длительности импульса сброса в режиме инициализации. --------------- //
/*
Разрядность счетчика 2^14 = 16384.
Тактовая частота счетчика 32 МГц, следовательно период 31,25 нсек.
*/
reg [13:0] CntRes; // Cчетчик длительности импульса сброса в режиме инициализации.
reg EnaCntRes; // Разрешение счета счетчика 'CntRes'.
reg ClrCntRes; // Сигнал сброса счетчика 'CntRes'.
always @(posedge wFtakt or negedge ClrCntRes)
begin
if (~ClrCntRes)
CntRes <= 14'd0;
else if (EnaCntRes)
CntRes <= CntRes + 1'b1;
end

wire w_512us = (CntRes == 14'h3FFF); // Значение счетчика 'CntRes' равно 16384 (512 мкс).
wire w_100us = (CntRes == 14'h0C80); // Значение счетчика 'CntRes' равно 3200 (100 мкс).


// --------------- Привязка цепей 'w_512us' и 'w_100us' к тактовой частоте. --------------- //
reg Rg_w_512us;
reg Rg_w_100us;
always @(posedge wFtakt or negedge LRESETn)
begin
if (~LRESETn)
begin
Rg_w_512us <= 1'b0;
Rg_w_100us <= 1'b0;
end
else
begin
Rg_w_512us <= w_512us;
Rg_w_100us <= w_100us;
end
end


// --------------- Cчетчик формирования диаграммы записи/чтения данных. --------------- //
/*
Разрядность счетчика 2^11 = 2048.
Рабочие интервалы (счетчик 11-ти разрядный):
48 * 31,25 нсек = 1,5 мксек.
480 * 31,25 нсек = 15 мксек.
2000 * 31,25 нсек = 62,5 мксек.
2048 * 31,25 нсек = 64 мксек.
*/
reg [10:0] CntData; // Cчетчик формирования диаграммы записи/чтения данных.
reg EnaCntData; // Разрешение счета счетчика 'CntData'.
reg ClrCntData; // Сигнал сброса счетчика 'CntData'.
always @(posedge wFtakt or negedge ClrCntData)
begin
if (~ClrCntData)
CntData <= 11'd0;
else if (EnaCntData)
CntData <= CntData + 1'b1;
end

wire w_1_5us = (CntData == 11'h2F); // Значение счетчика 'CntData' равно 48 (1,5 мкс).
wire w_15us = (CntData == 11'h1DF); // Значение счетчика 'CntData' равно 480 (15 мкс).
wire w_62_5us = (CntData == 11'h7CF); // Значение счетчика 'CntData' равно 2000 (62,5 мкс).
wire w_64us = (CntData == 11'h7FF); // Значение счетчика 'CntData' равно 2048 (64 мкс).


// --------------- Привязка цепей 'w_1_5us', 'w_15us', 'w_62_5us', 'w_64us' к тактовой частоте. --------------- //
reg Rg_w_1_5us;
reg Rg_w_15us;
reg Rg_w_62_5us;
reg Rg_w_64us;
always @(posedge wFtakt or negedge LRESETn)
begin
if (~LRESETn)
begin
Rg_w_1_5us <= 1'b0;
Rg_w_15us <= 1'b0;
Rg_w_62_5us <= 1'b0;
Rg_w_64us <= 1'b0;
end
else
begin
Rg_w_1_5us <= w_1_5us;
Rg_w_15us <= w_15us;
Rg_w_62_5us <= w_62_5us;
Rg_w_64us <= w_64us;
end
end


// --------------- Cчетчик числа принятых/переданных бит в режиме записи/чтения команд/данных. --------------- //
reg [2:0] CntDatR; // Cчетчик числа принятых/переданных бит в режиме записи/чтения команд/данных.
reg ClrCntDatR; // Сигнал сброса счетчика 'CntDatR'.
always @(posedge wFtakt or negedge ClrCntDatR)
begin
if (~ClrCntDatR)
CntDatR <= 3'd0;
else if (Ena_Rg_CD)
CntDatR <= CntDatR + 1'b1;
end


// --------------- Привязка входной линии данных к тактовой частоте. --------------- //
reg Rg_iB_In;
always @(posedge wFtakt or negedge LRESETn)
begin
if (~LRESETn)
Rg_iB_In <= 1'b0;
else
Rg_iB_In <= iButIn;
end


// --------------- Приемный регистр данных с шины i-Button. --------------- //
/*
Данные принимаются в следующей последовательности: D[0], D[1], D[2], D[3]...D[6], D[7].
*/
reg [7:0] RgRdShift; // Приемный регистр данных с шины i-Button.
reg EnaRdShift; // Сигнал разрешения записи в сдвиговый регистр в цикле чтения данных.
always @(posedge wFtakt or negedge LRESETn)
begin
if (~LRESETn)
RgRdShift <= 8'd0;
else if (EnaRdShift)
begin
RgRdShift[6:0] <= RgRdShift[7:1];
RgRdShift[7] <= Rg_iB_In;
end
end
assign D_iBut_CPU = RgRdShift;


// --------------- Объявление автомата состояний. --------------- //
reg [9:0] State;
parameter [9:0]
Idle = 10'b0000000001,
Wait_Zero = 10'b0000000010,
Wait_One = 10'b0000000100,
Wait_One2 = 10'b0000001000,
CS_Low = 10'b0000010000,
CS_TransD = 10'b0000100000,
CS_RecD = 10'b0001000000,
Wait_Exch = 10'b0010000000,
End_Trans = 10'b0100000000,
Back_Off = 10'b1000000000;


// --------------- Автомат состояний Миля с синхронными выходами. --------------- //
always @ (posedge wFtakt or negedge LRESETn)
begin
if (!LRESETn)
begin
State <= Idle;
Ena_Rg_iB_PRS <= 1'b0; // Установка в "1" регистра наличия устройства i-Button на шине.
Clr_Rg_iB_PRS <= 1'b0; // Сброс в "0" регистра наличия устройства i-Button на шине.
Clr_Rg_iB_BSY <= 1'b0; // Сброс в "0" регистра занятости устройства i-Button на шине.
EnaCntRes <= 1'b0; // Разрешение счета счетчика 'CntRes'.
ClrCntRes <= 1'b0; // Сигнал сброса счетчика 'CntRes'.
Upr_MUX <= 2'd0; // Мультиплексор управления выдачей на линию данных.
Ena_Rg_CD <= 1'b0; // Разрешение сдвига данных регистра 'Rg_CD'.
EnaCntData <= 1'b0; // Разрешение счета счетчика 'CntData'.
ClrCntData <= 1'b0; // Сигнал сброса счетчика 'CntData'.
ClrCntDatR <= 1'b0; // Сигнал сброса счетчика 'CntDatR'.
EnaRdShift <= 1'b0; // Сигнал разрешения записи в сдвиговый регистр в цикле чтения данных.
end
else
begin
case (State)
Idle: // Ожидание импульса запуска автомата состояний.
begin
if (Rg_Start_SM)
begin
Upr_MUX <= 2'd1; // На линию данных выдаем логический "0".

if (w_Res_iBut) // Команда сброса с последующей инициализацией устройства на шине.
begin
State <= Wait_Zero;
EnaCntRes <= 1'b1;
ClrCntRes <= 1'b1;
end
else
begin
State <= CS_Low;
EnaCntData <= 1'b1;
ClrCntData <= 1'b1;
ClrCntDatR <= 1'b1;
end
end
else
begin
State <= Idle;
end
end

Wait_Zero:
begin
if (Rg_w_512us)
begin
State <= Wait_One;
Upr_MUX <= 2'd0; // На линию данных выдаем логическую "1".
end
else
State <= Wait_Zero;
end

Wait_One:
begin
if (Rg_w_100us)
begin
if (Rg_iB_In) // На шине нет устройств.
begin
State <= End_Trans;
Clr_Rg_iB_PRS <= 1'b1;
end
else
begin
State <= Wait_One2; // На шине есть устройства.
Ena_Rg_iB_PRS <= 1'b1;
end
end
else
State <= Wait_One;
end

Wait_One2:
begin
Ena_Rg_iB_PRS <= 1'b0;

if (Rg_w_512us)
State <= End_Trans;
else
State <= Wait_One2;
end

CS_Low:
begin
Ena_Rg_CD <= 1'b0;

if (Rg_w_1_5us)
begin
if (w_Wr_iBut) // Команда записи данных.
begin
State <= CS_TransD;
Upr_MUX <= 2'd2;
end
else
begin
State <= CS_RecD;
Upr_MUX <= 2'd0;
end
end
else
State <= CS_Low;
end

CS_TransD:
begin
if (Rg_w_62_5us)
begin
State <= Wait_Exch;
Upr_MUX <= 2'd0;
end
else
State <= CS_TransD;
end

CS_RecD:
begin
if (Rg_w_15us)
begin
State <= Wait_Exch;
EnaRdShift <= 1'b1;
end
else
State <= CS_RecD;
end

Wait_Exch:
begin
EnaRdShift <= 1'b0;

if (Rg_w_64us)
begin
if (CntDatR == 3'd7)
State <= End_Trans;
else
begin
State <= CS_Low;
Upr_MUX <= 2'd1;
Ena_Rg_CD <= 1'b1;
end
end
else
State <= Wait_Exch;
end

End_Trans:
begin
State <= Back_Off;
Clr_Rg_iB_PRS <= 1'b0;
EnaCntRes <= 1'b0;
ClrCntRes <= 1'b0;
Clr_Rg_iB_BSY <= 1'b1;
Upr_MUX <= 2'd0;
EnaCntData <= 1'b0;
ClrCntData <= 1'b0;
ClrCntDatR <= 1'b0;
end

Back_Off:
begin
State <= Idle;
Clr_Rg_iB_BSY <= 1'b0;
end

default:
begin
State <= Idle;
Ena_Rg_iB_PRS <= 1'b0;
Clr_Rg_iB_PRS <= 1'b0;
Clr_Rg_iB_BSY <= 1'b0;
EnaCntRes <= 1'b0;
ClrCntRes <= 1'b0;
Upr_MUX <= 2'd0;
Ena_Rg_CD <= 1'b0;
EnaCntData <= 1'b0;
ClrCntData <= 1'b0;
ClrCntDatR <= 1'b0;
EnaRdShift <= 1'b0;
end
endcase
end
end


endmodule

Shivers
У Далласа (который 1-wire и придумал, собсно) есть свободнораспостраняемая корка 1-wire master с тестбенчем и документацией, написанная на vhdl и верилоге.
Но суть в том, что этим мастером надо управлять, и если делать автомат того же автодетекта/поиска устройств на шине, то алгоритм реализованный на автомате получится просто чудовищный. Т.е., вам понадобится еще модель mcu для запихывания внутрь, и память для программы, чтобы все 1-wire эвенты обрабатывать.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.