|
CycloneII 驱动移位寄存器74HC595 调试手记
----------祭奠为我所犯错误而牺牲的时间
一直在忙FPGA数据采集的设计。由于FPGA引脚十分紧张,所以想到用移位寄存器(串入并出)级联来节省引脚,之前从来没有用过74HC595,但是看了下片子感觉好简单啊(我的心态已近决定我要倒霉),还好我有个习惯就是不喜欢到网上找源码,因为够自信,我喜欢直接看datasheet,然后自己写代码。
74HC595的结构如下:
(原文件名:74HC595 strcture.JPG)
我一看,那么简单,一共两级,si是串行数据输入,SCK是移位时钟(上升沿寄存),SCLR是移位寄存器清零,RCK是输出寄存时钟(上升沿),G输出时能(低电平使能)。
靠,我想这个有什么啊!简单!在洞洞板上 一来二去把线接好了。
接线如下:
VCC -------------------- 5V (这里又埋下错误的伏笔)
SCLR -------------------- VCC
G ------------------- GND
SI,SCK,RCK ------------------- CycloneII
Qa ~ QH ------------------ 8个LED(共阴)看效果(这里又搞错了)下边在讲。
我是一次直接更新8位所以就不同清零了,所以直接把SCLR接VCC。
G接GND,输出一直使能。
然后我想了下Verilog程序结构---------简单啊! 典型的控制器加数据通路设计
控制器就是一个状态机:
(原文件名:Serial_74hc595 FSM.JPG)
数据通路:
(原文件名:data path.JPG)
设计思路如下:
因为驱动74hc595的模块是大工程的一部分,驱动74hc595的模块本接受顶层模块的控制,工作流程如下,调用模块先把待发送的数据打到总线上(data),然后置Load = 1,此时8位数据被serial_tansfer_74hc595寄存到data_byte(移位寄存器) 内,然后调用模块置start = 1,serial_transfer_74hc595开始发送,发送的过程如下是:通过控制移位寄存器data_byte发送数据,同时bit_count 计数,当8位数据全部发完,serial_transfer_74hc595置transfer_over = 1, 此时调用模块发现transfer_over = 1 ,传送完毕,完美啊!!!(真自恋)
如意算盘完成后开始堆码……….
三下五除二把发送模块写好了:
//Module :
//Desc : Transfer datas to shift 74hc595;
module serial_transfer_74hc595(sck, rck, si, sclr_n,
data, load, start, transfer_over,
clk, reset_n);
output sck, rck, si, sclr_n, transfer_over;
input load, start, clk, reset_n;
input[7 : 0] data;
reg rck;
reg transfer_over, shift;
reg[7 : 0] data_byte;
reg[2 : 0] bit_count;
reg[3 : 0] state, next_state;
parameter s_idle = 0;
parameter s_wait_for_start = 1;
parameter s_sending = 2;
parameter s_over = 3;
assign si = data_byte[7];
assign sclr_n = 1;
assign sck = clk;
always@(posedge clk or negedge reset_n) begin
if(!reset_n)
state <= s_idle;
else
state <= next_state;
end
always@(state or load or start or bit_count) begin
next_state = 0;
shift = 0;
transfer_over = 0;
next_state = state;
case(state)
s_idle :
if(load)
next_state = s_wait_for_start;
s_wait_for_start :
if(start) begin
next_state = s_sending;
shift = 1;
end
s_sending :
if(bit_count == 3'b111) begin
next_state = s_over;
end
else
shift = 1;
s_over :
begin
next_state = s_idle;
transfer_over = 1;
end
default :
next_state = s_idle;
endcase
end
always@(posedge clk or negedge reset_n) begin
if(!reset_n) begin
bit_count <= 0;
data_byte <= 0;
end
else begin
if(load) begin
bit_count <= 0;
data_byte <= data;
end
if(shift) begin
bit_count <= bit_count + 1;
data_byte <= data_byte << 1;
end
end
end
always@(state or clk) begin
if(state == s_over && ~clk)
rck = 1;
else
rck = 0;
end
endmodule
我的Cyclone II 核心板时钟 50Mhz 惟恐频率太高,所以我有写了分频模块:
//Module :
//Desc : Serial clock generator
module clk_gen(clk_out, clk_in, reset_n);
output clk_out;
input clk_in, reset_n;
reg clk_out, clk_out_0, clk_out_reg;
reg[2 : 0] count;
always@(posedge clk_in or negedge reset_n) begin
if(!reset_n) begin
count <= 0;
clk_out_0 <= 0;
clk_out <= 0;
clk_out_reg <= 0;
end
else begin
count <= count + 1;
if(count == 3'b001) begin
clk_out_reg <= ~clk_out_reg;
count <= 0;
end
clk_out_0 <= clk_out_reg;
clk_out <= clk_out_0;
end
end
endmodule
最后写个顶层模块作为调用者:
//Module :
//Desc : Top Moudle
module Test_For_74hc595(sck, rck, si, sclr_n, clk, reset_n);
output sck, rck, si, sclr_n;
input clk, reset_n;
wire sck;
wire wire_sck;
wire wire_si;
wire wire_clk = clk;
wire wire_reset_n = reset_n;
wire wire_rck;
wire wire_sclr_n;
wire[7 : 0] wire_data = 8'b1111_0101;
reg wire_load;
reg wire_start;
wire wire_transfer_over;
parameter idle = 0;
parameter load = 1;
parameter start = 2;
parameter sending = 3;
parameter over = 4;
reg[2 : 0] state, next_state;
assign rck = wire_rck;
assign si = wire_si;
assign sclr_n = wire_sclr_n;
clk_gen u1(wire_sck, wire_clk, wire_reset_n);
serial_transfer_74hc595 u2(sck, wire_rck, wire_si, wire_sclr_n,
wire_data, wire_load, wire_start, wire_transfer_over,
wire_sck, wire_reset_n);
always@(posedge wire_sck or negedge reset_n) begin
if(!reset_n)
state <= idle;
else
state <= next_state;
end
always@(state or wire_transfer_over) begin
wire_load = 0;
wire_start = 0;
next_state = state;
case(state)
idle :
next_state = load;
load :
begin
wire_load = 1;
next_state = start;
end
start :
begin
wire_start = 1;
next_state = sending;
end
sending :
if(wire_transfer_over)
next_state = over;
over :
next_state = over;
default :
next_state = load;
endcase
end
endmodule
全齐了!!
下面就是仿真了
先功能仿真:
结果如下:
(原文件名:functional sim.JPG)
欧也!!!莫有问题!
好接着时序仿真:
再看:
(原文件名:timing sim.JPG)
欧也!
也没问题。
我主要看了SI---- SCK的建立和保持时间够不够,还有RCK ---- SCK的建立时间按够否 74HC595手册这样描述:
(原文件名:si-sck ts th.JPG)
ts >= 10ns 够了
th >= 0ns 也够了(实际有2ns左右,我的si,sck线是一样长的)
(原文件名:rck- sck ts th.JPG)
ts >= 10ns
th >= 0ns (也全部够)
到此,我已经认为没有问题了
下步干嘛?当然是下载到板上测试啦:
………
先测试了下 发送 data_byte[7 : 0] = 8’b0101_0101
看了下LED,发现貌似正确,再看下,诶,怎么LED 现实的结果是 8’b1010_1010
不对!!!!这怎么回事,貌似少发了一位。
算了,再发个别的数据 这次是 data_byte[7 : 0] = 8’b1111_0000
再看结果,又不对,怎么是 8’b00011110
难道真实少发了一位??
我靠此刻我也没别的想法了,肯定程序不对吧~~~~~~,改!!!改了N各版本无果…..错误依旧。
此刻我已经无奈了,还有有个兄弟也在玩FPGA,我就去请教他,他看了电路说了句,哥们你找个电路连得不对吧~~~~~~我说怎么了??74hc595 的15脚是QA(最低位),你怎么把他当成QH了(最高位),晕,我一看还真是。这也就解释了为什么看起来少发一位的问题了。
想想自己明明记得手册上写的是 15脚是 QH啊,怎么又变成QA了???
我有证据:
(原文件名:pin.JPG)
这明明就是15对QH啊!!!!!!!
怎么回事呢???
在往下看,又发现:
(原文件名:pin15.JPG)
怪谁呢???唉~~~~还是怪自己吧,此刻我已经浪费了大半天。我完全没有想到会是电路接错,因为这确实太简单了~~~~~~~
好了结果了接线错误,我再测对了,这次我想看看发全一什么效果~~~~
诶,怎么有不对,只有一个灯亮???此刻我想到,可能是74HC595的驱动力不够。用万用表量了下输出脚,怎么是0V,驱动力不够也不该一点电压没有啊!!!!!
难道是我的FPGA有问题???算了换块板子试试。。
换了块板子,可以了,全亮了~~~~~结束了,我以为结束了,就把板子放在那里点着。我下意识的按了下FPGA复位键,诶~~~~~怎么又只有一个量了?????难道是代码的复位逻辑有错???再看代码~~~~是在找不出错误???到底怎么了????
此刻我忽然想到了我的74HC595接的是5V,FPGA是3.3V IO,但是我想即使是3.3V高电平,74HC595也该吃啊!!!
我有证据:
(原文件名:VIH.JPG)
74hc595 接4.5V ,只要 3.15V就可以认为是高电平了,怎么不能吃3.3V???唉,实际上我接的是5V,肯定VIH也高了,侥幸啊!!!!全是侥幸心理。
到此,我把74hc595接3.3V吧,所有的问题都消失了~~~~结束了,终于结束了。一个小小的芯片让我明白了很多道理。
1 . 无论干什么都要认真,理性,耐心,思考
2. 永远不要认为事情简单,麻痹大意和侥幸心理会让我吃大亏,还好这次只是时间损失,要是……..唉
最后放上工程代码: Quartus II 9.0ourdev_500877.zip(文件大小:1.13M) (原文件名:TestFor74HC595.zip)
附上原文PDF : 点击此处下载 ourdev_500881.pdf(文件大小:1.23M) (原文件名:原文.pdf)
74HC595 datasheet : 点击此处下载 ourdev_500882.pdf(文件大小:627K) (原文件名:m74hc595.pdf) |
阿莫论坛20周年了!感谢大家的支持与爱护!!
知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)
|