cyclone4驱动LM75A温湿度传感器学习
1. LM75A第一次使用,I2C接口,8脚
2. 打开quartus工程,下面只要是看看代码结构,问题在于多个always语句,逻辑上不太好分清楚,主要看状态机
1 module I2C_READ( 2 clk, 3 rst_n, 4 scl,sda,data 5 ); 6 7 input clk;//总线时钟 50MHz 8 input rst_n;//异步复位,低电平有效 9 10 output scl;//SCL 时钟 11 inout sda;// SDA 数据总线 12 output [15:0] data;//温度数据 13 14 reg [15:0]data_r;//温度数据寄存器 15 reg scl;//SCL 总线寄存器 16 reg sda_r;//SDA 总线寄存器 17 reg sda_link;//SDA 总线数据方向标志 18 reg [7:0]scl_cnt;//SCL 时钟产生计数器 19 reg [2:0]cnt;//用来标记SCL时钟计数器 20 reg [25:0]timer_cnt;//定时器,每隔2s 读取一次温度数据 21 reg [3:0]data_cnt;//数据串并转换寄存器 22 reg [7:0]address_reg;//器件地址寄存器 23 reg [8:0]state;//状态寄存器 24 ////////////////////////////////////////////////////////////////////////////////// 25 //进程1、2、3:产生SCL 总线时钟 26 always@(posedge clk or negedge rst_n) 27 begin 28 if(!rst_n) 29 scl_cnt <= 8'd0; 30 else if(scl_cnt == 8'd199) 31 scl_cnt <= 8'd0; 32 else 33 scl_cnt <= scl_cnt + 1'b1; 34 end 35 always@(posedge clk or negedge rst_n) 36 begin 37 if(!rst_n) 38 cnt <= 3'd5; 39 else 40 case(scl_cnt) 41 8'd49: cnt <= 3'd1;//高电平中间 42 8'd99: cnt <= 3'd2;//下降沿 43 8'd149:cnt <= 3'd3;//低电平中间 44 8'd199:cnt <= 3'd0;//上升沿 45 default: cnt <= 3'd5; 46 endcase 47 end 48 `define SCL_HIG (cnt == 3'd1) 49 `define SCL_NEG (cnt == 3'd2) 50 `define SCL_LOW (cnt == 3'd3) 51 `define SCL_POS (cnt == 3'd0) 52 always@(posedge clk or negedge rst_n) 53 begin 54 if(!rst_n) 55 scl <= 1'b0; 56 else if(`SCL_POS) 57 scl <= 1'b1; 58 else if(`SCL_NEG) 59 scl <= 1'b0; 60 end 61 ////////////////////////////////////////////////////////////////////////////////// 62 //进程4:定时器,每隔1s 读取一次温度数据 63 always@(posedge clk or negedge rst_n) 64 begin 65 if(!rst_n) 66 timer_cnt <= 26'd0; 67 else if(timer_cnt == 26'd49999999) 68 timer_cnt <= 26'd0; 69 else 70 timer_cnt <= timer_cnt + 1'b1; 71 end 72 ////////////////////////////////////////////////////////////////////////////////// 73 //状态机定义 74 parameter IDLE = 9'b0_0000_0000, 75 START = 9'b0_0000_0010, 76 ADDRESS = 9'b0_0000_0100, 77 ACK1 = 9'b0_0000_1000, 78 READ1 = 9'b0_0001_0000, 79 ACK2 = 9'b0_0010_0000, 80 READ2 = 9'b0_0100_0000, 81 NACK = 9'b0_1000_0000, 82 STOP = 9'b1_0000_0000; 83 `define DEVICE_ADDRESS 8'b1001_0001//器件地址,读操作 84 ////////////////////////////////////////////////////////////////////////////////// 85 //进程5:状态机描述 86 always@(posedge clk or negedge rst_n) 87 begin 88 if(!rst_n) 89 begin 90 data_r <= 16'd0; 91 sda_r <= 1'b1; 92 sda_link <= 1'b1; 93 state <= IDLE; 94 address_reg <= 15'd0; 95 data_cnt <= 4'd0; 96 end 97 else 98 case(state) 99 IDLE: 100 begin 101 sda_r <= 1'b1; 102 sda_link <= 1'b1; 103 if(timer_cnt == 26'd49999999) 104 state <= START; 105 else 106 state <= IDLE; 107 end 108 START://产生起始信号 109 begin 110 if(`SCL_HIG) 111 begin 112 sda_r <= 1'b0; 113 sda_link <= 1'b1; 114 address_reg <= `DEVICE_ADDRESS; 115 state <= ADDRESS; 116 data_cnt <= 4'd0; 117 end 118 else 119 state <= START; 120 end 121 ADDRESS://主机对器件进行寻址 122 begin 123 if(`SCL_LOW) 124 begin 125 if(data_cnt == 4'd8)//寻址完成,SDA改变方向,器件准备输出应答讯号 126 begin 127 state <= ACK1; 128 data_cnt <= 4'd0; 129 sda_r <= 1'b1; 130 sda_link <= 1'b0; 131 end 132 else//寻址过程中,SDA对器件作为输入 133 begin 134 state <= ADDRESS; 135 data_cnt <= data_cnt + 1'b1; 136 case(data_cnt) 137 4'd0: sda_r <= address_reg[7]; 138 4'd0: sda_r <= address_reg[7]; 139 4'd1: sda_r <= address_reg[6]; 140 4'd2: sda_r <= address_reg[5]; 141 4'd3: sda_r <= address_reg[4]; 142 4'd4: sda_r <= address_reg[3]; 143 4'd5: sda_r <= address_reg[2]; 144 4'd6: sda_r <= address_reg[1]; 145 4'd7: sda_r <= address_reg[0]; 146 default: ; 147 endcase 148 end 149 end 150 else 151 state <= ADDRESS; 152 end 153 ACK1://器件输出应答信号 154 begin 155 if(!sda && (`SCL_HIG)) 156 state <= READ1; 157 else if(`SCL_NEG) 158 state <= READ1; 159 else 160 state <= ACK1; 161 end 162 READ1://读器件数据,高字节 163 begin 164 if((`SCL_LOW) && (data_cnt == 4'd8))//读高字节数据完成,SDA改变方向,主机准备输出应答讯号 165 begin 166 state <= ACK2; 167 data_cnt <= 4'd0; 168 sda_r <= 1'b1; 169 sda_link <= 1'b1; 170 end 171 else if(`SCL_HIG)//读数据过程中,器件作为输出,这里有疑问,不是应该的等SCL一个时钟吗?SCL_HIG 172 begin 173 data_cnt <= data_cnt + 1'b1; 174 case(data_cnt) 175 4'd0: data_r[15] <= sda; 176 4'd1: data_r[14] <= sda; 177 4'd2: data_r[13] <= sda; 178 4'd3: data_r[12] <= sda; 179 4'd4: data_r[11] <= sda; 180 4'd5: data_r[10] <= sda; 181 4'd6: data_r[9] <= sda; 182 4'd7: data_r[8] <= sda; 183 default: ; 184 endcase 185 end 186 else 187 state <= READ1; 188 end 189 ACK2://主机输出应答讯号 190 begin 191 if(`SCL_LOW) 192 sda_r <= 1'b0; 193 else if(`SCL_NEG) 194 begin 195 sda_r <= 1'b1; 196 sda_link <= 1'b0; 197 state <= READ2; 198 end 199 else 200 state <= ACK2; 201 end 202 READ2://读低字节数据 203 begin 204 if((`SCL_LOW) && (data_cnt == 4'd8)) 205 begin 206 state <= NACK; 207 data_cnt <= 4'd0; 208 sda_r <= 1'b1; 209 sda_link <= 1'b1; 210 end 211 else if(`SCL_HIG) 212 begin 213 data_cnt <= data_cnt + 1'b1; 214 case(data_cnt) 215 4'd0: data_r[7] <= sda; 216 4'd1: data_r[6] <= sda; 217 4'd2: data_r[5] <= sda; 218 4'd3: data_r[4] <= sda; 219 4'd4: data_r[3] <= sda; 220 4'd5: data_r[2] <= sda; 221 4'd6: data_r[1] <= sda; 222 4'd7: data_r[0] <= sda; 223 default: ; 224 endcase 225 end 226 else 227 state <= READ2; 228 end 229 NACK://主机非应答 230 begin 231 if(`SCL_LOW) 232 begin 233 state <= STOP; 234 sda_r <= 1'b0; 235 end 236 else 237 state <= NACK; 238 end 239 STOP: 240 begin 241 if(`SCL_HIG) 242 begin 243 state <= IDLE; 244 sda_r <= 1'b1; 245 end 246 else 247 state <= STOP; 248 end 249 default: state <= IDLE; 250 endcase 251 end 252 assign sda = sda_link ? sda_r: 1'bz; 253 assign data = data_r; 254 endmodule
3. 这个代码需要好好研究下,有疑问的地方,data_cnt <= data_cnt + 1'b1;每次读一位的时候,SCL应该有一个时钟,按照这个时钟去读的,为啥代码使用data_cnt这个变量,感觉不对啊?这里不是很明白
1 begin 2 134 state <= ADDRESS; 3 135 data_cnt <= data_cnt + 1'b1; 4 136 case(data_cnt) 5 137 4'd0: sda_r <= address_reg[7]; 6 138 4'd0: sda_r <= address_reg[7]; 7 139 4'd1: sda_r <= address_reg[6]; 8 140 4'd2: sda_r <= address_reg[5]; 9 141 4'd3: sda_r <= address_reg[4]; 10 142 4'd4: sda_r <= address_reg[3]; 11 143 4'd5: sda_r <= address_reg[2]; 12 144 4'd6: sda_r <= address_reg[1]; 13 145 4'd7: sda_r <= address_reg[0]; 14 146 default: ; 15 147 endcase 16 148 end