目录
一、任务
二、分析
1.需要了解的
2.需要用到的模块
3.流程分析
三、Visio图
四、代码
五、实验现象
一、任务
使用IIC协议通信的AHT20,将温湿度数据读取出来,并在串口助手上显示。
二、分析
1.需要了解的
需要了解IIC协议简介
也可以看看EEPROM的读写
我写的这个代码与上面的EEPROM的读写类似。
2.需要用到的模块
需要用到,AHT20模块,IIC接口模块,串口发送模块,读写控制模块和数据处理模块。这里我没有使用CRC,但是,我在代码里面将它存储了,感兴趣的可以自己了解一下。
3.流程分析
根据上面的AHT20的读取流程,最好的是使用状态机来写,这里我分成5个状态:
WAIT
START
IDLE
READ
VLDWAIT:上电之后延时5ms,跳入START状态,
START:发送4个字完成以后,跳入IDLE状态,
IDLE:延时1s(大于80ms),跳入READ状态
READ:发送完8个字节,跳入VLD状态,
VLD:数据简单处理,跳入START状态循环。
三、Visio图
以下是整体的框架。(可以自己去看看RTL视图)
四、代码
top.v
module top( input sys_clk ,input sys_rst_n,output uart_txd ,output iic_scl ,inout iic_sda
);
//---------<Parameter definition>-----------------------------------//---------<Internal signal definition>-----------------------------
wire sda_in ;
wire sda_out ;
wire sda_oe ;
wire [31:0] humi_data ;
wire [31:0] temp_data ;
wire data_vld ;
wire [7:0] tx_data ;
wire tx_data_vld ;
wire tx_done ;
wire ready ;//三态门描述
assign iic_sda = sda_oe ? sda_out : 1'bz;
assign sda_in = iic_sda;aht20_top u_aht20_top(
.clk (sys_clk ),
.rst_n (sys_rst_n),
.humi_data (humi_data),
.temp_data (temp_data),
.data_vld (data_vld ),
.iic_scl (iic_scl ),
.sda_in (sda_in ),
.sda_out (sda_out ),
.sda_oe (sda_oe )
);data_process u_data_process( .clk (sys_clk ),.rst_n (sys_rst_n ),.humi_data (humi_data ),.temp_data (temp_data ),.data_vld (data_vld ),.tx_data (tx_data ),.tx_data_vld (tx_data_vld ),.ready (ready ),.tx_done (tx_done )
);uart_tx u_uart_tx( .clk (sys_clk ),.rst_n (sys_rst_n ),.din (tx_data ),.start_en (tx_data_vld),//开始发送的使能信号.tx_done (tx_done ),//一帧数据发送完成.ready (ready ),//控制指示信号 空闲时ready为高,忙时ready为低.uart_txd (uart_txd )
);endmodule
uart_tx.v
module uart_tx#(parameter BAUD = 115200,SYS_CLOCK = 50_000_000
)( input clk ,input rst_n ,input [7:0] din ,input start_en ,//开始发送的使能信号output reg tx_done ,//一帧数据发送完成output ready ,//控制指示信号 空闲时ready为高,忙时ready为低output reg uart_txd
);
//---------<Parameter definition>-----------------------------------
localparam BAUD_MAX = SYS_CLOCK/BAUD;//对应波特率情况下,传输1bit需要多少个系统时钟周期//---------<Internal signal definition>-----------------------------
reg [12:0] cnt_baud ;//波特率周期计数器
wire add_cnt_baud;
wire end_cnt_baud;
reg [3:0] cnt_bit ;//bit计数器
wire add_cnt_bit;
wire end_cnt_bit;
reg [7:0] din_r ;reg flag;//数据帧传输的指示信号//din_r
always @(posedge clk or negedge rst_n)begin if(!rst_n)begindin_r <= 'd0;end else if(start_en)begin din_r <= din;end
end//flag
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginflag <= 'd0;end else if(start_en)begin flag <= 1'b1;end else if(end_cnt_bit)begin flag <= 1'b0;end
end//cnt_baud
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_baud <= 'd0;end else if(add_cnt_baud)begin if(end_cnt_baud)begin cnt_baud <= 'd0;endelse begin cnt_baud <= cnt_baud + 1'b1;end end
end assign add_cnt_baud = flag;
assign end_cnt_baud = add_cnt_baud && cnt_baud == ((cnt_bit == 9) ? ((BAUD_MAX>>1) + (BAUD_MAX>>2)) : (BAUD_MAX - 1));//cnt_bit
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse begin cnt_bit <= cnt_bit + 1'b1;end end
end assign add_cnt_bit = end_cnt_baud;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 10 - 1;//uart_txd 并转串
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginuart_txd <= 1'b1;end else if(flag) begin case (cnt_bit)0 : uart_txd <= 1'b0;//起始位1 : uart_txd <= din_r[0];//LSB2 : uart_txd <= din_r[1];3 : uart_txd <= din_r[2];4 : uart_txd <= din_r[3];5 : uart_txd <= din_r[4];6 : uart_txd <= din_r[5];7 : uart_txd <= din_r[6];8 : uart_txd <= din_r[7];9 : uart_txd <= 1'b1;//停止位default: ;endcaseend else begin uart_txd <= 1'b1;end
end//tx_done
always @(posedge clk or negedge rst_n)begin if(!rst_n)begintx_done <= 'd0;end else begin tx_done <= end_cnt_bit;end
end//ready
assign ready = ~flag;endmodule
data_process
input clk ,input rst_n ,//aht20_ctrlinput [31:0] humi_data ,input [31:0] temp_data ,input data_vld ,//seg_driver// output reg [23:0] dig_data ,// output reg dig_data_vld,//uart_txoutput reg [7:0] tx_data ,output tx_data_vld ,input ready ,input tx_done
);
//---------<Parameter definition>-----------------------------------//---------<Internal signal definition>-----------------------------
wire [11:0] humi_bcd ;
wire humi_bcd_vld;
wire [11:0] temp_bcd ;
wire temp_bcd_vld;
reg [7:0] cnt ;//串口发送字节数量计数器
wire add_cnt;
wire end_cnt;
reg tx_flag;//串口发送数据的标志信号//*******************************************************************
//--温湿度二进制-->BCD码
//*******************************************************************
//湿度转换
bin2bcd u_bin2bcd_1(/*input */.clk (clk ),//时钟/*input */.rst_n (rst_n ),//复位/*input */.en (data_vld ),/*input [DIN_W-1:0] */.binary_din (humi_data ),//输入二进制数据/*output reg [DOUT_W-1:0] */.bcd_dout (humi_bcd ),//输出BCD码数据/*output reg */.bcd_dout_vld(humi_bcd_vld)
);//温度转换
bin2bcd u_bin2bcd_2(/*input */.clk (clk ),//时钟/*input */.rst_n (rst_n ),//复位/*input */.en (data_vld ),/*input [DIN_W-1:0] */.binary_din (temp_data ),//输入二进制数据/*output reg [DOUT_W-1:0] */.bcd_dout (temp_bcd ),//输出BCD码数据/*output reg */.bcd_dout_vld(temp_bcd_vld)
);//*******************************************************************
//--串口显示
//*******************************************************************
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt <= 'd0;end else if(add_cnt)begin if(end_cnt)begin cnt <= 'd0;endelse begin cnt <= cnt + 1'b1;end end
end assign add_cnt = tx_flag && ready;
assign end_cnt = add_cnt && cnt == 12 - 1;//tx_flag
always @(posedge clk or negedge rst_n)begin if(!rst_n)begintx_flag <= 'd0;end else if(humi_bcd_vld)begin tx_flag <= 1'b1;end else if(end_cnt)begin tx_flag <= 1'b0;end
end//tx_data
always @(posedge clk or negedge rst_n)begin if(!rst_n)begintx_data <= 'd0;end else begin case (cnt)0 : tx_data <= "0" + humi_bcd[11:8];//湿度数据的十位1 : tx_data <= "0" + humi_bcd[7:4];//湿度数据的个位2 : tx_data <= ".";3 : tx_data <= "0" + humi_bcd[3:0];//湿度数据小数点后1位4 : tx_data <= "%";5 : tx_data <= " ";6 : tx_data <= "0" + temp_bcd[11:8];7 : tx_data <= "0" + temp_bcd[7:4];8 : tx_data <= ".";9 : tx_data <= "0" + temp_bcd[3:0];10 : tx_data <= 8'hA1;//℃11 : tx_data <= 8'hE6;default: tx_data <= 0;endcaseend
endassign tx_data_vld = tx_flag && ready;endmodule
二进制转BCD
module bin2bcd#(parameter DIN_W = 32,DOUT_W = 12)(input clk ,//时钟input rst_n ,//复位input en ,input [DIN_W-1:0] binary_din ,//输入二进制数据//输出信号定义output reg [DOUT_W-1:0] bcd_dout ,//输出BCD码数据output reg bcd_dout_vld
);//状态机参数定义localparam IDLE = 4'b0001,READY = 4'b0010,SHIFT = 4'b0100,DONE = 4'b1000;//信号定义reg [3:0] state_c ;reg [3:0] state_n ;reg [DIN_W-1:0] din_r ; //数据锁存reg [5:0] shift_cnt ; //移位次数计数器wire add_shift_cnt;wire end_shift_cnt;reg [3:0] mem_r0 ;reg [3:0] mem_r1 ;reg [3:0] mem_r2 ;reg [3:0] mem_r3 ;reg [3:0] mem_r4 ;reg [3:0] mem_r5 ;reg [3:0] mem_r6 ;reg [3:0] mem_r7 ;reg [3:0] mem_r8 ;reg [3:0] mem_r9 ;wire [3:0] mem_w0 ;wire [3:0] mem_w1 ;wire [3:0] mem_w2 ;wire [3:0] mem_w3 ;wire [3:0] mem_w4 ;wire [3:0] mem_w5 ;wire [3:0] mem_w6 ;wire [3:0] mem_w7 ;wire [3:0] mem_w8 ;wire [3:0] mem_w9 ;wire [39:0] bcd_res ;wire idle2ready ; wire shift2done ;always @(posedge clk or negedge rst_n) begin if (!rst_n) beginstate_c <= IDLE ;endelse beginstate_c <= state_n;endendalways @(*) begin case(state_c) IDLE :beginif(idle2ready) state_n = READY;else state_n = state_c;endREADY:begin state_n = SHIFT;end SHIFT :beginif(shift2done) state_n = DONE ;else state_n = state_c ;endDONE :beginstate_n = IDLE;enddefault : state_n = IDLE;endcaseendassign idle2ready = state_c == IDLE && (en);assign shift2done = state_c == SHIFT && (end_shift_cnt);always @(posedge clk or negedge rst_n)beginif(!rst_n)beginshift_cnt <= 0;endelse if(add_shift_cnt)beginif(end_shift_cnt)shift_cnt <= 0;elseshift_cnt <= shift_cnt + 1;endendassign add_shift_cnt = state_c == SHIFT; assign end_shift_cnt = add_shift_cnt && shift_cnt == DIN_W-1; //din_ralways @(posedge clk or negedge rst_n)beginif(!rst_n)begindin_r <= 0;endelse if(en)begin din_r <= binary_din; end else if(state_c == SHIFT)begin //移位状态下,每个时钟周期向左移1位din_r <= din_r << 1'b1;endendalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginmem_r0 <= 0;mem_r1 <= 0;mem_r2 <= 0;mem_r3 <= 0;mem_r4 <= 0;mem_r5 <= 0;mem_r6 <= 0;mem_r7 <= 0;mem_r8 <= 0;mem_r9 <= 0;endelse if(idle2ready)begin mem_r0 <= 0;mem_r1 <= 0;mem_r2 <= 0;mem_r3 <= 0;mem_r4 <= 0;mem_r5 <= 0;mem_r6 <= 0;mem_r7 <= 0;mem_r8 <= 0;mem_r9 <= 0;end else if(state_c == SHIFT)beginmem_r0 <= {mem_w0[2:0],din_r[DIN_W-1]};mem_r1 <= {mem_w1[2:0],mem_w0[3]};mem_r2 <= {mem_w2[2:0],mem_w1[3]};mem_r3 <= {mem_w3[2:0],mem_w2[3]};mem_r4 <= {mem_w4[2:0],mem_w3[3]};mem_r5 <= {mem_w5[2:0],mem_w4[3]};mem_r6 <= {mem_w6[2:0],mem_w5[3]};mem_r7 <= {mem_w7[2:0],mem_w6[3]};mem_r8 <= {mem_w8[2:0],mem_w7[3]};mem_r9 <= {mem_w9[2:0],mem_w8[3]};endendassign mem_w0 = (mem_r0 > 4'd4)?(mem_r0 + 4'd3):mem_r0;assign mem_w1 = (mem_r1 > 4'd4)?(mem_r1 + 4'd3):mem_r1;assign mem_w2 = (mem_r2 > 4'd4)?(mem_r2 + 4'd3):mem_r2;assign mem_w3 = (mem_r3 > 4'd4)?(mem_r3 + 4'd3):mem_r3;assign mem_w4 = (mem_r4 > 4'd4)?(mem_r4 + 4'd3):mem_r4;assign mem_w5 = (mem_r5 > 4'd4)?(mem_r5 + 4'd3):mem_r5;assign mem_w6 = (mem_r6 > 4'd4)?(mem_r6 + 4'd3):mem_r6;assign mem_w7 = (mem_r7 > 4'd4)?(mem_r7 + 4'd3):mem_r7;assign mem_w8 = (mem_r8 > 4'd4)?(mem_r8 + 4'd3):mem_r8;assign mem_w9 = (mem_r9 > 4'd4)?(mem_r9 + 4'd3):mem_r9;assign bcd_res = {mem_r9,mem_r8,mem_r7,mem_r6,mem_r5,mem_r4,mem_r3,mem_r2,mem_r1,mem_r0};always @(posedge clk or negedge rst_n)beginif(!rst_n)beginbcd_dout <= 0;endelse if(state_c == DONE)beginbcd_dout <= bcd_res[DOUT_W-1:0];endendalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginbcd_dout_vld <= 1'b0;endelse beginbcd_dout_vld <= state_c == DONE;endendendmodule
ath20_top.v
module aht20_top( input clk ,input rst_n ,//data_processoutput [31:0] humi_data ,output [31:0] temp_data ,output data_vld ,//aht20output iic_scl ,input sda_in ,output sda_out ,output sda_oe
);
//---------<Internal signal definition>-----------------------------
wire start_en ;
wire [3:0] cmd ;
wire [7:0] wr_data ;
wire [7:0] rd_data ;
wire trans_done;rw_ctrl u_rw_ctrl(
.clk (clk ),
.rst_n (rst_n ),
.start_en (start_en ),
.cmd (cmd ),
.wr_data (wr_data ),
.rd_data (rd_data ),
.trans_done(trans_done),
.humi_data (humi_data ),
.temp_data (temp_data ),
.data_vld (data_vld )
);iic_intf u_iic_intf(
.clk (clk ),
.rst_n (rst_n ),
.start_en (start_en ),//读/写使能信号
.cmd (cmd ),//命令信号
.wr_data (wr_data ),//写入的数据
.rd_data (rd_data ),//读出的数据
.trans_done(trans_done),//传输1字节完成的脉冲信号
.iic_scl (iic_scl ),
.sda_in (sda_in ),
.sda_out (sda_out ),
.sda_oe (sda_oe )
);endmodule
rw_ctrl.v
module rw_ctrl (
input clk ,
input rst_n ,
//iic_intf
output reg start_en ,
output reg [3:0] cmd ,
output reg [7:0] wr_data ,
input [7:0] rd_data ,
input trans_done ,
//data_process
output reg [31:0] humi_data ,
output reg [31:0] temp_data ,
output reg data_vld
);
//---------<状态定义>-------------------------------------------------
localparam WAIT = 5'b00001, // 上电等待5msSTART = 5'b00010, // 发送测量命令:0x70、0xAC、0x33、0x00 --->4个字节IDLE = 5'b00100, // 等待AHT20数据处理完成--->延时1sREAD = 5'b01000, // 发送读数据指令,并接收数据--->发0x71、收:状态字、温湿度数据(5字节)、CRC--->8个字节VLD = 5'b10000; // 数据简单处理
reg [4:0] state_c , state_n;//---------<常数定义>-------------------------------------------------
localparam delay_1s = 50_000_000, // 1sdelay_5ms = 2_500_000 ; // 5mslocalparam start_bit = 4, //4字节read_bit = 8; //8字节
parameter CMD_START = 4'b0001,CMD_WRITE = 4'b0010,CMD_READ = 4'b0100,CMD_STOP = 4'b1000;
//---------<状态跳变条件定义>-------------------------------------------------
wire wait2start;
wire start2idle;
wire idle2read ;
wire read2vld ;
wire vld2idle ;
//---------<寄存器定义>-------------------------------------------------
//延时计数器
reg [25:0] cnt_delay ;//分时复用的延时计数器
wire add_cnt_delay;
wire end_cnt_delay;
reg [25:0] xx;
//字节计数器
reg [3:0] cnt_byte ;//分时复用的字节计数器
wire add_cnt_byte;
wire end_cnt_byte;
reg [3:0] yy;
//接收数据
reg [7:0] state_word;//状态字
reg [7:0] CRC ;//CRC校验
//接收数据
reg [7:0] humi_high ;
reg [7:0] humi_mid ;
reg [7:0] humi_temp_byte; // 包含湿度低4位和温度高4位
reg [7:0] temp_mid ;
reg [7:0] temp_low ;
//数据寄存
reg [19:0] humi_data_r ;
reg [19:0] temp_data_r ;
reg [1:0] data_vld_r ;//---------<延时计数器复用>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_delay <= 'd0;end else if(add_cnt_delay)begin if(end_cnt_delay)begin cnt_delay <= 'd0;endelse begin cnt_delay <= cnt_delay + 1'b1;end end
end assign add_cnt_delay = state_c == WAIT || state_c == IDLE ;
assign end_cnt_delay = add_cnt_delay && cnt_delay == xx - 1;always @(*)begin case (state_c)WAIT : xx = delay_5ms ;IDLE : xx = delay_1s ;default: xx = 0;endcase
end//---------<bit计数器复用>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_byte <= 'd0;end else if(add_cnt_byte)begin if(end_cnt_byte)begin cnt_byte <= 'd0;endelse begin cnt_byte <= cnt_byte + 1'b1;end end
end assign add_cnt_byte =( state_c == START || state_c == READ) && trans_done;
assign end_cnt_byte = add_cnt_byte && cnt_byte == yy - 1;always @(*)begin case (state_c)START : yy = start_bit; READ : yy = read_bit ;default: yy = 0;endcase
end//---------<描述状态转移>-------------------------------------------------
always @(posedge clk) beginif(!rst_n)state_c <= WAIT;elsestate_c <= state_n;
end//---------<描述状态转移规律>-------------------------------------------------
always @(*) begincase(state_c)WAIT :beginif(wait2start)state_n = START; elsestate_n = state_c;endSTART:beginif(start2idle)state_n = IDLE;else state_n = state_c;endIDLE :beginif(idle2read)state_n = READ;elsestate_n = state_c;endREAD :beginif(read2vld)state_n = VLD;elsestate_n = state_c;endVLD :beginif(vld2idle)state_n = START;elsestate_n = state_c;enddefault : state_n = WAIT;endcase
end//---------<状态条件赋值>-------------------------------------------------
assign wait2start = (state_c == WAIT) && (end_cnt_delay) ;
assign start2idle = (state_c == START) && (end_cnt_byte) ;
assign idle2read = (state_c == IDLE) && (end_cnt_delay) ;
assign read2vld = (state_c == READ) && (end_cnt_byte) ;
assign vld2idle = (state_c == VLD);//---------<状态输出>-------------------------------------------------
//start_en cmd wr_data
always @(*)begin case (state_c)START : begincase (cnt_byte)0 : begin start_en = 1'b1;cmd = CMD_START | CMD_WRITE;wr_data = 8'h70;end1 : begin start_en = 1'b1;cmd = CMD_WRITE ;wr_data = 8'hAC;end2 : begin start_en = 1'b1;cmd = CMD_WRITE ;wr_data = 8'h33;end3 : begin start_en = 1'b1;cmd = CMD_STOP | CMD_WRITE ;wr_data = 8'h00;enddefault: begin start_en = 1'b0;cmd = 0 ;wr_data = 8'h00;endendcaseendREAD : begincase (cnt_byte)0 : begin start_en = 1'b1;cmd = CMD_START | CMD_WRITE;wr_data = 8'h71;end7 : begin start_en = 1'b1;cmd = CMD_STOP | CMD_READ ;wr_data = 8'h00;enddefault: begin start_en <= 1'b1;cmd <= CMD_READ ;wr_data <= 8'h00;endendcaseenddefault: begin start_en <= 1'b0;cmd <= 4'd0 ;wr_data <= 8'h00;endendcase
end//data
always @(posedge clk or negedge rst_n)begin if(!rst_n)begin// templete_data <= 'd0;state_word <= 'd0;humi_high <= 'd0;humi_mid <= 'd0;humi_temp_byte <= 'd0;temp_mid <= 'd0;temp_low <= 'd0;CRC <= 'd0;end else if(state_c == READ && cnt_byte >= 1 && trans_done)begin case (cnt_byte)1 : state_word <= rd_data;2 : humi_high <= rd_data;3 : humi_mid <= rd_data;4 : humi_temp_byte <= rd_data;5 : temp_mid <= rd_data;6 : temp_low <= rd_data;7 : CRC <= rd_data;default: ;endcase// templete_data[39-(cnt_byte-2)*8 -:8] <= rd_data;end
end//data_vld_r
always @(posedge clk or negedge rst_n)begin if(!rst_n)begindata_vld_r <= 'd0;end else begin data_vld_r <= {data_vld_r[0],read2vld};end
end//流水线处理
always @(*)begin if(!rst_n)beginhumi_data_r = 'd0;temp_data_r = 'd0;end else if(data_vld_r[0])begin humi_data_r = {humi_high,humi_mid,humi_temp_byte[7:4]};temp_data_r = {humi_temp_byte[3:0],temp_mid,temp_low};end else beginhumi_data_r = 'd0;temp_data_r = 'd0;end
endalways @(posedge clk or negedge rst_n)begin if(!rst_n)beginhumi_data <= 'd0;temp_data <= 'd0;data_vld <= 'd0;end else if(data_vld_r[0])begin //RH[%] = (Srh / 2^^20) * 100% -- 扩大10倍//2^^9 + 2^^8 + 2^^7 + 2^^6 + 2^^5 + 2^^3 == 1000humi_data <= ((humi_data_r<<9) + (humi_data_r<<8) + (humi_data_r<<7) + (humi_data_r<<6) + (humi_data_r<<5) + (humi_data_r<<3))>>20;//T(℃) = (St / 2^^20) * 200 - 50--扩大10倍//2^^10 + 2^^9 + 2^^8 + 2^^7 + 2^^6 + 2^^4 == 2000temp_data <= (((temp_data_r<<10) + (temp_data_r<<9) + (temp_data_r<<8) + (temp_data_r<<7) +(temp_data_r<<6) + (temp_data_r<<4))>>20) - 500;data_vld <= 1'b1;end else begin data_vld <= 1'b0;end
endendmodule
iic_intf.v
module iic_intf#(parameter IIC_RATE = 100_000,SYS_CLOCK = 50_000_000
)( //userinput clk ,input rst_n ,input start_en ,//读/写使能信号input [3:0] cmd ,//命令信号input [7:0] wr_data ,//写入的数据output reg [7:0] rd_data ,//读出的数据output trans_done,//传输1字节完成的脉冲信号//iicoutput reg iic_scl ,input sda_in ,output reg sda_out ,output reg sda_oe );
//---------<Parameter definition>-----------------------------------
localparam IDLE = 7'b000_0001,//空闲状态START = 7'b000_0010,//起始信号状态WR_DATA = 7'b000_0100,//写数据状态R_ACK = 7'b000_1000,//接收应答状态RD_DATA = 7'b001_0000,//读数据状态S_ACK = 7'b010_0000,//发送应答状态STOP = 7'b100_0000;//停止信号状态localparam SCL_MAX = SYS_CLOCK/IIC_RATE;
localparam HALF_SCL = SCL_MAX>>1,CHANGE_TIME = SCL_MAX>>2,SAMPLE_TIME = HALF_SCL + CHANGE_TIME;//---------<Internal signal definition>-----------------------------
reg [6:0] state_c ;
reg [6:0] state_n ;
reg [8:0] cnt_scl ;//串行时钟计数器
wire add_cnt_scl;
wire end_cnt_scl;
reg [2:0] cnt_bit ;//比特计数器
wire add_cnt_bit;
wire end_cnt_bit;//状态转移条件
wire idle2start ;
wire idle2wr_data ;
wire idle2rd_data ;
wire start2wr_data;
wire wr_data2r_ack;
wire r_ack2idle ;
wire r_ack2stop ;
wire rd_data2s_ack;
wire s_ack2idle ;
wire s_ack2stop ;
wire stop2idle ;//*******************************************************************
//--State Machine
//*******************************************************************
//第一段:同步时序描述状态转移
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end
end//第二段:组合逻辑判断状态转移条件,描述状态转移方向
always @(*) begincase(state_c)IDLE : begin if(idle2start)state_n = START;else if(idle2wr_data)state_n = WR_DATA;else if(idle2rd_data)state_n = RD_DATA;else state_n = state_c;end START : begin if(start2wr_data)state_n = WR_DATA;else state_n = state_c;end WR_DATA : begin if(wr_data2r_ack)state_n = R_ACK;else state_n = state_c;end R_ACK : begin if(r_ack2idle)state_n = IDLE;else if(r_ack2stop)state_n = STOP;else state_n = state_c;end RD_DATA : begin if(rd_data2s_ack)state_n = S_ACK;else state_n = state_c;end S_ACK : begin if(s_ack2idle)state_n = IDLE;else if(s_ack2stop)state_n = STOP;else state_n = state_c;end STOP : begin if(stop2idle)state_n = IDLE;else state_n = state_c;end default : ;endcase
endassign idle2start = (state_c == IDLE) && start_en && cmd[0];
assign idle2wr_data = (state_c == IDLE) && start_en && cmd[1];
assign idle2rd_data = (state_c == IDLE) && start_en && cmd[2];
assign start2wr_data = (state_c == START) && end_cnt_scl;
assign wr_data2r_ack = (state_c == WR_DATA) && end_cnt_bit;
assign r_ack2idle = (state_c == R_ACK) && end_cnt_scl && ~cmd[3];
assign r_ack2stop = (state_c == R_ACK) && end_cnt_scl && cmd[3];
assign rd_data2s_ack = (state_c == RD_DATA) && end_cnt_bit;
assign s_ack2idle = (state_c == S_ACK) && end_cnt_scl && cmd[3] == 0;
assign s_ack2stop = (state_c == S_ACK) && end_cnt_scl && cmd[3];
assign stop2idle = (state_c == STOP) && end_cnt_scl; //cnt_scl
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_scl <= 'd0;end else if(add_cnt_scl)begin if(end_cnt_scl)begin cnt_scl <= 'd0;endelse begin cnt_scl <= cnt_scl + 1'b1;end end
end assign add_cnt_scl = state_c != IDLE;
assign end_cnt_scl = add_cnt_scl && cnt_scl == SCL_MAX - 1;//cnt_bit
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse begin cnt_bit <= cnt_bit + 1'b1;end end
end assign add_cnt_bit = end_cnt_scl && (state_c == WR_DATA || state_c == RD_DATA);
assign end_cnt_bit = add_cnt_bit && cnt_bit == 8 - 1;//iic_scl
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginiic_scl <= 1'b1;end else if(state_c != IDLE)begin if(cnt_scl < HALF_SCL)iic_scl <= 1'b0;else iic_scl <= 1'b1;end else begin iic_scl <= 1'b1;end
end//sda_out sda_oe
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginsda_oe <= 1'b0;sda_out <= 1'b1;end else begin case (state_c)IDLE : beginsda_oe <= 1'b0;sda_out <= 1'b1;endSTART : beginsda_oe <= 1'b1;sda_out <= (cnt_scl < SAMPLE_TIME) ? 1'b1 : 1'b0;// if(cnt_scl < SAMPLE_TIME)// sda_out <= 1'b1;// else // sda_out <= 1'b0;endWR_DATA : beginsda_oe <= 1'b1;if(cnt_scl == CHANGE_TIME - 1)//并转串sda_out <= wr_data[7-cnt_bit];else sda_out <= sda_out;endR_ACK : beginsda_oe <= 1'b0;sda_out <= 1'b1;endRD_DATA : beginsda_oe <= 1'b0;sda_out <= 1'b1;endS_ACK : beginsda_oe <= 1'b1;if(cnt_scl == CHANGE_TIME - 1)sda_out <= cmd[3] ? 1'b1 : 1'b0;endSTOP : beginsda_oe <= 1'b1;if(cnt_scl == CHANGE_TIME - 1)sda_out <= 1'b0;else if(cnt_scl == SAMPLE_TIME - 1)sda_out <= 1'b1;end default: ;endcaseend
end//rd_data
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrd_data <= 'd0;end else if(state_c == RD_DATA && cnt_scl == SAMPLE_TIME - 1)begin // 串转并rd_data[7-cnt_bit] <= sda_in;end
end//trans_done
assign trans_done = r_ack2idle | s_ack2idle | stop2idle;endmodule
五、实验现象
以下就是串口助手显示的温湿度数据。
以上就是AHT20温湿度读取并在串口显示(IIC协议)。(如果有错误,还请大家指出来,谢谢!)