以下代码I2C速度取为400K,所用开发板为24M主频,故需要分频。所选OLED为4线制,I2C协议,0.96吋。由于其功耗较低,完全可以用FPGA的IO脚供电,故在顶层文件里设置Vcc和Gnd,作为OLED的供电引脚。
以下代码在第一排显示21个字符A,第二排按照1Hz的频率循环0-9计数,同时完成ACK功能。
//顶层文件
//OLED I2C驱动,测试成功,第一排显示21个字符A,第二排按照1Hz的频率0-9计数,ACK测试成功
module I2C_top(
input CLK_24M, //24MHz
input CLK_1K, //低频更新数据,此处取1KHz
input CLK_1, //1Hz时钟
input RST,
output SCL,
inout SDA,
output READY,
output ACK1,
output ACK2,
output ACK3,
output Vcc,
output Gnd
);
parameter CMD = 8'h00; //命令
parameter DAT = 8'h40; //数据
reg [4:0]cnt; //计数器,用来分频
reg [23:0]Data;
reg [7:0]reg_data;
reg EN;
reg CLK_400K;
reg [3:0] state; //状态计数器
reg [7:0] count; //用于数据处理的计数器
reg [4:0] m; //m:0~8
reg [8:0] n; //n:0~128
reg [7:0] x; //x坐标
reg [5:0] number; //显示数字索引
reg [3:0] num; //显示数字
reg [7:0] number_data; //数字字库
I2C_COM u1(.CLK_400K(CLK_400K),
.RST(RST),
.Data(Data),
.EN(EN),
.RDY(READY),
.ack1(ACK1),
.ack2(ACK2),
.ack3(ACK3),
.SCL(SCL),
.SDA(SDA));
assign Vcc=1'b1;
assign Gnd=1'b0;
always@(posedge CLK_1) //1Hz计数器,用于显示
if(num<9)
num<=num+1'b1;
else num<=0;
//产生I2C控制时钟400KHz
always@(posedge CLK_24M or negedge RST)
begin
if(!RST) begin
CLK_400K<=0;
cnt<=0;
end
else if(cnt<29) //分频 24000/400/2-1=30-1=29
cnt<=cnt+1'b1;
else begin
CLK_400K<=!CLK_400K;
cnt<=0;
end
end
//I2C寄存器配置
always@(posedge CLK_1K or negedge RST)
begin
if(!RST) begin //
count<=0;
EN<=0;
state<=0;
m<=0;
n<=0;
x<=0;
end
else if (count<100) count<=count+1; //延时
else if (count<129) //初始化
case (state)
0:if (READY) begin Data<={8'h78,CMD,reg_data};EN<=1;state<=1; end //OLED地址:0x78
1:if (READY) begin EN<=0; state<=2; end
2:begin count<=count+1; state<=0; end
default: state<=0;
endcase
else if (count==129) //清屏操作
case(state)
0:if (m<8) begin
Data<={8'h78,CMD,8'hB0+m};//
EN<=1;
state<=1;
end
else begin
EN<=0;
state<=0;
count<=count+1;
end
1:if(READY) begin //I2C发送结束
EN<=0;
state<=2;
end
2:begin
Data<={8'h78,CMD,8'h00};//
EN<=1;
state<=3;
end
3:if(READY) begin //I2C发送结束
EN<=0;
state<=4;
end
4:begin
Data<={8'h78,CMD,8'h10};//
EN<=1;
state<=5;
end
5:if(READY) begin //I2C发送结束
EN<=0;
state<=6;
end
6:if (n<128) begin
Data<={8'h78,DAT,8'h00};//I2C DeviceAddress:0x78
EN<=1;
state<=7;
end
else begin
n<=0;
m<=m+1;
state<=0;
end
7:if (READY) begin //I2C发送结束
EN<=0;
n<=n+1;
state<=6;
end
default:state<=0;
endcase
else if (count==130) case (state) //y位置
0:if (READY) begin Data<={8'h78,CMD,reg_data};EN<=1;state<=1; end //OLED地址:0x78
1:if (READY) begin EN<=0; state<=2; end
2:begin count<=count+1; state<=0; end
default: state<=0;
endcase
else if (count==131) case (state) //x位置
0:if (READY) begin Data<={8'h78,CMD,reg_data+x};EN<=1;state<=1; end //OLED地址:0x78
1:if (READY) begin EN<=0; state<=2; end
2:begin count<=count+1; state<=0; end
default: state<=0;
endcase
else if (count==132)
case (state) //写位置命令
0:if (READY) begin Data<={8'h78,CMD,reg_data};EN<=1;state<=1; end //OLED地址:0x78
1:if (READY) begin EN<=0; state<=2; end
2:begin count<=count+1; state<=0; end
default: state<=0;
endcase
else if (count<139) case (state)//写字母A
0:if (READY) begin Data<={8'h78,DAT,reg_data};EN<=1;state<=1; end //OLED地址:0x78
1:if (READY) begin EN<=0;state<=2; end
2:begin count<=count+1; state<=0; end
default: state<=0;
endcase
else if (count==139) //第一排显示21个A
if (x<120) begin //x范围是0-127,此处x起始坐标取为1。第21个字符开始于121。
count<=133 ;//重复写
x<=x+6; //字库是6*8,所以每次+6
end
else
count<=140;
else if (count==140) case (state) //y位置
0:if (READY) begin Data<={8'h78,CMD,reg_data};EN<=1;state<=1; end //OLED地址:0x78
1:if (READY) begin EN<=0; state<=2; end
2:begin count<=count+1; state<=0; end
default: state<=0;
endcase
else if (count==141) case (state) //x位置
0:if (READY) begin Data<={8'h78,CMD,reg_data};EN<=1;state<=1; end //OLED地址:0x78
1:if (READY) begin EN<=0; state<=2; end
2:begin count<=count+1; state<=0; end
default: state<=0;
endcase
else if (count==142)
case (state) //写位置命令
0:if (READY) begin Data<={8'h78,CMD,reg_data};EN<=1;state<=1; end //OLED地址:0x78
1:if (READY) begin EN<=0; state<=2; end
2:begin count<=count+1; state<=0; end
default: state<=0;
endcase
else //显示可变数字0-9
case (state)
0:begin number<=num*6;state<=1; end
1:if (READY) begin Data<={8'h78,DAT,number_data};EN<=1;state<=2; end //OLED地址:0x78
2:if (READY) begin EN<=0;state<=3; number<=number+1; end
3:begin Data<={8'h78,DAT,number_data};EN<=1;state<=4; end
4:if (READY) begin EN<=0;state<=5; number<=number+1;end
5:begin Data<={8'h78,DAT,number_data};EN<=1;state<=6; end
6:if (READY) begin EN<=0;state<=7; number<=number+1; end
7:begin Data<={8'h78,DAT,number_data};EN<=1;state<=8; end
8:if (READY) begin EN<=0;state<=9; number<=number+1; end
9:begin Data<={8'h78,DAT,number_data};EN<=1;state<=10; end
10:if (READY) begin EN<=0;state<=11;number<=number+1; end
11:begin Data<={8'h78,DAT,number_data};EN<=1;state<=12; end
12:if (READY) begin EN<=0;state<=0; count<=140; end
default: state<=0;
endcase
end
//I2C需要配置的寄存器值
always@(count)
case(count-100)
0:reg_data<=8'hAE;// 100~128条为初始化命令
1:reg_data<=8'h00;//
2:reg_data<=8'h10;//
3:reg_data<=8'h40;//
4:reg_data<=8'hB0;//
5:reg_data<=8'h81;//
6:reg_data<=8'hFF;//
7:reg_data<=8'hA1;//
8:reg_data<=8'hC8;//
9:reg_data<=8'hA6;//
10:reg_data<=8'hA8;//
11:reg_data<=8'h3F;//
12:reg_data<=8'hD3;//
13:reg_data<=8'h00;//
14:reg_data<=8'hD5;//
15:reg_data<=8'h80;//
16:reg_data<=8'hD9;//
17:reg_data<=8'hF1;//
18:reg_data<=8'hDA;//
19:reg_data<=8'h12;//
20:reg_data<=8'hDB;//
21:reg_data<=8'h40;//
22:reg_data<=8'h20;//
23:reg_data<=8'h02;//
24:reg_data<=8'h8D;//
25:reg_data<=8'h14;//
26:reg_data<=8'hA4;//
27:reg_data<=8'hA6;//
28:reg_data<=8'hAF;//
30:reg_data<=8'hB0; //字符位置原点(y)
31:reg_data<=8'h01; //字符位置01点(x)
32:reg_data<=8'h10; //写命令
33:reg_data<=8'h00; //写A数据(字库)
34:reg_data<=8'h7C;
35:reg_data<=8'h12;
36:reg_data<=8'h11;
37:reg_data<=8'h12;
38:reg_data<=8'h7C;
40:reg_data<=8'hB1; //字符位置(y) 第二排
41:reg_data<=8'h0D; //字符位置(x)0x3D x&0x0f
42:reg_data<=8'h13; //写命令 ((x&0xf0)>>4)|0x10
default:reg_data<=8'hzz;
endcase
//字库译码
always@(number)
case(number)
0:number_data<=8'h00; //写0数据(字库)
1:number_data<=8'h3E;
2:number_data<=8'h51;
3:number_data<=8'h49;
4:number_data<=8'h45;
5:number_data<=8'h3E;
6:number_data<=8'h00; //写1数据(字库)
7:number_data<=8'h00;
8:number_data<=8'h42;
9:number_data<=8'h7F;
10:number_data<=8'h40;
11:number_data<=8'h00;
12:number_data<=8'h00; //写2数据(字库)
13:number_data<=8'h42;
14:number_data<=8'h61;
15:number_data<=8'h51;
16:number_data<=8'h49;
17:number_data<=8'h46;
18:number_data<=8'h00; //写3数据(字库)
19:number_data<=8'h21;
20:number_data<=8'h41;
21:number_data<=8'h45;
22:number_data<=8'h4B;
23:number_data<=8'h31;
24:number_data<=8'h00; //写4数据(字库)
25:number_data<=8'h18;
26:number_data<=8'h14;
27:number_data<=8'h12;
28:number_data<=8'h7F;
29:number_data<=8'h10;
30:number_data<=8'h00; //写5数据(字库)
31:number_data<=8'h27;
32:number_data<=8'h45;
33:number_data<=8'h45;
34:number_data<=8'h45;
35:number_data<=8'h39;
36:number_data<=8'h00; //写6数据(字库)
37:number_data<=8'h3C;
38:number_data<=8'h4A;
39:number_data<=8'h49;
40:number_data<=8'h49;
41:number_data<=8'h30;
42:number_data<=8'h00; //写7数据(字库)
43:number_data<=8'h01;
44:number_data<=8'h71;
45:number_data<=8'h09;
46:number_data<=8'h05;
47:number_data<=8'h03;
48:number_data<=8'h00; //写8数据(字库)
49:number_data<=8'h36;
50:number_data<=8'h49;
51:number_data<=8'h49;
52:number_data<=8'h49;
53:number_data<=8'h36;
54:number_data<=8'h00; //写9数据(字库)
55:number_data<=8'h06;
56:number_data<=8'h49;
57:number_data<=8'h49;
58:number_data<=8'h29;
59:number_data<=8'h1E;
default:reg_data<=8'hzz;
endcase
endmodule
//SCL,SDA数据传输时序代码(I2C写控制代码)
`timescale 1ns / 1ps
module I2C_COM(CLK_400K, //I2C控制接口传输所需时钟
RST, //重置,0有效
Data, //SDA接口传输的24位数据
EN, //使能,1有效
RDY, //传输准备标志,1有效
ack1, //ACK应答
ack2, //ACK应答
ack3, //ACK应答
SCL, //FPGA与I2C时钟接口
SDA); //FPGA与I2C数据接口
input [23:0]Data;
input RST;
input CLK_400K;
input EN;
output RDY;
output SCL;
inout SDA;
output ack1,ack2,ack3;
reg [6:0] count; //I2C时间片计数器
reg sclk;
reg ack1,ack2,ack3;
reg RDY;
reg SCL;
reg isOut; //SDA输出标示
wire SDA;
reg sdia; //寄存器,用于进行数据读写
assign SDA = isOut ? sdia : 1'bz; //SDA数据输出选择,1输出,0高阻(接收从机响应或数据)
always@(posedge CLK_400K or negedge RST)
begin
if(!RST) //初始状态,SCK,SDA全部为高
begin
RDY<=1;
SCL<=1;
sdia<=1;
ack1<=0;
ack2<=0;
ack3<=0;
isOut<=1;
end
else if (!EN) begin count<=0; ack1<=0;ack2<=0;ack3<=0;RDY<=1;isOut<=1; end
else case(count)
0,1:begin RDY<=0;SCL<=1;sdia<=1;
ack1<=0;
ack2<=0;
ack3<=0;
isOut<=1;
count<=count+1;
end //2个时钟,确保释放总线
2:begin sdia<=0;count<=count+1; end //起始
3:begin SCL<=0;count<=count+1; end //
4:begin sdia<=Data[23]; count<=count+1; end //SCL=0采集数据
5:begin SCL<=1; count<=count+1; end //SCL上升沿传数据
6:begin SCL<=0;count<=count+1; end
7:begin sdia<=Data[22];count<=count+1; end
8:begin SCL<=1;count<=count+1; end
9:begin SCL<=0;count<=count+1; end
10:begin sdia<=Data[21];count<=count+1; end
11:begin SCL<=1;count<=count+1; end
12:begin SCL<=0;count<=count+1; end
13:begin sdia<=Data[20];count<=count+1; end
14:begin SCL<=1;count<=count+1; end
15:begin SCL<=0;count<=count+1; end
16:begin sdia<=Data[19];count<=count+1; end
17:begin SCL<=1;count<=count+1; end
18:begin SCL<=0;count<=count+1; end
19:begin sdia<=Data[18];count<=count+1; end
20:begin SCL<=1;count<=count+1; end
21:begin SCL<=0;count<=count+1; end
22:begin sdia<=Data[17];count<=count+1; end
23:begin SCL<=1;count<=count+1; end
24:begin SCL<=0;count<=count+1; end
25:begin sdia<=Data[16];count<=count+1; end
26:begin SCL<=1; count<=count+1;end //SCL期间不允许改变数据
27:begin SCL<=0; count<=count+1;isOut<=0;end //高阻
28:begin SCL<=1;count<=count+1; end
29:begin ack1<=!SDA;count<=count+1; end
30:begin SCL<=0; count<=count+1; isOut<=1;end
31:begin sdia<=Data[15]; count<=count+1; end
32:begin SCL<=1;count<=count+1; end //SCL上升沿传数据
33:begin SCL<=0;count<=count+1; end
34:begin sdia<=Data[14];count<=count+1; end
35:begin SCL<=1;count<=count+1; end
36:begin SCL<=0;count<=count+1; end
37:begin sdia<=Data[13];count<=count+1; end
38:begin SCL<=1;count<=count+1; end
39:begin SCL<=0;count<=count+1; end
40:begin sdia<=Data[12];count<=count+1; end
41:begin SCL<=1;count<=count+1; end
42:begin SCL<=0;count<=count+1; end
43:begin sdia<=Data[11];count<=count+1; end
44:begin SCL<=1;count<=count+1; end
45:begin SCL<=0;count<=count+1; end
46:begin sdia<=Data[10];count<=count+1; end
47:begin SCL<=1;count<=count+1; end
48:begin SCL<=0;count<=count+1; end
49:begin sdia<=Data[9];count<=count+1; end
50:begin SCL<=1;count<=count+1; end
51:begin SCL<=0;count<=count+1; end
52:begin sdia<=Data[8];count<=count+1; end
53:begin SCL<=1;count<=count+1; end
54:begin SCL<=0;count<=count+1;isOut<=0; end //高阻
55:begin SCL<=1;count<=count+1; end
56:begin ack2<=!SDA;count<=count+1; end //读ack
57:begin SCL<=0;count<=count+1;isOut<=1; end
58:begin sdia<=Data[7];count<=count+1; end
59:begin SCL<=1; count<=count+1; end //SCL上升沿传数据
60:begin SCL<=0;count<=count+1; end
61:begin sdia<=Data[6];count<=count+1; end
62:begin SCL<=1;count<=count+1; end
63:begin SCL<=0;count<=count+1; end
64:begin sdia<=Data[5];count<=count+1; end
65:begin SCL<=1;count<=count+1; end
66:begin SCL<=0;count<=count+1; end
67:begin sdia<=Data[4];count<=count+1; end
68:begin SCL<=1;count<=count+1; end
69:begin SCL<=0;count<=count+1; end
70:begin sdia<=Data[3];count<=count+1; end
71:begin SCL<=1;count<=count+1; end
72:begin SCL<=0;count<=count+1; end
73:begin sdia<=Data[2];count<=count+1; end
74:begin SCL<=1;count<=count+1; end
75:begin SCL<=0;count<=count+1; end
76:begin sdia<=Data[1];count<=count+1; end
77:begin SCL<=1;count<=count+1; end
78:begin SCL<=0;count<=count+1; end
79:begin sdia<=Data[0];count<=count+1; end
80:begin SCL<=1;count<=count+1; end
81:begin SCL<=0;count<=count+1;isOut<=0; end //高阻
82:begin SCL<=1;count<=count+1; end
83:begin ack3<=!SDA; count<=count+1; end //读ack
84:begin SCL<=0; count<=count+1;isOut<=1; end
85:begin sdia<=0;count<=count+1; end
86:begin SCL<=1;count<=count+1; end
87:begin sdia<=1; count<=count+1; end //停止,释放总线
88:begin RDY<=1; end
default:begin RDY<=0;SCL<=1;sdia<=1; end //释放总线,待机
endcase
end
endmodule