Jump to content

    

Реализация 1-wire

Добрый день!

Есть такая задача, подключить к ПЛИС Xilinx XC3S50AN несколько датчиков температуры DS18b20 и считывать с них значение температуры.

С плис начал работать совсем недавно. Подскажите в какую сторону копать, может у кого есть опыт таких разработок. Буду благодарен за любую помощь.

Share this post


Link to post
Share on other sites
...Подскажите в какую сторону копать...

Например в эту.

(я так понимаю в гугуле завелись жёсткие админы которые банят :biggrin: всех начинающих...)

Share this post


Link to post
Share on other sites
Добрый день!

Есть такая задача, подключить к ПЛИС Xilinx XC3S50AN несколько датчиков температуры DS18b20 и считывать с них значение температуры.

С плис начал работать совсем недавно. Подскажите в какую сторону копать, может у кого есть опыт таких разработок. Буду благодарен за любую помощь.

 

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

 

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

Share this post


Link to post
Share on other sites

Делал на SV, но переписать на обычный Verilog не очень сложно.

`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

Share this post


Link to post
Share on other sites

Если поможет, такая реализация:

 

`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

Share this post


Link to post
Share on other sites

У Далласа (который 1-wire и придумал, собсно) есть свободнораспостраняемая корка 1-wire master с тестбенчем и документацией, написанная на vhdl и верилоге.

Но суть в том, что этим мастером надо управлять, и если делать автомат того же автодетекта/поиска устройств на шине, то алгоритм реализованный на автомате получится просто чудовищный. Т.е., вам понадобится еще модель mcu для запихывания внутрь, и память для программы, чтобы все 1-wire эвенты обрабатывать.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this