OpenEdv-开源电子网

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

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

[复制链接]

1

主题

12

帖子

0

精华

新手上路

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

串入并出左对齐.jpg
回复

使用道具 举报

1

主题

12

帖子

0

精华

新手上路

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

使用道具 举报

13

主题

3515

帖子

1

精华

论坛元老

Rank: 8Rank: 8

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

使用道具 举报

0

主题

165

帖子

0

精华

高级会员

Rank: 4

积分
787
金钱
787
注册时间
2026-1-29
在线时间
63 小时
发表于 2026-9-28 13:36:50 | 显示全部楼层
你遇到的这个“左对齐输出”问题,在工业控制和动态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

主题

12

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2026-9-28
在线时间
9 小时
 楼主| 发表于 2026-9-28 14:03:11 | 显示全部楼层
本帖最后由 声威电声 于 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

主题

12

帖子

0

精华

新手上路

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

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

使用道具 举报

0

主题

165

帖子

0

精华

高级会员

Rank: 4

积分
787
金钱
787
注册时间
2026-1-29
在线时间
63 小时
发表于 2026-9-28 15:43:46 | 显示全部楼层
声威电声 发表于 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

主题

12

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2026-9-28
在线时间
9 小时
 楼主| 发表于 2026-9-29 10:33:59 | 显示全部楼层
本帖最后由 声威电声 于 2026-9-29 11:48 编辑
664929323 发表于 2026-9-28 15:43
非常理解你的需求——用 EPM7128(CPLD)在不增加外围电路、不改变现有接线的前提下,实现任意位宽(16/1 ...

万分感谢。但是有个问题就是CPLD外部没有晶振能否实现。因为移位都是需要晶振支持,然而BCK只有有数据的时候才能产生。所以最头疼就是这个问题。因为是固化到模块内部无法增加晶振。还有就是我需要20BIT并口真实有效。也就是16与18BIT需要填0,而24BIT自动去掉后四位。我现在最头疼就是不能增加晶振。也就是只能通过逻辑门实现。如果不行就要考虑您提供的晶振方案。但是整体改动就更麻烦一些。无论如何您是热心的工程师。祝福国庆节快乐。如果有能够不依赖外部晶振就更完美。再次感谢。
另外,有无方法实现输入第一个MSB自动填充到EMP7128的某个位置,而顺序这样填充,第一个进入的输入永远固定一个位置?因为不会使用Verilog HDL,所以还要请教您,首先开辟24BIT为0,然后一个时钟上沿触发一个门永远固定位置。后面以此类推。这样就轻松实现了。通过2只74HC163 的输出端的计数器来改变开门的路径(幻想一下2*74HC163+2*74HC42=20位开关),把1-24BIT数据加载到所有74HC42的开关控制的20bit寄存器中,然后当开1的时候刚好MSB进入第一个,当第二位到来的时候刚好第二个门开启(虽然1-24位都经过在这个门上,但是只有第二个信号的时候这个开关是开的),以此类推开20次门(虽然1-24位都经过在这个门上,但是只有第20个信号的时候这个开关是开的)刚好LSB加载到最后一个位置。以后21-24经过到所有门的时候所有门都是关闭,自动忽略。然后利用LE复位163与加载24BIT信息,重新进入下一个周期。这样就避免了595与164的列队方式。

宗旨是利用一个计数器靠BCK触发自动加一,利用如38译码器的东西,实现20个开关的控制,把20位数据的加载到所有的门上面,计数1的时候开第一个门,刚好20位数据的MSB进入第一个门,其他所有门虽然也有20bit信息但是门没开,计数器再加一开第二个,这时候第二扇门开启而刚好到来的是第2位信息。以此类推,计数器不停加一,不停逐个开,直到20个门LSB到来之后不再动作,后面虽然继续开门但是没有引线出来到DAC,所以自动作废,等待LE。LE复位计数器与加载并口数据输出。
对于16和18bit,则是计数器数到16或者18不再动作等待LE,因为没有数据,16-20或18-20中间所有都是空白默认都是0。然后等待LE。


问题就是利用电路图来搞定非常复杂所以只能通过Verilog HDL,几句话即可搞定。您是专家帮忙看一下这样的方案如何编辑?


回复

使用道具 举报

0

主题

165

帖子

0

精华

高级会员

Rank: 4

积分
787
金钱
787
注册时间
2026-1-29
在线时间
63 小时
发表于 2026-9-29 13:23:31 | 显示全部楼层
声威电声 发表于 2026-9-29 10:33
万分感谢。但是有个问题就是CPLD外部没有晶振能否实现。因为移位都是需要晶振支持,然而BCK只有有数据的 ...

你好!不依赖外部晶振,在CPLD(比如你提到的EMP7128)内部完全可以实现,甚至比你想象中更简单——核心就是利用CPLD内部门电路延迟产生的时钟,或者干脆用BCK信号直接生成内部系统时钟,彻底绕开外置晶振。

另一个关键点是,用纯逻辑门(74HC163+74HC42)去做20扇门的序列控制,逻辑上虽然走得通,但在CPLD的实际实现里会极度消耗宏单元,并且带来无法保证的时序延迟。而你头疼的“输入第一个MSB固定位置、后面自动顺延”的问题,用几句Verilog HDL就能优雅解决,完全没必要去硬拼那十几片4066或42译码器。下面分块说清楚。

一、不用外接晶振的两种核心方案

1. CPLD内部门级振荡器(最直接替代方案)

你遇到的第一道坎——“CPLD外部没有晶振、移位需要时钟”——本质上是把时钟源和移位操作这两个概念绑得太紧了。移位寄存器或计数器确实需要一个时钟脉冲来驱动,但这个时钟不一定非要来自外部晶振。利用CPLD内部闲置的逻辑门,可以自己造一个时钟出来。

具体做法很简单:把3个或5个非门串接成一个环路,形成一个环形振荡器。由于门电路本身存在固有的传播延迟(通常在几个纳秒量级),这个环路会在通电瞬间自我激发,产生高频方波。为了得到稳定的、可供移位使用的频率(例如常见的几十MHz),一般会在环路里串入一个小电阻和一个电容,构成RC振荡网络,把原始的高频毛刺平滑成稳定方波。这种方案在早年没有内置振荡器的CPLD时代(EMP7128就属于这类)是教科书级的常规手段,只要控制好门延迟级数,频率可落在几MHz到几十MHz区间,完全满足音频类移位寄存器的需求。

注意事项:环路里非门的数量必须是奇数(3级或5级最常见),否则逻辑会自锁在某一个电平上,振荡不起来。另外,输出波形会有比较明显的抖动,不适合对时钟纯净度有严苛要求的应用,但驱动一个串行输入移位寄存器或者简单计数,是绰绰有余的。

2. 用BCK边沿生成内部系统时钟(更贴合你当前需求)

还有一个更优解,就是直接用你手头已有的BCK信号来驱动所有逻辑,完全不必额外造一个独立时钟。你提到“BCK只有有数据的时候才能产生”,这恰好是串行音频的常态——BCK本身就是帧同步信号,它自带的数据沿天然给出了每一个Bit的时间基准。

实现方式是:在CPLD内部建一个状态机或脉冲展宽电路。每当BCK出现一个上升沿,就通过去毛刺和窄脉冲生成电路(比如用门延时加RC网络,或者更推荐用两段式状态机)提取出一个干净的短脉冲。这个脉冲可以用锁相环倍频到更高频率(如果需要的话),或者直接作为全局时钟去驱动一个移位寄存器链。在标准I2S协议中,BCK和LRCK的占空比通常是50%,这意味着每个BCK周期内都可以稳定地采样并移位一次。这样BCK本身就成了你的系统时钟,既省掉了外置晶振,又天然保证了数据和时钟的相位对齐。

你后续要处理的移位、计数、门控,全都挂在这个BCK生成的时钟域下,整个模块就自包含了,根本不需要外接任何时钟源。

二、你设想的74HC163+74HC42方案,为什么在CPLD里走不通

先把理论可行性说清楚:用两片74HC163构成模20计数器,然后用两片74HC42(4线-10线译码器)扩展出20个独立的使能线,去控制20个模拟开关(比如4066之类),让每个Bit在对应的时间窗口选通进入预定位置——逻辑推演上是成立的。

但EMP7128内部是可编程互连矩阵+宏单元结构,不是一块面包板让你随意飞线。把20个独立门控信号全部路由出来,还要让这20个信号经过不同延迟到达各自的寄存器,会急剧消耗宏单元资源,并且产生大量绕线延迟。学过数字电路的都知道,大量译码器叠加出来的组合逻辑,会形成很深的扇入扇出树,容易在内部产生竞争冒险,结果是某些通道的数据可能错位。更要命的是,CPLD全局时钟网络的分配是极其宝贵的,如果你这20条信号不是同步逻辑,很可能出现时序收敛问题,连仿真都未必能跑出干净波形。

这也是为什么现代硬件描述语言能取代纯逻辑门设计——它让工具自己去优化这些复杂的组合逻辑,而不是让你手动搭建。

三、用Verilog HDL几行代码实现你要的20位完美对齐

你说“不会Verilog”,其实像你描述的那种“24位预载、BCK来时首位固定入位、后面自动填零、20位截断”的需求,用HDL写起来异常简洁,逻辑只有一句话:24位移位寄存器 + BCK驱动的计数器。

下面这个框架直接就可以放到Quartus II里编译,完全针对EMP7128这类CPLD:      
verilog

module Audio_20Bit_Align(
    input clk,          // 由BCK或内部振荡器产生的系统时钟
    input bck,          // BCK信号,上升沿代表一个数据位有效
    input [23:0] par_data, // 24位并口输入(对应DSP模式下的24bit数据)
    input load,         // 并行装载信号(你的LE),上升沿时将24位数据打入
    input [1:0] width_sel, // 数据宽度选择:00=24bit,01=20bit,10=18bit,11=16bit
    output reg [19:0] dac_data  // 对齐后的20位输出,直接接DAC
);

    reg [23:0] shift_reg;
    reg [3:0] bit_cnt;

    // 24位数据拼接与移位逻辑
    always @(posedge clk) begin
        if(load) begin
            // 装载时,把并口数据最高位放在首位,准备移位
            shift_reg <= par_data;
            bit_cnt <= 0;
        end
        else if(posedge bck) begin // BCK上升沿,数据左移一位
            shift_reg <= shift_reg << 1;
            bit_cnt <= bit_cnt + 1;
        end
    end

    // 根据宽度选择,自动填零并截取20位输出
    always @(*) begin
        case(width_sel)
            2'b01: dac_data = shift_reg[23:4]; // 20bit:自动丢掉低4位
            2'b10: dac_data = {shift_reg[23:6], 2'b00}; // 18bit:低6位填0
            2'b11: dac_data = {shift_reg[23:8], 4'b0000}; // 16bit:低8位填0
            default: dac_data = shift_reg[23:0]; // 24bit全保留
        endcase
    end

endmodule

      
逻辑解读一下,就三件事:

load(你的LE)上升沿来时,24位并行数据直接锁存到shift_reg,计数器清零,这个时候还没开始移位。
随后每一个bck上升沿,shift_reg整体左移一位,MSB(最高位)首先被推到预定的高位上,后续数据像流水线一样依次占据更高位——完全实现了你说的“第一个MSB固定进入某个位置,后面顺次跟进”。
输出选择部分,用一个case语句根据width_sel自动处理不同位宽:20位模式直接截掉低4位;18位模式在低6位补零;16位模式在低8位补零。不用外部开关,全部数字化可控。
注意一点:这里对load和bck做了优先级约定,load会打断移位,符合你“装载并口数据后,再开始逐位移出”的预期。如果load需要异步复位计数器,只需加一条异步清零端即可。

四、工程实操建议

如果你不习惯写代码,有一个过渡方法:在Quartus II里先建好这个Verilog文件,编译通过后,将它生成为一个符号,然后放进原理图编辑界面。你在原理图里拖入74HC163、74HC42这些你熟悉的芯片符号,用连线把你的HDL模块符号和这些外部逻辑连起来。这样你看到的是一张熟悉的原理图,但内部复杂时序已经由HDL工具自动优化掉了。这也是很多传统硬件工程师转型时的平滑路径。

至于那套门控方案,真的可以放下了——在可编程逻辑器件里,越复杂、越门控的东西,往往越脆弱。

你的20位真实有效、16/18位自动填零的需求,本质上就是标准的音频DSP并串转换里的位对齐问题,用移位寄存器天然解决,不需要任何类似4066的模拟开关。整个方案去掉595和164的级联,数据流更直接,延迟也更低。

祝你国庆愉快,这个小瓶颈拿下来后,后面会顺畅很多。
回复

使用道具 举报

1

主题

12

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2026-9-28
在线时间
9 小时
 楼主| 发表于 2026-9-29 14:26:38 | 显示全部楼层
664929323 发表于 2026-9-29 13:23
你好!不依赖外部晶振,在CPLD(比如你提到的EMP7128)内部完全可以实现,甚至比你想象中更简单——核心 ...

我画了一份草图,本想给您参考。没有想到您回复的如此之快。再次感谢。如果成功我联系您,将支付您设计费用。我还是公布一下我的基本思路。但是对于利用语言编辑可能更加简单。
MSB加载.png
回复

使用道具 举报

1

主题

12

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2026-9-28
在线时间
9 小时
 楼主| 发表于 2026-9-29 14:50:04 | 显示全部楼层
本帖最后由 声威电声 于 2026-9-29 15:14 编辑
664929323 发表于 2026-9-29 13:23
你好!不依赖外部晶振,在CPLD(比如你提到的EMP7128)内部完全可以实现,甚至比你想象中更简单——核心 ...

非常感谢。对于EPM7128我是用的MAX+PLUS II来编辑,所以要麻烦您帮我形成一个封装。这样可以直接添加到我的电路之中。三个输入引脚,24个输出引脚。在使用中目前只会使用电路图编辑。没办法年级大不懂语言。但是非常想学习,因为就是几句话就可以完成我的所有设计。目前项目急于实现,待到我学到精髓时候黄瓜菜都彻底凉透心了!
我已下载Quartus II,非常期待测试结果。
编辑项目.png
回复

使用道具 举报

1

主题

12

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2026-9-28
在线时间
9 小时
 楼主| 发表于 2026-9-29 15:38:29 | 显示全部楼层
利用假期硬啃语言!
假期学习.png
回复

使用道具 举报

1

主题

12

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2026-9-28
在线时间
9 小时
 楼主| 发表于 2026-9-30 11:24:57 | 显示全部楼层
有四处错误不知道如何调整?
err.png
回复

使用道具 举报

1

主题

12

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2026-9-28
在线时间
9 小时
 楼主| 发表于 2026-10-6 12:33:37 | 显示全部楼层
Error (10170): Verilog HDL syntax error at audio_20bit_align.v(20) near text "posedge";  expecting an operand
Error (10112): Ignored design unit "Audio_20Bit_Align" at audio_20bit_align.v(1) due to previous errors
Error: Quartus II 64-Bit Analyze Current File was unsuccessful. 2 errors, 0 warnings
        Error: Peak virtual memory: 406 megabytes
        Error: Processing ended: Tue Oct 06 12:30:20 2026
        Error: Elapsed time: 00:00:01
        Error: Total CPU time (on all processors): 00:00:00
回复

使用道具 举报

1

主题

12

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2026-9-28
在线时间
9 小时
 楼主| 发表于 2026-10-12 11:26:13 | 显示全部楼层
664929323 发表于 2026-9-29 13:23
你好!不依赖外部晶振,在CPLD(比如你提到的EMP7128)内部完全可以实现,甚至比你想象中更简单——核心 ...

您好。麻烦帮看一下有一处错误,不知如何处理!
回复

使用道具 举报

1

主题

12

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2026-9-28
在线时间
9 小时
 楼主| 发表于 7 天前 | 显示全部楼层
搞定!通过10.1休息这几天包括后续几天研究发现一个问题。不管是Verilog还是VHDL或者AHDL,在使用AI编辑的情况下,都是问题百出,尤其豆包,是真的豆包。连最基本的编译都不能通过。其次DEEPSEEK能够在修改之后勉强编译通过,但是对于执行门都没有。再次千问,编译能够通过。执行一堆垃圾噪声完全没有一点音乐和人声。最好的是KIMI,编辑通过,测试能够出声但是有噪声。相对来说KIMI是这几种最好的。所以干脆放弃了采用verilog方式。还是最基本的死磕硬件实现。对于不会编程的人来讲懂硬件即是好事也是坏事,好事就是你能理解所有门和触发器的工作原理,坏事是不能操作MCU。对于CPLD来说,硬件操作完全是执行力最高的方式。因为直接写电路进去它想错都没有机会。而不会像AI那样,错误百出了。但是还是要借助于AHDL的部分代码完成。因为在写清零CLR0的时候,如果使用电路图方式那么直接系统会默认输出接地。所以某些部分还是需要使用AHDL来辅助操控。现在测试完成了之前的要求。在并口输出永恒为20bit而且MSB对齐,不管输入是14、15、16、17、18、19、20、21、22、23、24bit还是任意数位只要小于32bit,都能完美实现,而非如verilog那种必须根据输入位数来限制输入端的LSB补零。
MSB20BIT.png
回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

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

GMT+8, 2026-10-22 01:37

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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