其他分享
首页 > 其他分享> > FPGA——从IIC到SCCB状态机实现

FPGA——从IIC到SCCB状态机实现

作者:互联网

一、SCCB协议注意事项

1、读时序


2阶段写紧跟着一个2阶段读,意思是读时序有一个完整的2阶段写以及一个完整的2阶段读
所以,读时序是有两个完整阶段的,2阶段写有起始位停止位,2阶段读也有起始位和停止位

2、IIC协议与SCCB协议

IIC协议与SCCB协议的写时序是完全相同的,而读时序通过以上的说明可以知道,SCCB的读时序只不过比IIC多了一个停止位而已

二、设计思路

在IIC的状态机基础下,加一个停止位即可

https://www.cnblogs.com/cnlntr/p/14378951.html

IIC部分

SCCB部分

三、SCCB代码

module sccb_module(

   rst_n       , 
   clk         ,
   waddr_num   ,
   device_addr ,
   word_addr   ,
   wr          ,
   wr_data     ,
   wr_data_vld ,
   rd          ,
   rd_data     ,
   rd_data_vld ,
   done        ,
   scl         ,
   sda
);
//输入输出参数
parameter SYS_CLOCK  =  50_000_000;
parameter SCL_CLOCK  =  380_000;    //快速模式
parameter WADDR_W    =  2;
parameter DADDR_W    =  8;
parameter WR_ADDR_W  =  16;
parameter WDATA_W    =  8;
parameter RDATA_W    =  8;

//计数器参数
parameter SCL_N      =  SYS_CLOCK/SCL_CLOCK;
parameter SCL_W      =  8;

parameter HABIT_W    =  5;
parameter HABIT_N    =  18;

parameter BYTE_W     =  6;
parameter BYTEN_W    =  2;

//状态机参数
parameter IDLE       =  9'b000000001;  //空闲
parameter S1         =  9'b000000010;  //写开始
parameter S2         =  9'b000000100;  //写控制
parameter S3         =  9'b000001000;  //读开始
parameter S4         =  9'b000010000;  //读控制
parameter S5         =  9'b000100000;  //读数据
parameter S6         =  9'b001000000;  //写地址
parameter S7         =  9'b010000000;  //写数据
parameter S8         =  9'b100000000;  //结束
parameter STATE_W    =  9;

//中间变量参数
parameter SDA_DATA_N =  8;

input                      rst_n; 
input                      clk;      
input    [WR_ADDR_W-1:0]   word_addr;   
input                      wr;          
input    [WDATA_W-1:0]     wr_data;     
input                      rd;          
input    [WADDR_W-1:0]     waddr_num;
input    [DADDR_W-1:0]     device_addr;

output                     wr_data_vld; 
output   [RDATA_W-1:0]     rd_data;  
output                     rd_data_vld; 
output                     done;
output                     scl;
inout                      sda;

wire                       wr_data_vld; 
reg      [RDATA_W-1:0]     rd_data;  
wire                       rd_data_vld; 
reg                        done;
reg                        scl;
wire                       sda;

//计数器变量
reg      [SCL_W-1:0]       cnt_scl;
wire                       add_cnt_scl;
wire                       end_cnt_scl;
reg                        scl_vld;

reg      [HABIT_W-1:0]     cnt_hlbit;
wire                       add_cnt_hlbit;
wire                       end_cnt_hlbit;

reg      [BYTE_W-1:0]      cnt_bytes;
wire                       add_cnt_bytes;
wire                       end_cnt_bytes;
reg      [BYTEN_W-1:0]     bytes_num;

//中间变量
reg                        scl_high;
reg                        scl_low;
reg      [SDA_DATA_N-1:0]  sda_out_data;
reg                        wr_flag;
reg                        rd_flag;
reg                        ack_flag;
reg                        noack_flag;
reg                        ack_error;

wire                       sda_in;
reg                        sda_en;
reg                        sda_out;

//状态机变量
reg      [STATE_W-1:0]     state_c;
reg      [STATE_W-1:0]     state_n;
wire                       idle2s1_start;
wire                       s12s2_start;
wire                       s22s6_start;
wire                       s22idle_start;
wire                       s32s4_start;
wire                       s42s5_start;
wire                       s42idle_start;
wire                       s52s8_start;
wire                       s62s7_start;
wire                       s62s3_start;
wire                       s62idle_start;
wire                       s72s8_start;
wire                       s72idle_start;
wire                       s82idle_start;


// assign sda     = sda_en ? sda_out : 1'bz;
assign sda = (sda_en && !sda_out) ? 1'b0 : 1'bz;
assign sda_in  = sda;
//SDA接上拉电阻
pullup(sda);
//SCL时钟计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt_scl <= 0;
   else if(add_cnt_scl)begin
      if(end_cnt_scl)
         cnt_scl <= 0;
      else
         cnt_scl <= cnt_scl + 1'b1;
   end
end
assign add_cnt_scl = scl_vld;
assign end_cnt_scl = (add_cnt_scl && cnt_scl == SCL_N - 1) || ack_error;

//高低电平计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      cnt_hlbit <= 0;
   end
   else if(add_cnt_hlbit)begin
      if(end_cnt_hlbit)
         cnt_hlbit <= 0;
      else
         cnt_hlbit <= cnt_hlbit + 1'b1;
   end
end
assign add_cnt_hlbit = (state_c == S2 || state_c == S6 || state_c == S7 || state_c == S4 || state_c == S5) && (scl_low || scl_high);
assign end_cnt_hlbit = (add_cnt_hlbit && cnt_hlbit == HABIT_N - 1) || ack_error;

//字节数计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      cnt_bytes <= 0;
   end
   else if(add_cnt_bytes)begin
      if(end_cnt_bytes)
         cnt_bytes <= 0;
      else
         cnt_bytes <= cnt_bytes + 1'b1;
      end
end
assign add_cnt_bytes = end_cnt_hlbit && state_c == S6;
assign end_cnt_bytes = add_cnt_bytes && cnt_bytes == bytes_num - 1 ;

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      bytes_num <= 1;
   else if((wr || rd) && !(wr_flag ||rd_flag))
      bytes_num <= waddr_num;
end

//scl 时钟高电平中部标志位
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      scl_high <= 0;
   else if(cnt_scl == (SCL_N>>2)-1 && add_cnt_scl)
      scl_high <= 1;
   else
      scl_high <= 0;
end
//scl 时钟低电平中部标志位
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      scl_low <= 0;
   else if(cnt_scl == (SCL_N>>2) + (SCL_N>>1) -1 && add_cnt_scl)
      scl_low <= 1;
   else
      scl_low <= 0;
end



always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      scl_vld <= 0;
   else if(wr || rd)
      scl_vld <= 1;
   else if(done)
      scl_vld <= 0;
   else if(ack_error)
      scl_vld <= 0;
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      scl <= 1;
   else if(cnt_scl == (SCL_N>>1)-1 && add_cnt_scl && state_c != S8 && state_c != S1)
      scl <= 0;
   else if(state_c == S1)begin
      if(cnt_scl == (SCL_N>>1) -1 && add_cnt_scl && sda == 0)
         scl <= 0;
   end
   else if(end_cnt_scl)
      scl <= 1;
end


//状态机
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      state_c <= IDLE;
   else
      state_c <= state_n;
end

always @(*)begin
   case(state_c)
      IDLE:begin
         if(idle2s1_start)
            state_n = S1;
         else
            state_n = state_c;
      end
      S1:begin
         if(s12s2_start)
            state_n = S2;
         else
            state_n = state_c;
      end
      S2:begin
         if(s22idle_start)
            state_n = IDLE;
         else if(s22s6_start)
            state_n = S6;
         else
            state_n = state_c;
      end
      S3:begin
         if(s32s4_start)
            state_n = S4;
         else
            state_n = state_c;
      end
      S4:begin
         if(s42idle_start)
            state_n = IDLE;
         else if(s42s5_start)
            state_n = S5;
         else
            state_n = state_c;
      end
      S5:begin
         if(s52s8_start)
            state_n = S8;
         else
            state_n = state_c;
      end
      S6:begin
         if(s62idle_start)
            state_n = IDLE;
         else if(s62s7_start)
            state_n = S7;
         else if(s62s3_start)
            state_n = S3;
         else
            state_n = state_c;
      end
      S7:begin
         if(s72idle_start)
            state_n = IDLE;
         else if(s72s8_start)
            state_n = S8;
         else
            state_n = state_c;
      end
      S8:begin
         if(s82idle_start)
            state_n = IDLE;
         else
            state_n = state_c;
      end
      default:state_n = IDLE;
   endcase
end

assign idle2s1_start =  state_c == IDLE && (wr || rd);
assign s12s2_start   =  state_c == S1   && scl_low && !sda && !scl;
assign s22s6_start   =  state_c == S2   && ack_flag && scl_low;
assign s22idle_start =  state_c == S2   && !ack_flag && scl_low && !sda_en;
assign s32s4_start   =  state_c == S3   && scl_low && !sda && !scl;
assign s42s5_start   =  state_c == S4   && ack_flag && scl_low;
assign s42idle_start =  state_c == S4   && !ack_flag && scl_low && !sda_en;
assign s52s8_start   =  state_c == S5   && noack_flag && scl_low;
assign s62idle_start =  state_c == S6   && end_cnt_bytes && !ack_flag;
assign s62s7_start   =  state_c == S6   && end_cnt_bytes && ack_flag && wr_flag;
assign s62s3_start   =  state_c == S6   && end_cnt_bytes && ack_flag && rd_flag;
assign s72s8_start   =  state_c == S7   && ack_flag && scl_low;
assign s72idle_start =  state_c == S7   && !ack_flag && scl_low && !sda_en;
assign s82idle_start =  state_c == S8   && end_cnt_scl && sda && scl;

//SDA三态门使能
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      sda_en <= 0;
   else if(state_c == IDLE)begin
      if(wr || rd)
         sda_en <= 1;
      else
         sda_en <= 0;
   end
   else if(state_c == S2 || state_c == S6 || state_c == S7 || state_c == S4)begin
      if(cnt_hlbit == 16 - 1 && add_cnt_hlbit)
         sda_en <= 0;
      //读控制接收ACK后不必拉高,因为之后是读数据,使能要一直拉低
      else if(end_cnt_hlbit && state_c != S4)
         sda_en <= 1;
   end
   else if(state_c == S5)begin
      if(cnt_hlbit >= 0 && cnt_hlbit < 16 - 1)
         sda_en <= 0;
      else if(cnt_hlbit >= 17 - 1 && cnt_hlbit < 18)
         sda_en <= 1;
   end
end

//SDA输出
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      sda_out <= 1;
   end
   //写起始位
   else if(state_c == S1)begin
      if(end_cnt_scl && sda == 1)
         sda_out <= 0;
   end
   //写控制
   else if(state_c == S2)begin
      sda_out <= sda_out_data[7];
   end
   //读起始位
   else if(state_c == S3)begin
      if(!sda_out && scl_high)
         sda_out <= 1;
      else if(sda_out && scl_high)
         sda_out <= 0;
   end
   //读控制
   else if(state_c == S4)begin
      sda_out <= sda_out_data[7];
   end
   //发送NOACK
   else if(state_c == S5)begin
      if(cnt_hlbit == 16 - 1 && add_cnt_hlbit)
         sda_out <= 1;
      else if(cnt_hlbit == 18 - 1 && add_cnt_hlbit)
         sda_out <= 0;
   end
   //写地址
   else if(state_c == S6)begin
      sda_out <= sda_out_data[7];
   end
   //写数据
   else if(state_c == S7)begin
      sda_out <= sda_out_data[7];
   end
   else if(state_c == S8)begin
      if(cnt_scl == (SCL_N >> 1) - 1 && add_cnt_scl)
         sda_out <= 1;
   end
end

//SDA接收数据
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      rd_data <= 0;
   else if(state_c == S5)begin
      if(cnt_hlbit < 17 - 1 && cnt_hlbit >= 0 && scl_high)
         rd_data <= {rd_data[6:0],sda_in};
   end
end

//SDA输出数据
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      sda_out_data <= 0;
   else if(state_c == S1)
      sda_out_data <= device_addr;  //写控制
   else if(state_c == S2)begin
      if(scl_low && cnt_hlbit < 17 - 1 && cnt_hlbit >= 0)
         sda_out_data <= {sda_out_data[6:0],1'b0};
      else if(scl_low && end_cnt_hlbit)begin
         if(bytes_num == 1)
            sda_out_data <= word_addr[7:0];
         else
            sda_out_data <= word_addr[15:8];
      end
   end
   else if(state_c == S3)
      sda_out_data <= device_addr;  //读控制
   else if(state_c == S4)begin
      if(scl_low && cnt_hlbit < 17 - 1 && cnt_hlbit >= 0)
         sda_out_data <= {sda_out_data[6:0],1'b0};
   end
   else if(state_c == S6)begin
      if(scl_low && cnt_hlbit < 17 - 1 && cnt_hlbit >= 0)
         sda_out_data <= {sda_out_data[6:0],1'b0};
      else if(add_cnt_bytes && !end_cnt_bytes)
         sda_out_data <= word_addr[7:0];
      else if(end_cnt_bytes)
         sda_out_data  <= wr_data;
   end
   else if(state_c == S7)begin
      if(scl_low && cnt_hlbit < 17 - 1 && cnt_hlbit >= 0)
         sda_out_data <= {sda_out_data[6:0],1'b0};
   end
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      done <= 0;
   else if(state_c == S8)begin
      //提前一拍与SCL计数器结束条件对齐
      if(cnt_scl == SCL_N - 2 && add_cnt_scl && scl && sda)
         done <= 1;
      else
         done <= 0;
   end
end

//读有效
assign wr_data_vld = state_c == S2 || state_c == S6 || state_c == S7 || state_c == S4;
assign rd_data_vld = state_c == S5;

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      wr_flag <= 0;
   else if(wr && !(wr_flag || rd_flag))
      wr_flag <= 1;
   else if(done)
      wr_flag <= 0;
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      rd_flag <= 0;
   else if(rd && !(wr_flag || rd_flag))
      rd_flag <= 1;
   else if(done)
      rd_flag <= 0;
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      ack_flag <= 0;
   else if(scl_high && cnt_hlbit == 17 - 1 && add_cnt_hlbit && sda == 0)
      ack_flag <= 1;
   else if(scl_low && end_cnt_hlbit)
      ack_flag <= 0;
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      noack_flag <= 0;
   else if(state_c == S5)begin
      if(scl_high && cnt_hlbit == 17 - 1 && add_cnt_hlbit && sda == 1)
         noack_flag <= 1;
      else if(scl_low && end_cnt_hlbit)
         noack_flag <= 0;
   end
end


always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      ack_error <= 0;
   else if(s22idle_start || s62idle_start || s72idle_start || s42idle_start)
      ack_error <= 1;
   else
      ack_error <= 0;
end

endmodule

标签:SCCB,cnt,wire,scl,状态机,sda,IIC,parameter,reg
来源: https://www.cnblogs.com/cnlntr/p/14409498.html