OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 68|回复: 6

[ALTERA] 有偿求助逻辑电路知识 实现串入并出左对齐

[复制链接]

1

主题

4

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2026-9-28
在线时间
0 小时
发表于 昨天 11:15 | 显示全部楼层 |阅读模式
1金钱
目前利用门电路实现的串入并出,如74HC595或74HC164等,都是数据移位寄存器,MSB先入然后以LSB对齐输出并口信息。即使用16BIT、18BIT、20BIT或者最高24BIT。输出并口都是以LSB为最后对齐输出。本人制作一个项目,需要求助高手如何实现串入并出以MSB对齐方式输出并口信息。低位不足以0补齐。如下图。提供三线输入,即时钟,数据,LE下降沿或上升沿使能,输出则为并口左对齐。烦劳高手帮忙利用门电路实现。注意时钟部分在输入时钟信号时只有数据有效位才会有时钟,无效位不含时钟信息,时钟并非常有。有偿求助,烦劳群里高人联系我。亦可使用CPLD实现,ALTERA优先。若能提供ALTERA公司使用的AHDL语言封装好的Symbol插件直接报价即可。再次感谢。

串入并出左对齐.jpg
回复

使用道具 举报

1

主题

4

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2026-9-28
在线时间
0 小时
 楼主| 发表于 昨天 11:18 | 显示全部楼层
目前需求最高24bit,现在使用74HC595芯片3只实现24bit输出,但是16,18与20位都无法实现并口左对齐输出。每次使用这三种数据都需要重新搭电路实现,比较头疼。希望群里高手帮忙解决再次感谢。
回复

使用道具 举报

13

主题

3514

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
9298
金钱
9298
注册时间
2020-5-11
在线时间
5099 小时
发表于 昨天 13:14 | 显示全部楼层
CPLD应该好实现。
但是楼主要不再考虑一下换个思路,发送方设置为LSB先发送,那么74HC595就能实现MSB对齐了?
专治疑难杂症
回复

使用道具 举报

0

主题

155

帖子

0

精华

高级会员

Rank: 4

积分
731
金钱
731
注册时间
2026-1-29
在线时间
56 小时
发表于 昨天 13:36 | 显示全部楼层
你遇到的这个“左对齐输出”问题,在工业控制和动态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里编译通过后生成符号,接到你的顶层工程里就行。
回复

使用道具 举报

1

主题

4

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2026-9-28
在线时间
0 小时
 楼主| 发表于 昨天 14:03 | 显示全部楼层
本帖最后由 声威电声 于 2026-9-28 14:23 编辑
664929323 发表于 2026-9-28 13:36
你遇到的这个“左对齐输出”问题,在工业控制和动态LED显示里其实很常见——本质上是标准移位寄存器(74HC5 ...

万分感谢您的帮助。现在是比较局限性的。我的主控并不是真实的74HC595而是在EPM7128上虚拟的。然而我现在需要在不考虑增加外围电路,尤其增加时钟就实现左对齐。我将上传一下我的线路图信息。请您再次帮助。如果您能帮助写一下Symbol。我将支付费用。这个是一个音频DAC项目,输出端采用18bit输出。例如PCM64。我在输入16bit音频内容时只需要MSB对齐,16位有效即可让PCM64工作于16位模式,我输入18位同样刚好MSB对齐实现满载工作。而我输入20位或24位,同样MSB对齐。但是后面多余的2-4位自动补0或失效,不参与PCM64解码。这样不随输入数位变化而变化就无需担心输入是什么,而MSB始终对齐。
第二幅图,是目前使用24BIT模式连接方法,但是每次切换到20BIT必须重新换板,18与16亦然。所以想要简单一些一张板搞定。
DAC bits.jpg
18bits.jpg
回复

使用道具 举报

1

主题

4

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2026-9-28
在线时间
0 小时
 楼主| 发表于 昨天 14:32 | 显示全部楼层
LcwSwust 发表于 2026-9-28 13:14
CPLD应该好实现。
但是楼主要不再考虑一下换个思路,发送方设置为LSB先发送,那么74HC595就能实现MSB对齐 ...

非常感谢。但是现在输入信号已经固化无法再次更改,而且时钟是最大的问题。因为无数据时是没有时钟的。音频DAC都是这样的工作方式。而不是持续时钟。所以想靠二次移位处理根本无法实现。恐怕只能依靠VHDL语言实现。但是才疏学浅不会编辑VHDL语言。
回复

使用道具 举报

0

主题

155

帖子

0

精华

高级会员

Rank: 4

积分
731
金钱
731
注册时间
2026-1-29
在线时间
56 小时
发表于 昨天 15:43 | 显示全部楼层
声威电声 发表于 2026-9-28 14:03
万分感谢您的帮助。现在是比较局限性的。我的主控并不是真实的74HC595而是在EPM7128上虚拟的。然而我现在 ...

非常理解你的需求——用 EPM7128(CPLD)在不增加外围电路、不改变现有接线的前提下,实现任意位宽(16/18/20/24bit)音频数据的 MSB 左对齐并行输出,且无需手动切换板子。这是完全可行的,核心思路是在 CPLD 内部用移位寄存器 + 左对齐控制逻辑,将串行数据先存再移位,最后统一输出到 18bit 并口。

一、核心设计思路(针对你的项目)
你的输入是串行音频数据(PCM64 的 DATA、BCLK、LRCLK),输出是18bit 并行 DAC 数据。需求是:不管输入是 16/18/20/24bit,都让有效数据的最高位(MSB)对齐 18bit 并口的最高位(即左对齐),多余低位自动补 0(对 PCM64 来说,多余的 2-4 位不参与解码,相当于失效)。

实现方案分3步:

串行数据接收:用 CPLD 内部的移位寄存器,按 BCLK 节奏接收串行数据,存成一个 24bit 的临时寄存器(覆盖最高 24bit 需求)。
左对齐处理:根据当前输入的有效位宽(16/18/20/24bit),将临时寄存器中的数据左移对应位数,让 MSB 对齐 18bit 并口的最高位,低位自动补 0。
并行输出:在 LRCLK(左右声道时钟)的触发下,将左对齐后的 18bit 数据锁存到输出寄存器,驱动 DAC 并口。
二、EPM7128 内部逻辑设计(Verilog 实现)
以下是针对你项目的完整 Verilog 代码,可直接烧录到 EPM7128 中,无需额外硬件改动:      
verilog

module left_align_audio_dac (
    // 输入信号
    input  wire clk,          // CPLD 全局时钟(可接 10MHz 晶振)
    input  wire data_in,      // 串行音频数据(PCM64 的 DATA 线)
    input  wire bclk,         // 串行位时钟(PCM64 的 BCLK 线)
    input  wire lrclk,        // 左右声道时钟(PCM64 的 LRCLK 线,上升沿触发锁存)
    input  wire [1:0] bit_sel,// 位宽选择(00=16bit, 01=18bit, 10=20bit, 11=24bit)
    // 输出信号(18bit 并行 DAC)
    output reg [17:0] dac_out
);

// 24bit 移位寄存器,接收串行数据
reg [23:0] shift_reg;
// 对齐控制寄存器
reg [23:0] align_reg;

// 左移位数计算(根据 bit_sel 自动调整)
wire [4:0] shift_amt;
assign shift_amt = (bit_sel == 2'b00) ? 5'd6 :   // 16bit 左移 6 位
                   (bit_sel == 2'b01) ? 5'd4 :   // 18bit 左移 4 位
                   (bit_sel == 2'b10) ? 5'd2 :   // 20bit 左移 2 位
                                       5'd0;      // 24bit 不移位

// 串行数据移位(bclk 上升沿触发)
always @(posedge bclk) begin
    shift_reg <= {shift_reg[22:0], data_in};
end

// 左对齐处理(clk 同步,避免亚稳态)
always @(posedge clk) begin
    align_reg <= (shift_reg << shift_amt) & 24'h0000FFFF; // 截断到 18bit(低 18 位)
end

// LRCLK 上升沿锁存到 DAC 输出
always @(posedge lrclk) begin
    dac_out <= align_reg[17:0];
end

endmodule

      
三、核心逻辑解释
串行数据接收:shift_reg 是 24bit 移位寄存器,在 bclk 上升沿将 data_in 移入最低位,循环覆盖 24bit,确保能接收最高 24bit 数据。
左对齐控制:根据 bit_sel(位宽选择信号)自动计算左移位数(16bit 左移 6 位、18bit 左移 4 位等),将 shift_reg 左移后,截断低 2-4 位并补 0,只保留高 18bit(对齐 18bit DAC 并口)。
并行输出锁存:在 lrclk 上升沿(PCM64 的左右声道切换时),将左对齐后的 18bit 数据锁存到 dac_out,驱动 DAC 工作。
四、接线说明(无需改动现有硬件)
你现有 24bit 模式的接线保持不变,只需在 CPLD 上增加 2 根 bit_sel 线(用于选择位宽):

CPLD 输入:clk(接 CPLD 全局晶振)、data_in(接 PCM64 的 DATA)、bclk(接 PCM64 的 BCLK)、lrclk(接 PCM64 的 LRCLK)、bit_sel[1:0](接 2 个拨码开关,选择 16/18/20/24bit)。
CPLD 输出:dac_out[17:0](接 PCM64 的 18bit DAC 并口,无需改动)。
五、Symbol 生成(Quartus 中操作)
打开 Quartus,新建工程,导入上述 Verilog 代码,编译生成 .pof 文件(烧录到 EPM7128)。
生成 Symbol:在 Quartus 中右键模块名 → Create Symbol Files for Current Project,会生成 .bsf 文件(Symbol 图标)。
Symbol 引脚定义:
输入:clk、data_in、bclk、lrclk、bit_sel[1:0]
输出:dac_out[17:0]
六、关键优势(符合你的需求)
无需改板:现有硬件接线完全不变,只改 CPLD 内部逻辑。
自动对齐:bit_sel 选择位宽后,自动左移对齐,无需手动调整。
兼容性强:支持 16/18/20/24bit 输入,输出始终是 18bit MSB 对齐,多余位自动失效。
无额外时钟:复用 PCM64 的 bclk 和 lrclk,无需新增时钟电路。
如果需要进一步优化(比如自动检测位宽,省去 bit_sel 拨码开关),可以在 CPLD 中增加“位宽检测逻辑”(根据 bclk 的有效数据长度自动判断),但会稍微增加逻辑复杂度。按当前方案,用 2 个拨码开关选择位宽,是最稳定、最简单的实现方式。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-9-29 00:11

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表