One-Wire是一种只需要一条线即可传输资料的传输协定,而通常这种传输协定会用於与小型装置沟通,例如数位温度、湿度感测器。
那我们在这边会以 AM2302 的规格为主去撰写我们的程序
注意:Tbe 为 master 拉低
图片出处:AM2302 Datasheet
这边我们由图一及图二可以看到在接收 AM2302 的资料时,一共会有一连串的 Start Signal 以及 40 bit 的 data,分别是湿度的前 8 bit、後 8 bit,温度的前 8 bit、後 8 bit,还有最後的 8 bit 较验位(较验位 = 前面四笔 8 bit 资料做相加)
而图三及图四则告诉了我们各种信号所会花费的时间长
而 datasheet 内容也有提到这里的 SDA 线也是有 pull-up 的~
先来第一步吧!
先定义状态机
//===== state machine =====//
localparam IDLE = 3'd0;
localparam Be = 3'd1;
localparam GO = 3'd2;
localparam REL = 3'd3;
localparam REH = 3'd4;
localparam DATA = 3'd5;
localparam CHECKOUT = 3'd6;
定义每个间隔时间
//===== during time =====//
localparam durBe = 16'd50000;//1ms
localparam durGo = 16'd1500; //30us
localparam durRel = 16'd2555; //80us
localparam durReh = 16'd2560; //80us
localparam durDataHIGH = 16'd2580; //70us
localparam durDataMax = 16'd5000; //100us
这里的间隔周期以 datasheet 的 typical 为准,而这里的时脉是以 50M 为主,举个例子:如果要 1ms 那麽 counter 就要数 1ms/20ns = 50000,其他以此类推。
再来比较有趣的是这里可以不用精准定义 "0" 讯号以及 "1" 讯号的 HIGH 时间长,因为只要在资料线一变 LOW 时去检查在 HIGH 时数到的数字有没有超过一定的值就可以视为讯号 "1" 了(这里是抓 70us,因为 "0" HIGH 的 typical 值为 30us,"1" HIGH 的 typical 值为 75us)
再来定义输入输出
输入:
输出:
inout
module AM2302_controller(
clkSys,
en,
rst_n,
SDA,
dataReady,
data_o
);
/*---------Ports Declarations---------*/
input clkSys;
input en;
input rst_n;
inout SDA;
output dataReady;
output [39:0] data_o;
reg dataReady;
reg [39:0] data_o;
宣告变数:
/*---------variables---------*/
reg [2:0] fstate;
reg [15:0] counter;
reg [39:0] databuf;
reg counterEN;
reg flag_get;
reg sdaReg;
SDA 输出
/*---------assign wire---------*/
assign SDA = (sdaReg)?(1'bz):(1'b0);
counter 致能控制 counter 的计数
/*---------counter---------*/
always@(posedge clkSys or negedge rst_n)begin
if(!rst_n) counter <= 16'd0;
else if(counterEN)counter <= counter + 16'd1;
else counter <= 16'd0;
end
状态机状态逻辑
/*---------fstate_state---------*/
always@(posedge clkSys or negedge rst_n)begin
if(!rst_n)fstate <= IDLE;
else begin
case(fstate)
IDLE:begin
if(en)fstate <= Be;
else fstate <= IDLE;
end
Be:begin
if(counter == durBe)fstate <= GO;
else fstate <= Be;
end
GO:begin
if(counter == durGo)fstate <= REL;
else fstate <= GO;
end
REL:begin
if(counter == durRel)fstate <= REH;
else fstate <= REL;
end
REH:begin
if(counter == durReh)fstate <= DATA;
else fstate <= REH;
end
DATA:begin
if(counter == durDataMax)fstate <= CHECKOUT;
else fstate <= DATA;
end
CHECKOUT:fstate <= IDLE;
default: fstate <= IDLE;
endcase
end
end
状态机输出逻辑
/*---------fstate_output---------*/
always@(posedge clkSys or negedge rst_n)begin
if(!rst_n)begin
counterEN <= 1'b0;
sdaReg <= 1'b1;
end
else begin
case(fstate)
IDLE:begin
counterEN <= 1'b0;
sdaReg <= 1'b1;
end
Be:begin
if(counter < durBe)counterEN <= 1'b1;
else counterEN <= 1'b0;
sdaReg <= 1'b0;
end
GO:begin
if(counter < durGo)counterEN <= 1'b1;
else counterEN <= 1'b0;
sdaReg <= 1'b1;
end
REL:begin
if(!SDA && counter < durRel)counterEN <= 1'b1;
else counterEN <= 1'b0;
sdaReg <= 1'b1;
end
REH:begin
if(SDA && counter < durReh)counterEN <= 1'b1;
else counterEN <= 1'b0;
sdaReg <= 1'b1;
end
DATA:begin
if(SDA && counter < durDataMax)counterEN <= 1'b1;
else counterEN <= 1'b0;
sdaReg <= 1'b1;
end
CHECKOUT:begin
counterEN <= 1'b0;
sdaReg <= 1'b1;
end
default:begin
counterEN <= 1'b0;
sdaReg <= 1'b1;
end
endcase
end
end
flag_get
/*---------flag_get---------*/
always@(posedge clkSys or negedge rst_n)begin
if(!rst_n)flag_get <= 1'b0;
else flag_get <= SDA;
end
把 SDA 延後一个 clk 给 flag_get,往後只要读到 SDA = 0;flag_get = 1,就知道 SDA 此时下降了(负缘)
蒐集资料
/*---------put data in databuf---------*/
always@(posedge clkSys or negedge rst_n)begin
if(!rst_n)databuf <= 39'd0;
else begin
if(fstate > REH)begin
if(!SDA && flag_get && (counter > durDataHIGH)) databuf <= {databuf[38:0],1'b1};
else if(!SDA && flag_get && (counter < durDataHIGH))databuf <= {databuf[38:0],1'b0};
else databuf <= databuf;
end
else databuf <= 39'd0;
end
end
在 SDA 刚下降时去检查刚刚在 SDA 为 HIGH 时维持了多少时间,如果大於 70us 则将 "1" 左移移入 databuf,反之则移入 "0"。
检查较验码
/*---------check data and sent dataReady---------*/
always@(posedge clkSys or negedge rst_n)begin
if(!rst_n)begin
data_o <= 39'd0;
dataReady <= 1'b0;
end
else begin
if(fstate==CHECKOUT)begin
if(databuf[7:0]==databuf[39:32]+databuf[31:24]+databuf[23:16]+databuf[15:8])begin
data_o <= databuf;
dataReady <= 1'b1;
end
else begin
data_o <= data_o;
dataReady <= 1'b0;
end
end
else begin
data_o <= data_o;
dataReady <= 1'b0;
end
end
end
endmodule
在 CHECKOUT 状态检查较验码,如果正确则输出至 data_o,并且将 dataReady 升为 "1"。
如此以来我们就完成了这个 AM2302 controller 模组瞜,是不是比上次的 I2C 更简单了一点呢~~~~
<<: 【Day 25】SwiftUI -Drawing Paths and Shapes
在这个演算法当道的时代 每一家网路公司在想办法尽量的搜集使用者的资讯 不论是苹果限制脸书获取使用者的...
Aloha!我是少女人妻 Uerica!这篇是最後一篇了,虽然没有写到很深,但对搜寻引擎真的有多了...
Hello, 各位 iT邦帮忙 的粉丝们大家好~~~ 本篇是 Re: 从零开始用 Xamarin 技...
NOT与!的意思相同,它代表的意思是,运算结果为0时回传1,其他都回传1 WHERE NOT (AG...
主导的第一个计画就是帮厂商开发一个平台并且包含3D模型模拟的功能,然後需要有一个後台给厂商能够上传图...