FPGA SCCB
SCCB写操作与IIC写操作一致
SCCB读操作与IIC读操作区别
IIC读操作
SCCB读操作
与IIC代码相比增加了读中点停止状态,当写操作时,SCCB与IIC无异,读操作时在写地址状态后跳转到读中点停止状态,产生上升沿停止位,之后跳转到读开始状态(在此状态中产生起始位)。
module SCCB(
Clk ,
Rst_n ,
Wr ,
Wr_data ,
Wr_data_vaule ,
Wr_done ,
Word_adrr , //存储单元地址
Device_adress , //器件地址
Read ,
Rd_data ,
Rd_data_vaule ,
Rd_done ,
Sda ,
Sccb_clk
);
input Clk;
input Rst_n;
input Wr;
input Read;
input [7:0] Device_adress; //器件地址
input [7:0] Word_adrr; //存储单元地址
input [7:0]Wr_data;//写入的数据
output reg[7:0]Rd_data;//读出的数据
output Wr_data_vaule;//写数据有效标志信号
output reg Rd_data_vaule;//读数据有效标志信号
inout Sda;
output reg Wr_done;
output reg Rd_done;
output Sccb_clk;
parameter CLK_FREQUENCY = 50_000_000;
parameter Sccb_clk_FREQUENCY = 100_000; //sccb clk时钟频率100KHZ
localparam COUNT_MAX = CLK_FREQUENCY/Sccb_clk_FREQUENCY;
//独热码
localparam IDLE = 10'b0_000_000_001,
WR_START = 10'b0_000_000_010,
WR_CTRL = 10'b0_000_000_100,//写控制 写器件地址
WR_ADDRE = 10'b0_000_001_000,//写地址
RDMSTOP = 10'b0_000_010_000,
WR_DATA = 10'b0_000_100_000,//写数据
RD_START = 10'b0_001_000_000,//读开始
RD_CTRL = 10'b0_010_000_000,//读控制
RD_DATA = 10'b0_100_000_000,//读数据
STOP = 10'b1_000_000_000;
reg iic_free_n;//i2c空闲标志位,低电平有效
wire [7:0]waddress_data;//器件地址数据写
wire [7:0]raddress_data;//器件地址数据读
reg [15:0]iic_count; //产生i2C_CLK信号计数寄存器
reg clk_high;
reg clk_low;
reg [7:0]half_clk_cnt;//计数一个状态中高低电平计数器
reg [9:0]main_state;//主状态机状态寄存器
reg ack;//数据接收方对发送方的响应标志位
reg send_en;//控制Sda线输入输出开关
reg sda_reg;//sda信号线寄存器
reg task_en_n;//任务开始结束控制寄存器
reg [7:0]sda_data_out;//sda待输出的数据
reg [7:0]sda_data_in;//sda待写入的数据
wire be_rd_data_vaule;//读数据有效前寄存器
reg w_flag;
reg r_flag;
reg clk_r;
//写/读器件地址8位
assign waddress_data = {Device_adress[7:1],1'b0 };
assign raddress_data = {Device_adress[7:1],1'b1 };
assign Sccb_clk = ((main_state == RD_START) || (main_state == RDMSTOP)) ? 1'b1 : clk_r;
assign Sda = send_en?sda_reg:1'bz;
//iic_free_n状态判断
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
iic_free_n <= 1'b0;
else if(Wr_done || Rd_done)
iic_free_n <= 1'b0;
else if(Wr|Read)
iic_free_n <= 1'b1;
else
iic_free_n <= iic_free_n;
//iic_count计数
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
iic_count <= 1'b0;
else if(iic_free_n)
begin
if(iic_count == COUNT_MAX - 1'b1)
iic_count <= 16'd0;
else
iic_count <= iic_count + 1'b1;
end
else
iic_count <= 1'b0;
//Sccb_clk产生
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
clk_r <= 1'b1;
else if(iic_count == COUNT_MAX >> 1)
clk_r <= 1'b0;
else if(iic_count == 1'b0)
clk_r <= 1'b1;
else
clk_r <= clk_r;
//标志Sccb_clk高电平 clk_high产生
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
clk_high <= 1'b0;
else if(iic_count == (COUNT_MAX >> 2) )
clk_high <= 1'b1;
else
clk_high <= 1'b0;
//标志Sccb_clk低电平 clk_low产生
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
clk_low <= 1'b0;
else if(iic_count == (COUNT_MAX >> 2 ) + (COUNT_MAX >> 1) )
clk_low <= 1'b1;
else
clk_low <= 1'b0;
//sda串行收发Sccb_clk高低电平计数器
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
half_clk_cnt <= 1'b0;
else
if((main_state == WR_CTRL) || (main_state ==WR_ADDRE)|| (main_state ==WR_DATA) || (main_state ==RD_CTRL) ||(main_state ==RD_DATA))
begin
if(clk_high | clk_low)
begin
if(half_clk_cnt == 8'd17)
half_clk_cnt <= 8'b0;
else
half_clk_cnt <= half_clk_cnt + 1'b1;
end
else
half_clk_cnt <= half_clk_cnt;
end
else
half_clk_cnt <= 1'b0;
//数据接收方对发送方的响应标志位ACK
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
ack <= 1'b0;
else
if((half_clk_cnt == 8'd16) && clk_high && (Sda == 1'b0))
ack <= 1'b1;
else
if((half_clk_cnt == 8'd17 )&& clk_low)
ack <= 1'b0;
else
ack <= ack;
//输出串行数据任务
task send_eight_data;
if(clk_high && (half_clk_cnt == 8'd16))
task_en_n <= 1'b1;
else
if(half_clk_cnt < 8'd17)
begin
sda_reg <= sda_data_out[7];
if(clk_low)
sda_data_out <= {sda_data_out[6:0],1'b0};
else
sda_data_out <= sda_data_out;
end
endtask
//输入串行任务
task get_eight_data;
if(clk_low && (half_clk_cnt == 8'd15))
task_en_n <= 1'b1;
else
if(half_clk_cnt < 8'd15)
begin
if(clk_high)
sda_data_in <= {sda_data_in[6:0],Sda};
else
sda_data_in <= sda_data_in;
end
endtask
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
begin
sda_reg <= 1'b1;
Wr_done <= 1'b0;
w_flag <= 1'b0;
r_flag <= 1'b0;
Rd_done <= 1'b0;
end
else
begin
case(main_state)
IDLE :
begin
sda_reg <= 1'b1;
w_flag <= 1'b0;
r_flag <= 1'b0;
Wr_done <= 1'b0;
Rd_done <= 1'b0;
if(Wr)
begin
main_state <= WR_START;
w_flag <= 1'b1;
end
else if(Read)
begin
main_state <= WR_START;
r_flag <= 1'b1;
end
else
main_state <= IDLE ;
end
WR_START:
begin
if(clk_low)
begin
main_state <= WR_CTRL;
sda_data_out <= waddress_data;
task_en_n <= 1'b0;
end
else if(clk_high)
begin
sda_reg <= 1'b0;
main_state <= WR_START;
end
else
main_state <= WR_START;
end
WR_CTRL:
begin
if(task_en_n == 1'b0)
send_eight_data;
else
if(ack == 1'b1)
begin
if(clk_low)
begin
main_state <= WR_ADDRE;
task_en_n <= 1'b0;
sda_data_out <= Word_adrr;
end
else
main_state <= WR_CTRL;
end
else
main_state <= IDLE;
end
WR_ADDRE:
begin
if(task_en_n == 1'b0)
send_eight_data;
else
begin
if(ack == 1'b1)
begin
if(clk_low && w_flag)
begin
main_state <= WR_DATA;
task_en_n <= 1'b0;
sda_data_out <= Wr_data;
end
else
if(clk_low && r_flag)
begin
main_state <= RDMSTOP;
sda_reg <= 1'b0; //产生上升沿停止位
end
else
main_state <= WR_ADDRE;
end
else
main_state <= IDLE;
end
end
RDMSTOP:
begin
if(clk_low)
main_state <= RD_START ;
else
if(clk_high)
begin
sda_reg <= 1'b1; //产生上升沿
main_state <= RDMSTOP ;
end
else
main_state <= RDMSTOP ;
end
WR_DATA: //10_000
begin
if(task_en_n == 1'b0)
send_eight_data;
else
begin
if(ack == 1'b1)
begin
if(clk_low)
begin
main_state <= STOP;
sda_reg <= 1'b0; //上升沿停止
end
else
main_state <= WR_DATA;
end
else
main_state <= IDLE;
end
end
RD_START:
begin
if(clk_low)
begin
main_state <= RD_CTRL;
task_en_n <= 1'b0;
sda_data_out <= raddress_data;
end
else
if(clk_high)
begin
main_state <= RD_START;
sda_reg <= 1'b0;//产生下降沿起始位
end
else
main_state <= RD_START;
end
RD_CTRL:
begin
if(task_en_n == 1'b0)
send_eight_data;
else
begin
if(ack == 1'b1)
begin
if(clk_low)
begin
main_state <= RD_DATA;
task_en_n <= 1'b0;
end
else
main_state <= RD_CTRL;
end
else
main_state <= IDLE;
end
end
RD_DATA:
begin
if(task_en_n == 1'b0)
get_eight_data;
else
begin
sda_reg <= 1'b1; //产生NOack信号表示不读
if(clk_low)
begin
main_state <= STOP;
sda_reg <= 1'b0; //产生上升沿停止位
end
else
main_state <= RD_DATA;
end
end
STOP:
begin
if(clk_high)
begin
sda_reg <= 1'b1; //产生上升沿停止态
main_state <= IDLE;
if(w_flag)
Wr_done <= 1'b1;
else
if(r_flag)
Rd_done <= 1'b1;
end
else
main_state <= STOP;
end
default : main_state <= IDLE;
endcase
end
//控制sda开关闭合关闭
always @(*) //描述组合逻辑电路
begin
case(main_state)
IDLE:
send_en = 1'b0;
RD_START,WR_START,RDMSTOP,STOP: //写/读开始 停止位
send_en = 1'b1;
RD_CTRL,WR_CTRL,WR_ADDRE,WR_DATA:
begin
if(half_clk_cnt < 8'd16)
send_en = 1'b1;
else
send_en = 1'b0;
end
RD_DATA:
begin
if(half_clk_cnt < 8'd16)
send_en = 1'b0;
else
send_en = 1'b1;
end
default:send_en = 1'b0;
endcase
end
//写输入有效标志位
assign Wr_data_vaule = ((main_state == WR_ADDRE) && (w_flag && clk_low) && (ack == 1'b1));
//读数据有效标志位前寄存器
assign be_rd_data_vaule = ((main_state == RD_DATA) && (half_clk_cnt == 8'd15) && clk_low);
//读数据有效标志位
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Rd_data_vaule <= 1'b0;
else if(be_rd_data_vaule)
Rd_data_vaule <= 1'b1;
else
Rd_data_vaule <= 1'b0;gai
//读出的有效数据
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Rd_data <= 8'd0;
else if(be_rd_data_vaule)
Rd_data <= sda_data_in;
else
Rd_data <= Rd_data;
endmodule
此模块在后续的系統中将会用到,所以在此不做仿真验证。