主要是参考以下这个blog
【接口时序】6、IIC总线的原理与Verilog实现 - jgliu - 博客园
如下图,ADS1015的数据每次读或者写操作,都是先写入指针寄存器的值,再去读或者写16bit的数据。
参考链接中,IIC操作流程是传送设备地址→传送指针寄存器→传送设备地址→读/写8bit数据→结束。
ADS1015的操作流程是传送设备地址→传送指针寄存器→传送设备地址→读/写高8bit数据→读/写低8bit数据→结束
相比于参考链接中的IIC操作过程,ADS1015多了一个读8bit数据过程。因此,在原来的基础上增加一个读/写8bit数据的状态即可。
参考链接中,iic_recv.v和iic_send.v的信号命名都一致,可以很好的合并在一起。
在这里,我在移植代码只ADS1015的过程中,先将iic_recv和iic_send的读写16bit数据流程调通,然后再合并,最后通过一个顶层文件中的状态机进行调用,轮流读取4个通道的电压数值。
对比参考链接 recv过程中修改了4'd12(包括)之后的内容,send修改了4'd9和4'd10状态
module iic_recv_send_ctrl
(
input I_clk , // 系统50MHz时钟
input I_rst_n , // 系统全局复位
input I_iic_recv_en , // IIC发送使能位
input I_iic_send_en , // IIC发送使能位
input [6:0] I_dev_addr , // IIC设备的物理地址
input [7:0] I_word_addr , // IIC设备的字地址,即我们想操作的IIC的内部地址
input [15:0] I_write_data , // 往IIC设备的字地址写入的数据
output reg [7:0] O_read_data , // 从IIC设备的字地址读出来的数据
output reg O_done_flag , // 读或写IIC设备结束标志位
output [15:0] O_read_16bit_data,
// 标准的IIC设备总线
output O_scl , // IIC总线的串行时钟线
inout IO_sda // IIC总线的双向数据线
// output mode_test
);
parameter C_DIV_SELECT = 10'd500 ; // 分频系数选择
parameter C_DIV_SELECT0 = (C_DIV_SELECT >> 2) - 1 , // 用来产生IIC总线SCL低电平最中间的标志位
C_DIV_SELECT1 = (C_DIV_SELECT >> 1) - 1 , // 用来产生IIC串行时钟线
C_DIV_SELECT2 = (C_DIV_SELECT0 + C_DIV_SELECT1) + 1 , // 用来产生IIC总线SCL高电平最中间的标志位
C_DIV_SELECT3 = (C_DIV_SELECT >> 1) + 1 ; // 用来产生IIC总线SCL下降沿标志位
reg [9:0] R_scl_cnt ; // 用来产生IIC总线SCL时钟线的计数器
reg R_scl_en ; // IIC总线SCL时钟线使能信号
reg [3:0] R_state ;
reg R_sda_mode ; // 设置SDA模式,1位输出,0为输入
reg R_sda_reg ; // SDA寄存器
reg [7:0] R_load_data ; // 发送/接收过程中加载的数据,比如设备物理地址,字地址和数据等
reg [3:0] R_bit_cnt ; // 发送字节状态中bit个数计数
reg R_ack_flag ; // 应答标志
reg [3:0] R_jump_state ; // 跳转状态,传输一个字节成功并应答以后通过这个变量跳转到导入下一个数据的状态
reg [7:0] R_read_data_reg ;
reg [15:0] read_reg_16bit_data;
reg [7:0] r_high_bit_data;
reg [7:0] r_low_bit_data;
reg [7:0] r_low_bit_data_r;
wire W_scl_low_mid ; // SCL的低电平中间标志位
wire W_scl_high_mid ; // SCL的高电平中间标志位
wire W_scl_neg;
assign IO_sda = (R_sda_mode == 1'b1) ? R_sda_reg : 1'bz ;
assign O_read_16bit_data = read_reg_16bit_data;
//assign mode_test = R_sda_mode;
always @(posedge I_clk or negedge I_rst_n)
begin
if(!I_rst_n)
R_scl_cnt <= 10'd0 ;
else if(R_scl_en)
begin
if(R_scl_cnt == C_DIV_SELECT - 1'b1)
R_scl_cnt <= 10'd0 ;
else
R_scl_cnt <= R_scl_cnt + 1'b1 ;
end
else
R_scl_cnt <= 10'd0 ;
end
assign O_scl = (R_scl_cnt <= C_DIV_SELECT1) ? 1'b1 : 1'b0 ; // 产生串行时钟信号O_scl
assign W_scl_low_mid = (R_scl_cnt == C_DIV_SELECT2) ? 1'b1 : 1'b0 ; // 产生scl低电平正中间标志位
assign W_scl_high_mid = (R_scl_cnt == C_DIV_SELECT0) ? 1'b1 : 1'b0 ; // 产生scl高电平正中间标志位
assign W_scl_neg = (R_scl_cnt == C_DIV_SELECT3) ? 1'b1 : 1'b0 ; // 产生scl下降沿标志位
always @(posedge I_clk or negedge I_rst_n)
begin
if(!I_rst_n)
begin
R_state <= 4'd0 ;
R_sda_mode <= 1'b1 ;
R_sda_reg <= 1'b1 ;
R_bit_cnt <= 4'd0 ;
O_done_flag <= 1'b0 ;
R_jump_state <= 4'd0 ;
R_read_data_reg <= 8'd0 ;
R_ack_flag <= 1'b0 ;
O_read_data <= 8'd0 ;
r_low_bit_data_r <= 8'd0;
r_low_bit_data <= 8'd0;
read_reg_16bit_data <= 16'd0;
end
else if(I_iic_recv_en) // 从IIC设备 读取数据
begin
case(R_state)
4'd0 : // 空闲状态,用来初始化相关所有信号
begin
R_sda_mode <= 1'b1 ; // 设置SDA为输出