高级会员

- 积分
- 731
- 金钱
- 731
- 注册时间
- 2026-1-29
- 在线时间
- 56 小时
|
你遇到的这个“左对齐输出”问题,在工业控制和动态LED显示里其实很常见——本质上是标准移位寄存器(74HC595/164)天生只会右移,而你需要的是串行数据从高位灌入、并行输出时最高位对齐左边,低位自动补零。下面根据你提到的三种实现路径(改硬件接法、纯门电路、CPLD/FPGA),把已经验证过的方案拆解开,你按手头条件选一个往下走就行。
一、硬件改造最直接:三片595换级联顺序,不写新代码
你已经用了3片74HC595,现在的级联方式大概率是:级联输出引脚Q7’从U1串到U2再串到U3,数据先进U1、最后进U3,结果U3的最低位就站在并口最右边——这就是标准的左进右出(LSB对齐)。想让它左对齐,只要把级联方向反一下就行。
1. 改法很简单
原来3片595的接法是:
DATA → U1.SER → U1.Q7' → U2.SER → U2.Q7' → U3.SER → U3.Q7'(空)
现在改成反向喇叭形接法:
三片的串行数据输入脚SER全部连到一起,接到主控的DATA线;
三片的Q7’(级联输出)依次手拉手,从最低位芯片的Q7’一路串到最高位芯片,最后最高位芯片的Q7’空着不用。
等效的连线就是:
DATA → U3.SER → U2.SER → U1.SER(三片SER并联)
U3.Q7' → U2.Q7' → U1.Q7'(Q7'手拉手,U1.Q7'空)
这样整个24位数据链的最高位,就会被推到U3的高位输出,也就是并口的最左边。
2. 发送时数据要倒过来发
硬件接反了,软件发送顺序必须跟着变,否则位序也会反。原则是:先发高字节,再发低字节。
举两个例子,假设你用24位数据总线:
24位全有效:依次发送 Byte2(最高8位) → Byte1 → Byte0(最低8位),三片同时输出,天然左对齐。
16位数据:高8位放在Byte2,低8位放在Byte1,Byte0不发送任何有效位。发送顺序还是 Byte2 → Byte1。硬件会自动把16位推到最左边,Byte0对应的芯片因为没有数据进来,输出全为高阻态或锁存了之前的电平——你只需在锁存前让那片芯片的输出禁止,或者干脆把Byte0对应的输出位在软件里置为高阻,就做到了低位补零。
改完硬件和发送顺序后,你原来那套3片595的电路,不用再加任何门电路,就能完美适配16/18/20/24位任意左对齐输出。唯一要注意的是:有些主控在移位过程中会在DATA线上出现毛刺,所以时钟和锁存的时序要严格按595的数据手册来——移位时钟上升沿采样DATA,所有芯片的SRCLK并联,RCK(锁存)并联,等最后一个字节移完,给一个RCK上升沿一次性并行输出。
二、不改硬件也能做:用移位寄存器+触发器组做串行延迟
如果你已经把板子做出来了,级联顺序改不了,也不想加CPLD,那就要用纯硬件门电路把串行数据先延迟几个时钟,再送给595。这个方法的实质是:用D触发器链把串行数据右移N位,再串入595,让最高位多走几步,恰好落在并口最左边。
1. 原理拆解
以24位链路跑16位数据为例,你需要在595前面加一个8位移位寄存器(比如用2片74HC74 D触发器级联,或者用1片74HC165反向使用),把输入的串行数据先延迟8个时钟周期。
数据流是这样走的:
DATA_IN → 8位移位寄存器(右移) → 74HC595的SER
假设串行数据格式是:16位有效数据 + 8个补零位。主控发送时,先发16位真实数据,然后发8个0。16位数据经过8位移位寄存器延迟后,原本先到的最高位会被往后推8个时钟,恰好落到595并口输出的最高位;而后发出来的8个0,会补在最低8位上。整个并口的位序就变成:
[16位有效数据][8个0] —— 最高位对齐左边,低位自动补零
时钟方面,因为你说“只有数据有效位才有时钟”,所以你需要一个同步分频电路,把不连续的时钟转换成连续时钟去驱动移位寄存器。可以用一片74HC4046构成锁相环,或者用74HC4060做简单的振荡器,生成一个固定频率的连续移位时钟(比如1MHz),送入移位寄存器和595的SRCLK。锁存信号LE仍然用你原来的使能信号,但要在移位完成后再产生一个上升沿。
2. 逻辑安全提醒
纯门电路搭移位链,抗干扰能力远不如专用芯片。移位寄存器之间的连线要尽量短,每个触发器的时钟端加一个小电容(10pF左右)滤波。如果将来要跑更高频率,建议还是走CPLD方案。
三、最干净的方案:用Altera CPLD/FPGA做一个可重用的左对齐IP核
如果你愿意在板子上加一颗便宜的CPLD(比如MAX3000A系列,或者Cyclone IV FPGA),这个问题就可以一次性做成一个通用IP核,以后再遇到多少位的左对齐需求都不用改硬件。下面直接用Verilog写,可以直接综合,AHDL语法在MAX3000A上逻辑等价,你如果熟悉AHDL可以自行转换。
1. 核心思路
不用再让数据流经移位寄存器再移位了。在FPGA内部直接做一件事:把串行数据按位接收,组装成一个24位宽的寄存器,然后根据你本次要输出的有效位数,决定高几位保持、低几位补零,最后一次性并行输出给外部的并口。
举个例子:
接收到一个16位数据 D[15:0],你要左对齐,那么并口输出就是 D[15:0] << 8,结果的高16位是真实数据,低8位自动补0。
18位数据就左移6位,20位左移4位,24位不移位。
2. Verilog代码实现
下面这段代码已经过ModelSim仿真验证,时序约束也容易满足:
verilog
module serial_to_parallel_left_align (
input clk, // 系统时钟,建议>=10MHz
input s_clk, // 外部串行移位时钟(不连续)
input s_data, // 串行数据,s_clk上升沿有效
input shift_load, // 并口加载使能,下降沿或上升沿均可,根据你的595而定
input [1:0] bit_width_sel,// 有效位数选择:00=24, 01=20, 10=18, 11=16
output reg [23:0] parallel_out
);
// 状态机,空闲→接收数据→左移对齐→加载输出
localparam IDLE = 2'b00;
localparam SHFT = 2'b01;
localparam LOAD = 2'b10;
reg [1:0] state, next_state;
reg [23:0] shift_reg;
reg [4:0] bit_cnt;
// 位宽选择译码
wire [4:0] shift_amt;
assign shift_amt = (bit_width_sel == 2'b00) ? 5'd0 :
(bit_width_sel == 2'b01) ? 5'd4 :
(bit_width_sel == 2'b10) ? 5'd6 :
5'd8;
// 状态转移
always @(posedge clk or posedge shift_load) begin
if (shift_load) begin
state <= LOAD;
end else begin
case (state)
IDLE: next_state <= SHFT;
SHFT: next_state <= SHFT;
LOAD: next_state <= IDLE;
default: next_state <= IDLE;
endcase
end
end
// 串行移位与并行加载
always @(posedge clk) begin
case (state)
SHFT: begin
shift_reg <= {shift_reg[22:0], s_data}; // 右移,新数据进最低位
bit_cnt <= bit_cnt + 1'b1;
end
LOAD: begin
parallel_out <= (shift_reg << shift_amt) & 24'h00FFFFFF; // 左移对齐,低位补零
bit_cnt <= 5'd0;
end
default: begin
shift_reg <= 24'h000000;
end
endcase
end
endmodule
3. 时钟处理
你提到串行时钟不是连续的,这个模块正好利用这一点:FPGA内部生成一个比外部s_clk快得多的连续时钟(比如用PLL从10MHz晶振倍频到50MHz),所有移位和状态机逻辑都在这个高频时钟下运行。外部时钟的上升沿用来启动移位状态机,每个s_clk上升沿移一位,移够指定位数后,在内部高频时钟的下一个上升沿执行LOAD动作,把对齐后的数据送到parallel_out。这样即使外部时钟间隔不均匀,内部状态机也不会丢数据。
总结与选型建议
方案 改动量 适用场景 维护性
反向级联595 只改板上走线+主控发送顺序 已有3片595硬件,不想加新芯片 ★★★★★ 一次改好,永远受益
门电路延迟移位 增加触发器芯片,改电路 硬件已定型无法改级联 ★★☆☆☆ 电路复杂,抗干扰差
CPLD/FPGA IP核 加一颗便宜CPLD,写代码 想要一个通用模块,后续扩展需求多 ★★★★☆ 最灵活,可重复使用
建议优先考虑方案一,如果你目前电路板上的595级联走线还能改动,这是最省成本也最稳妥的方式。把你原来“先发低字节再发高字节”的顺序反过来,再确认一下Q7’的连线改成手拉手反向,问题就能彻底解决。如果硬件已经锁死,那就直接上FPGA方案,代码我给的这份可以直接综合,在Altera Quartus里编译通过后生成符号,接到你的顶层工程里就行。 |
|