三,1-Wire总线上按Byte读写的Verilog实现及DS18B20的Byte操作
上面用简单状态机实现了1-Wire总线上单bit数据的读写操作。在此基础上,可以通过状态机嵌套的方法实现按Byte的读写操作。实现Byte读写控制的状态机,控制8bit数据的读写操作,而每一bit的读写操作则通过嵌套单bit数据读写的状态机来实现。
1. 按Byte读操作
从DS18B20的datasheet上我们可以看到,1-Wire总线在发送或者接收数据的时候,LSB的数据在前,MSB的数据在后。所以对于Byte数据的读操作,只要依次在总线上读取8bit数据,并按照bit0~bit7的顺序将其存储到一个8bits的寄存器里即可。
通常情况下,设计状态机的时候,都会设计一个默认的初始状态(也可以称之为IDLE状态)。在这里每个bit数据的读操作可以设定为一个状态。这样,整个状态机就需要九个状态。在我们的设计中,九个状态分别是RBD_IDLE, RBD_BIT0 ~RBD_BIT7,其中 RBD_IDLE是IDLE状态,RBD_BIT0~RBD_BIT7分别是读取bit0~bit7数据的状态。
状态机在系统复位或者操作完成以后,进入RBD_IDLE状态。当读Byte数据的使能信号(RBDBEGIN)有效时,状态机进入RBD_BIT0,并调用1-Wire总线单bit读操作的状态机实现单bit的读操作,读取bit0的数据。为了在状态机进入RBD_BIT0的时候,正确调用单bit读操作的状态机,就要在RBD_BIT0状态下,使能RDBEGIN信号(单bit读操作的使能信号,详见上文)。在读完bit0的数据后(单bit读操作状态机回到IDLE状态),状态机需要进入RBD_BIT1状态,读取bit1的数据。也就是说,状态机从RBD_BIT0进入到RBD_BIT1的条件是bit0读操作结束(即其状态机回到IDLE状态),这个条件可以用单bit读操作状态机的PHASE_RD_OVER生成。上述的操作就实现了两个状态机的嵌套,顶层状态机(Byte读操作状态机)的信号触发底层状态机(单bit读操作状态机)进入工作状态,当底层状态机完成工作进入IDLE状态时,触发顶层状态机进入下一个状态。
从RBD_BIT0到RBD_BIT7的操作是相同的,依次读取8bit的数据。不过读完bit7的数据后,状态机要转回到RBD_IDLE状态。另外就是在单bit读的过程中,要根据Byte读状态机的状态,将读到的单bit数据写入对应的寄存器中。
下面是verilog的实现:
reg [7:0] RD_BYTE_DATA; //Byte读数据结果
wire RBDBEGIN ; // Byte读使能信号
reg PHASE_RD_OVER_Q;
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
PHASE_RD_OVER_Q <= 1'b0;
else
PHASE_RD_OVER_Q <= PHASE_RD_OVER;
end
wire RD_BIT_OVER = PHASE_RD_OVER & PHASE_RD_OVER_Q; //单bit读操作结束信号
parameter RBD_IDLE = 9'b0_0000_0001,
RBD_BIT0 = 9'b0_0000_0010,
RBD_BIT1 = 9'b0_0000_0100,
RBD_BIT2 = 9'b0_0000_1000,
RBD_BIT3 = 9'b0_0001_0000,
RBD_BIT4 = 9'b0_0010_0000,
RBD_BIT5 = 9'b0_0100_0000,
RBD_BIT6 = 9'b0_1000_0000,
RBD_BIT7 = 9'b1_0000_0000;
reg [8:0] RBDSM, RBDSMNXT;
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
RBDSM <= RBD_IDLE;
else
RBDSM <= RBDSMNXT;
end
always @(RBDSM or RBDBEGIN or RD_BIT_OVER) begin
case(RBDSM)
RBD_IDLE:
if(RBDBEGIN)
RBDSMNXT = RBD_BIT0;
else
RBDSMNXT = RBD_IDLE;
RBD_BIT0:
if(RD_BIT_OVER)
RBDSMNXT = RBD_BIT1;
else
RBDSMNXT = RBD_BIT0;
RBD_BIT1:
if(RD_BIT_OVER)
RBDSMNXT = RBD_BIT2;
else
RBDSMNXT = RBD_BIT1;
RBD_BIT2:
if(RD_BIT_OVER)
RBDSMNXT = RBD_BIT3;
else
RBDSMNXT = RBD_BIT2;
RBD_BIT3:
if(RD_BIT_OVER)
RBDSMNXT = RBD_BIT4;
else
RBDSMNXT = RBD_BIT3;
RBD_BIT4:
if(RD_BIT_OVER)
RBDSMNXT = RBD_BIT5;
else
RBDSMNXT = RBD_BIT4;
RBD_BIT5:
if(RD_BIT_OVER)
RBDSMNXT = RBD_BIT6;
else
RBDSMNXT = RBD_BIT5;
RBD_BIT6:
if(RD_BIT_OVER)
RBDSMNXT = RBD_BIT7;
else
RBDSMNXT = RBD_BIT6;
RBD_BIT7:
if(RD_BIT_OVER)
RBDSMNXT = RBD_IDLE;
else
RBDSMNXT = RBD_BIT7;
default:
RBDSMNXT = RBD_IDLE;
endcase
end
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
RD_BYTE_DATA <= 8'b0;
else if(PHASE_RD_MSAP & (RD_CNT == 6'd13) )
begin
case(RBDSM)
9'b0_0000_0010:
RD_BYTE_DATA[0] <= DQ;
9'b0_0000_0100:
RD_BYTE_DATA[1] <= DQ;
9'b0_0000_1000:
RD_BYTE_DATA[2] <= DQ;
9'b0_0001_0000:
RD_BYTE_DATA[3] <= DQ;
9'b0_0010_0000:
RD_BYTE_DATA[4] <= DQ;
9'b0_0100_0000:
RD_BYTE_DATA[5] <= DQ;
9'b0_1000_0000:
RD_BYTE_DATA[6] <= DQ;
9'b1_0000_0000:
RD_BYTE_DATA[7] <= DQ;
default:
RD_BYTE_DATA <= RD_BYTE_DATA;
endcase
end
else
RD_BYTE_DATA <= RD_BYTE_DATA;
end
2. 按Byte写操作
按Byte写操作的状态机与按Byte读操作的状态机的控制原理基本上是相同的。只不过在这里要嵌套的是单bit写操作的状态机。另外就是在单bit写操作的时候,要把对应bit的数据放到DQ总线上。
限于篇幅,状态机的实现不再给出,可自行参照Byte读操作的状态机做修改。
3. DS18B20的Byte操作
DS18B20的控制主要包括初始化,ROM命令和功能命令等。初始化的控制方式前面已经讨论过了。这里主要讨论一下ROM命令和功能命令。实际上不管是ROM命令还是功能命令,都可以归结为一个字节的命令(Byte写操作)加上0或者多个的Byte读/写操作。所以我们完全可以用上面所述的Byte读写状态机来实现ROM和功能命令。
对于只有一个单Byte写操作的命令,我们直接引用单Byte写操作的状态机就可以实现了。
下图即为执行SKIP ROM(8’hcc)命令时,从DQ总线上抓到的波形。
下图为执行COVERT(8’h44)命令时,从DQ总线上抓到的波形。
而对于如读ROM数据的操作,则是一个Byte的写命令操作加上连续多个Byte的读数据的操作。在实现的时候,我们完全可以依照Byte读写操作状态机嵌套单bit读写操作状态机的方法,设计一个多BYTE读写操作的状态机,嵌套单Byte读写状态机。
下面是从DS18B20读出9Byte数据的状态机的verilog实现:
reg [7:0] TMP_LSB;
reg [7:0] TMP_MSB;
reg [7:0] USERBYTE1;
reg [7:0] USERBYTE2;
reg [7:0] CFGREG;
reg [7:0] RESERVED5;
reg [7:0] RESERVED6;
reg [7:0] RESERVED7;
reg [7:0] REGCRC;
wire RDSPAD_EN; //读DS18B20 rom数据使能信号
wire BYTE_READ_OVER = RD_BIT_OVER & PHASE_RBD_BIT7; //Byte读操作结束信号。
wire BYTE_WRITE_OVER = WD_BIT_OVER & PHASE_WBD_BIT7; //Byte 写操作结束信号。
parameter RDSPAD_IDLE = 11'b000_0000_0001,
RDSPAD_CMD = 11'b000_0000_0010, //读ROM命令
RDSPAD_BYTE1 = 11'b000_0000_0100, // 读ROM Byte 1
RDSPAD_BYTE2 = 11'b000_0000_1000,
RDSPAD_BYTE3 = 11'b000_0001_0000,
RDSPAD_BYTE4 = 11'b000_0010_0000,
RDSPAD_BYTE5 = 11'b000_0100_0000,
RDSPAD_BYTE6 = 11'b000_1000_0000,
RDSPAD_BYTE7 = 11'b001_0000_0000,
RDSPAD_BYTE8 = 11'b010_0000_0000,
RDSPAD_BYTE9 = 11'b100_0000_0000;
reg [10:0] RDSPADSM, RDSPADSMNXT;
wire PHASE_RDSPAD_IDLE = RDSPADSM[0];
wire PHASE_RDSPAD_CMD = RDSPADSM[1];
wire PHASE_RDSPAD_BYTE1 = RDSPADSM[2];
wire PHASE_RDSPAD_BYTE2 = RDSPADSM[3];
wire PHASE_RDSPAD_BYTE3 = RDSPADSM[4];
wire PHASE_RDSPAD_BYTE4 = RDSPADSM[5];
wire PHASE_RDSPAD_BYTE5 = RDSPADSM[6];
wire PHASE_RDSPAD_BYTE6 = RDSPADSM[7];
wire PHASE_RDSPAD_BYTE7 = RDSPADSM[8];
wire PHASE_RDSPAD_BYTE8 = RDSPADSM[9];
wire PHASE_RDSPAD_BYTE9 = RDSPADSM[10];
wire PHASENXT_RDSPAD_IDLE = RDSPADSMNXT[0];
wire PHASENXT_RDSPAD_CMD = RDSPADSMNXT[1];
wire PHASENXT_RDSPAD_BYTE1 = RDSPADSMNXT[2];
wire PHASENXT_RDSPAD_BYTE2 = RDSPADSMNXT[3];
wire PHASENXT_RDSPAD_BYTE3 = RDSPADSMNXT[4];
wire PHASENXT_RDSPAD_BYTE4 = RDSPADSMNXT[5];
wire PHASENXT_RDSPAD_BYTE5 = RDSPADSMNXT[6];
wire PHASENXT_RDSPAD_BYTE6 = RDSPADSMNXT[7];
wire PHASENXT_RDSPAD_BYTE7 = RDSPADSMNXT[8];
wire PHASENXT_RDSPAD_BYTE8 = RDSPADSMNXT[9];
wire PHASENXT_RDSPAD_BYTE9 = RDSPADSMNXT[10];
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
RDSPADSM <= RDSPAD_IDLE;
else
RDSPADSM <= RDSPADSMNXT;
end
always @( RDSPADSM or RDSPAD_EN or BYTE_READ_OVER or BYTE_WRITE_OVER) begin
case(RDSPADSM)
RDSPAD_IDLE:
if(RDSPAD_EN)
RDSPADSMNXT = RDSPAD_CMD;
else
RDSPADSMNXT = RDSPAD_IDLE;
RDSPAD_CMD:
if(BYTE_WRITE_OVER)
RDSPADSMNXT = RDSPAD_BYTE1;
else
RDSPADSMNXT = RDSPAD_CMD;
RDSPAD_BYTE1:
if(BYTE_READ_OVER)
RDSPADSMNXT = RDSPAD_BYTE2;
else
RDSPADSMNXT = RDSPAD_BYTE1;
RDSPAD_BYTE2:
if(BYTE_READ_OVER)
RDSPADSMNXT = RDSPAD_BYTE3;
else
RDSPADSMNXT = RDSPAD_BYTE2;
RDSPAD_BYTE3:
if(BYTE_READ_OVER)
RDSPADSMNXT = RDSPAD_BYTE4;
else
RDSPADSMNXT = RDSPAD_BYTE3;
RDSPAD_BYTE4:
if(BYTE_READ_OVER)
RDSPADSMNXT = RDSPAD_BYTE5;
else
RDSPADSMNXT = RDSPAD_BYTE4;
RDSPAD_BYTE5:
if(BYTE_READ_OVER)
RDSPADSMNXT = RDSPAD_BYTE6;
else
RDSPADSMNXT = RDSPAD_BYTE5;
RDSPAD_BYTE6:
if(BYTE_READ_OVER)
RDSPADSMNXT = RDSPAD_BYTE7;
else
RDSPADSMNXT = RDSPAD_BYTE6;
RDSPAD_BYTE7:
if(BYTE_READ_OVER)
RDSPADSMNXT = RDSPAD_BYTE8;
else
RDSPADSMNXT = RDSPAD_BYTE7;
RDSPAD_BYTE8:
if(BYTE_READ_OVER)
RDSPADSMNXT = RDSPAD_BYTE9;
else
RDSPADSMNXT = RDSPAD_BYTE8;
RDSPAD_BYTE9:
if(BYTE_READ_OVER)
RDSPADSMNXT = RDSPAD_IDLE;
else
RDSPADSMNXT = RDSPAD_BYTE9;
default:
RDSPADSMNXT = RDSPAD_IDLE;
endcase
end
//采集并保存TMP_LSB
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
TMP_LSB <= 8'b0;
else if(PHASENXT_RDSPAD_BYTE2 & BYTE_READ_OVER)
TMP_LSB <= RD_BYTE_DATA;
else
TMP_LSB <= TMP_LSB;
end
//采集并保存 TMP_MSB
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
TMP_MSB <= 8'b0;
else if(PHASENXT_RDSPAD_BYTE3 & BYTE_READ_OVER)
TMP_MSB <= RD_BYTE_DATA;
else
TMP_MSB <= TMP_MSB;
end
//采集并保存 USERBYTE1
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
USERBYTE1 <= 8'b0;
else if(PHASENXT_RDSPAD_BYTE4 & BYTE_READ_OVER)
USERBYTE1 <= RD_BYTE_DATA;
else
USERBYTE1 <= USERBYTE1;
End
//采集并保存USERBYTE2
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
USERBYTE2 <= 8'b0;
else if(PHASENXT_RDSPAD_BYTE5 & BYTE_READ_OVER)
USERBYTE2 <= RD_BYTE_DATA;
else
USERBYTE2 <= USERBYTE2;
end
//采集并保存 CFGREG
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
CFGREG <= 8'b0;
else if(PHASENXT_RDSPAD_BYTE6 & BYTE_READ_OVER)
CFGREG <= RD_BYTE_DATA;
else
CFGREG <= CFGREG;
end
//采集并保存 RESERVED5
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
RESERVED5 <= 8'b0;
else if(PHASENXT_RDSPAD_BYTE7 & BYTE_READ_OVER)
RESERVED5 <= RD_BYTE_DATA;
else
RESERVED5 <= RESERVED5;
end
//采集并保存 RESERVED6
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
RESERVED6 <= 8'b0;
else if(PHASENXT_RDSPAD_BYTE8 & BYTE_READ_OVER)
RESERVED6 <= RD_BYTE_DATA;
else
RESERVED6 <= RESERVED6;
end
//采集并保存 RESERVED7
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
RESERVED7 <= 8'b0;
else if(PHASENXT_RDSPAD_BYTE9 & BYTE_READ_OVER)
RESERVED7 <= RD_BYTE_DATA;
else
RESERVED7 <= RESERVED7;
end
//采集并保存 REGCRC
always @(posedge CLK1MHZ or negedge RESET)
begin
if(~RESET)
REGCRC <= 8'b0;
else if(PHASENXT_RDSPAD_IDLE & BYTE_READ_OVER)
REGCRC <= RD_BYTE_DATA;
else
REGCRC <= REGCRC;
end
下图是从示波器上抓出的单Byte读操作的DQ上的波形。