I 说明
本程式对IIC协议模块:链接,进行连续读写测试。
测试对象为AP3216C传感器,通过对其进行连续的写数据,读数据。从而完成对该传感器的初始化与使用。
学习的重点应为使用多路器+状态值 的方法,借助IIC协议模块“iic_done”信号的反馈,来完成连续读写。
II AP3216C
AP3216C是一款环境光传感器,本程式将通过IIC协议读出此传感器的两个值:
16bit的【als_data】(ALS数据的[7:0]储存在0C地址的[7:0],ALS数据的[15:8]储存在0D地址的[7:0]) 与
10bit的【ps_data】(PS数据的[3:0]储存在0E地址的[3:0],PS数据的[9:4]储存在0F地址的[5:0])
读取这两种数据之前都需要一定的【准备时间】
该传感器需要向 [00H] 地址 写入111启用ALS+PS+IR的单次模式(初始化)
III 程式说明
【AP3216C模块】控制【IIC协议模块】对AP3216C进行读写控制,读出两组数据"[15:0]als_data" 与 “[9:0]ps_data”
【数码管显示模块】用来驱动数码管的显示,【按键模块】将输入的按键转化为状态值,当按键一次按下,数码管显示als_data数据,当按键再次按下,数码管显示ps_data数据。
IV 模块分解
1顶层模块
module ap3216c_top(
//global clock
input sys_clk ,
input sys_rst_n ,
//按键输出(触摸按键,无去抖过程)
input touch_key,
//连接IIC从器件ap3216c
output scl ,
inout sda ,
//连接六位八段数码管
output [5:0] sel ,
output [7:0] seg
);
//...
wire clk ; // 1M时钟“dri_clk”引出
//...
wire i2c_exec ; // iic使能
wire [15:0] iic_inner_reg_addr ; // i2c从器件内部寄存器地址
wire i2c_done ; // i2c操作结束标志
wire read1_write0 ; // i2c读写控制
wire [ 7:0] i2c_data_r; // i2c读出数据
wire [ 7:0] i2c_data_w; // i2c写入数据
//...
wire [15:0] als_data ; // ALS的数据
wire [ 9:0] ps_data ; // PS的数据
i2c_dri(
//fpga
.clk(sys_clk) , // 输入时钟(clk_freq)
.rst_n(sys_rst_n) , // 复位信号
//addr and data
.slave_address(7'h1e), //从机地址
.iic_inner_reg_addr(iic_inner_reg_addr), //从机内部寄存器地址,若为8bit形式,直接用低八位
.i2c_w_data(i2c_data_w), //写数据端口
.i2c_r_data(i2c_data_r), //读数据端口
//select mode
.type16_type8(1'b0), //从机内部寄存器地址是否属于16bit形式(16-1/8-0)
.read1_write0(read1_write0), //读模式或写模式
//i2c interface
.i2c_exec(i2c_exec) , // IIC使能【1】
.i2c_done(i2c_done) , // 一次操作完成的标志信号
.scl(scl) , // SCL输出
.sda(sda) , // SDA输出
//user interface
.dri_clk(clk) // 低频时钟输出
);
ap3216c u_ap3216c(
.clk (clk ),
.rst_n (sys_rst_n ),
//iic ctrl
.read1_write0 (read1_write0 ),
.i2c_exec (i2c_exec ),
.iic_inner_reg_addr (iic_inner_reg_addr ),
.i2c_data_w (i2c_data_w),
.i2c_data_r (i2c_data_r),
.i2c_done (i2c_done ),
//data to dig_tube
.als_data (als_data ),
.ps_data (ps_data )
);
touch_key f0(
.in(touch_key),
.clk(sys_clk),
.rst_n(sys_rst_n),
.outstate(shift)
);
wire shift;
wire [15:0] data_out;
assign data_out = shift ? ps_data : als_data;
dig8_6 f1(
.clk(sys_clk),
.rst(sys_rst_n),
.set_data(data_out),//6xh h=4
.dig(sel), //六位独热码表示六个数码管
.dict(seg)
);
endmodule
2AP3216C模块(控制连续读写)
/*
数字环境光传感器:ALS
距离传感器:PS
红外LED:IR
[内部寄存器地址] (有效位) 含义
[00H] (2:0) 写入111代表启用ALS+PS+IR的单次模式
[0AH] (7) 读出0代表IR&PS的数据有效 1无效
[0AH] (1:0) 读出IR有效数据位[1:0]
[0BH] (7:0) 读出IR有效数据位[7:0]
[0CH] (7:0) 读出ALS有效数据位[7:0]
[0DH] (7:0) 读出ALS有效数据位[15:8]
[0EH] (7) 读出0代表物体远离 读出1代表物体靠近
[0EH] (6) 读出0代表PS&IR有效 1代表无效
[0EH] (3:0) 读出PS有效数据位[3:0]
[0FH] (7) 读出0代表物体远离 读出1代表物体靠近
[0FH] (6) 读出0代表PS&IR有效 1代表无效
[0FH] (5:0) 读出PS有效数据位[5:0]
。
本程式将PS数据与ALS数据读出,
PS为10位数据:PS数据的[3:0]储存在0E地址的[3:0],PS数据的[9:4]储存在0F地址的[5:0]
ALS为16位数据:ALS数据的[7:0]储存在0C地址的[7:0],ALS数据的[15:8]储存在0D地址的[7:0]
操作过程:初始化(0~2) -> 读PS(3~8) -> 读ALS(9~14) -> 读PS(3~8)...
*/
module ap3216c(
input clk ,
input rst_n ,
output reg read1_write0 , // IIC读写控制
output reg i2c_exec , // IIC使能
output reg [15:0] iic_inner_reg_addr , // AP3216C内部寄存器地址
output reg [ 7:0] i2c_data_w , // I2C写8bit数据
input [ 7:0] i2c_data_r , // I2C读8bit数据
input i2c_done , // IIC一次操作完成标志
output reg [15:0] als_data , // ALS的数据
output reg [ 9:0] ps_data // PS的数据
);
//parameter define
parameter TIME_PS = 14'd12_500 ; // PS转换时间为12.5ms(clk = 1MHz)
parameter TIME_ALS = 17'd100_000 ; // ALS转换时间为100ms(clk = 1MHz)
parameter TIME_REST = 8'd2 ; // 停止后重新开始的时间间隔控制
//reg define
reg [ 3:0] flow_cnt ; // 状态流控制
reg [18:0] wait_cnt ; // 计数等待
reg [15:0] als_data_t ; // ALS的临时数据
reg als_done ; // 环境光照强度值采集完成信号
reg [ 9:0] ps_data_t ; // PS的临时数据
//配置AP3216C并读取数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
i2c_exec <= 1'b0;
iic_inner_reg_addr <= 8'd0;
read1_write0 <= 1'b0;
i2c_data_w <= 8'h0;
flow_cnt <= 4'd0;
wait_cnt <= 18'd0;
ps_data <= 10'd0;
ps_data_t <= 10'd0;
als_data_t <= 16'd0;
als_done <= 1'b0;
end
else begin
i2c_exec <= 1'b0;
case(flow_cnt)
//【0:等待0.01ms】 ->1
4'd0: begin
if(wait_cnt == 18'd100) begin
wait_cnt <= 18'd0;
flow_cnt <= flow_cnt + 1'b1;
end
else
wait_cnt <= wait_cnt +1'b1;
end
//【1、向00H写入03H,启用ALS+PS+IR的单次模式】->2
4'd1: begin
i2c_exec <= 1'b1 ;
read1_write0 <= 1'b0 ;
iic_inner_reg_addr <= 8'h00;
i2c_data_w <= 8'h03;
flow_cnt <= flow_cnt + 1'b1;
end
//【2、等待IIC发送完成】->3
4'd2: begin
if(i2c_done)
flow_cnt <= flow_cnt + 1'b1;
end
//【3、等待12.5ms,因即将读取PS,手册要求读取PS前准备12.5ms】->4
4'd3: begin
if(wait_cnt == TIME_PS) begin
als_done <= 1'b0; //清除als_done标志
wait_cnt <= 18'd0;
flow_cnt <= flow_cnt + 1'd1;
end
else
wait_cnt <= wait_cnt + 1'b1;
end
/*【4、对地址0EH做一次读操作】 ->5
[0EH] (3:0) 读出PS有效数据位[3:0]
*/
4'd4: begin
i2c_exec <= 1'b1;
read1_write0<= 1'b1;
iic_inner_reg_addr <= 8'h0E;
flow_cnt <= flow_cnt + 1'b1;
end
//【5、等待IIC的读操作完成,并处理数据】->6
4'd5: begin
if(i2c_done) begin
flow_cnt <= flow_cnt + 1'b1;
ps_data_t[3:0] <= i2c_data_r[3:0];;
end
end
//【6、等待一段时间以进行下一次读写】 ->7
4'd6: begin
if(wait_cnt == TIME_REST) begin//TIME_REST
wait_cnt <= 18'd0;
flow_cnt <= flow_cnt + 1'b1;
end
else
wait_cnt <= wait_cnt +1'b1;
end
/*【7、对地址0FH做一次读操作】 ->8
[0FH] (5:0) 读出PS有效数据位[3:0]
*/
4'd7: begin
i2c_exec <= 1'b1;
read1_write0<= 1'b1;
iic_inner_reg_addr <= 8'h0F;
flow_cnt <= flow_cnt + 1'b1;
end
//【8、等待IIC的读操作完成,并处理数据】->9
4'd8: begin
if(i2c_done) begin
flow_cnt <= flow_cnt + 1'b1;
ps_data_t[9:4] <= i2c_data_r[5:0];
end
end
//【9、等待100ms,因即将读取ALS,手册要求读取ALS前准备100ms,同时将接收到的PS整理】->10
4'd9: begin
if(wait_cnt == TIME_ALS) begin
wait_cnt <= 18'd0;
flow_cnt <= flow_cnt + 1'd1;
ps_data <= ps_data_t;
end
else
wait_cnt <= wait_cnt + 1'b1;
end
/*【10、对地址0CH做一次读操作】 ->11
[0CH] (7:0) 读出ALS有效数据位[7:0]
*/
4'd10: begin
i2c_exec <= 1'b1;
read1_write0<= 1'b1;
iic_inner_reg_addr <= 8'h0C;
flow_cnt <= flow_cnt + 1'b1;
end
//【11、等待IIC的读操作完成,并处理数据】->12
4'd11: begin
if(i2c_done) begin
als_data_t[7:0] <= i2c_data_r;
flow_cnt <= flow_cnt + 1'b1;
end
end
//【12、等待一段时间以进行下一次读写】 ->13
4'd12: begin
if(wait_cnt == TIME_REST) begin
wait_cnt <= 18'd0;
flow_cnt <= flow_cnt + 1'b1;
end
else
wait_cnt <= wait_cnt +1'b1;
end
/*【13、对地址0DH做一次读操作】 ->14
[0DH] (7:0) 读出ALS有效数据位[15:8]
*/
4'd13: begin
i2c_exec <= 1'b1;
read1_write0<= 1'b1;
iic_inner_reg_addr <= 8'h0D;
flow_cnt <= flow_cnt + 1'b1;
end
//【14、等待IIC的读操作完成,并处理数据,输出als_done标志,提供给用于(单位转换)的always块使用】 ->3
4'd14: begin
if(i2c_done) begin
als_done <= 1'b1;
als_data_t[15:8] <= i2c_data_r;
flow_cnt <= 4'd3;
end
end
endcase
end
end
//当采集的环境光转换成光照强度(单位:lux)
always @ (*) begin
if(als_done)
als_data = als_data_t * 6'd35 / 7'd100;
end
endmodule
3按键转状态模块
/*
触摸按键:当按键按下时outstate输出1,再次按下时outstate输出0. 循环切换。
*/
module touch_key (in,clk,rst_n,outstate);
input in; //按键输入(物理)
input clk;
input rst_n;
output reg outstate; //按键按下状态量输出
//同步
reg temp1,temp2;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
temp1 <= 1'b0;
temp2 <= 1'b0;
end
else
begin
temp1 <= in ;
temp2 <= temp1;
end
//上升沿检测
reg temp3,temp4;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
temp3 <= 1'b0;
temp4 <= 1'b0;
end
else
begin
temp3 <= temp2 ;
temp4 <= temp3;
end
wire up;
assign up = temp3 & (!temp4);
//状态切换
reg switch;
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
outstate <= 1'b0;
else if(up == 1'b1)
outstate <= ~outstate;
else
outstate <= outstate;
end
endmodule