利用SPI驱动12864液晶

SPI 发送模块
在这里,我们要在主机上建立,一个向从机写入数据的SPI 发送模块,首先我们先从C 语言上了解几个主机在SPI 写操作上容易被疏忽的小细节: 我们知道SPI 设备在传输都有一个规则,SCL 时钟信号在“上升沿”的时候是“锁存数 据”,SCL 时钟信号在“下降沿”是“设置数据”。在这里我们SPI 主机(FPGA),写操 作要干的工作就是在“拉高SCL 时钟信号之前”设置数据(移位数据),设置数据之后, 再拉高时钟信号。但是我们常常会忽略了一些具体的细节。
利用SPI驱动12864液晶
上面有两个主机的SPI_Send 函数(写函数),左边的写法是最常用,但是也是最容易忽 略小细节。相比右边的写法比较谨慎,在最低的程度上符合一写小细节。
利用SPI驱动12864液晶
再来,我们继续引用ST7565P 芯片的写入时序图,分析并且区分上边的两个写法。关于 SCL 信号在空闲的时候总是处于高电平。当主机开始向从机写入数据,主机会先拉低 CS 信号,再拉低SCL 信号,然后“设置”数据,亦即主机(FPGA)更新SI 的数据(主 机数据移位操作),最后再拉高SCL 信号。同一时间,从机会因为SCL 的上升沿变化, 从机(液晶资源)“锁存”(从机读取数据操作)SI 上的数据。 很明显左边的写法没有符合这些细节,然而右边的写法却符合这些细节。无论是左边的 写法还是右边的写法,都忽略了一个致命的细节,两种写法都无法确定SPI 时钟信号的时钟频率。
module spi_write_module
(
CLK,RSTn,Start_Sig,SPI_Data,Done_Sig,SPI_Out
);
input CLK;
input RSTn;
input Start_Sig;
output [9:0] SPI_Data;
output Done_Sig;
output [3:0]SPI_Out;//[3]CS,[2]A0 [1]CLK [0]D0
parameter T0P5US=4'd9;//0.5us
always@(posedge CLK or negedge RSTn)
if(!RSTn)
Count1<=4'd0;
else if(Count1==T0P5US)
Count1<4'd0;
else if(Start_Sig)
Count1<=Count1+1'b1;
else
Count1<=4'd0;
reg[4:0]i;
reg rCLK;
reg rDO;
reg isDone;
always@(posedge CLK or negedge RSTn)
if(!RSTn)
begin
i<=5'd0;
rCLK<=1'b1;
rDO<=1'b0;
isDone<=1'b0;
end
else if(Start_Sig)
case(i)
5'd0,5'd2,5'd4,5'd6,5'd8,5'd10,5'd12,5'd14:
if(Count1==T0P5US)begin rCLK<=1'b0;rDO<=SPI_Data[7-(i>>1)];i<=1+1'b1;end
5'd1,5'd3,5'd5,5'd7,5'd9,5'd11,5'd13,5'd15:
if(Count==T0P5US)begin rCLK<=1'b1;i<=i+1'b1;end
5'd16:
begin isDone<=1'b1;i<=i+1'b1;end
5'd17:
begin isDone<=1'b0;i<=5'd0;end
endcase
assign Done_Sig=isDone;
assign SPI_Out={SPI_Data[9],SPI_Data[8],rCLK,rDO};
endmodule
SCL 的时钟频率定义为1Mhz , 也就是说周期时间是1us ,半周期就是0.5us 。如果以 20Mhz 来定时,那么计数的结果是10。在19 行定义了0.5us 的常量,第23~33 行是0.5us 的定时器。但是比较不同的是,这个定时器平时不工作,当Start_Sig 拉高的时候才开 始计数(第30 行)。 第37~65 行是spi_write_module.v 的核心功能。i 寄存器表示操作步骤,rCLK 寄存器表 示SCL 然而rDO 寄存器表示SI 。如同前面所述那样,SCL 时钟信号,处于空闲状态
时是出于高电平,所以rCLK 复位值是逻辑1(46 行)。
SPI_Data : 第9 位表示CS,第8 位表示A0,第7 .. 0 位表示一字节数据。
SPI_Out : 第3 位表示CS,第2 位表示A0,第1 位表示SCL,第0 位表示SI。
当Start_Sig 拉高的同时,定时器开始计数(30 行),该模块也开始执行(50 行)。
当第一个0.5us 定时产生的时候(54 行),也就是第一个时钟的前半周期,亦即下降沿,
rCLK 设置为逻辑0。根据SPI 传输的规则,下降沿的时候主机设置数据,rDO 赋予 SPI_Data 信号的第7 位(SPI 传输是从最高位开始,最低位结束),最后i 递增以示下 一个步骤。 当i 等于1 的时候并且定时产生(56 行),这表示第一个时钟的后半周期,亦即上升沿, rCLK 设置为逻辑1。在SPI 传输的规则中上升沿的时候,从机锁存数据。然后i 递增以 示下一个步骤。
上述的步骤会一直重复到第八次,直到一字节的数据发送完毕。最后会产生一个完成信 号(59~63 行)。
这里有一个表达式需要说明一下:
i >> 1 : 表示i / 2。右移操作也代表了“i / j2”(j 是右移次数)。
假设8 >> 2 ,亦即8 / 22 等于2。8 >> 3, 亦即8 / 23 等于1。
最后还有一个重点就是SPI_Out 的驱动(70 行)。在上面我已经重复过SPI_Out 是占 4 位的输出。而且每一个位都有意义。
SPI_Out 第3 位:表示了CS,所以直接由SPI_Data 的第9 位驱动。
SPI_Out 第2 位:表示了A0,同样也是直接由SPI_Data 的第8 位驱动。
SPI_Out 第1 位:表示了SCL,以寄存器rCLK 来驱动。
SPI_Out 第0 位:表示了SI,以寄存器rDO 来驱动。
这样的目的是简化连线的复杂度。我们知道Verilog HDL 语言的位操作是很强大。懂得 善用,会对建模提到很大的帮助。
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值