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