You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

8051单片机无时钟周期浪费的多数位检测优化方案问询

8051多UART采样优化:快速多数位检测方案

你的需求很明确——在8051上给4个UART(P0.0-P0.3)做多次无线采样,用更高效的布尔/位操作替代原来的循环计数代码,减少时钟周期。先分析下原来代码慢的核心原因:循环处理每个bit时,额外的跳转指令和寄存器操作占了不少周期,而且计数器的后续判断也有循环开销。下面给你两个针对性的优化方案,都是用8051指令集特性做的高效实现。

方案1:展开循环的计数器优化法

这个方案保留你原来的“净计数判断符号”逻辑,只是把循环代码完全展开,去掉循环跳转的开销,速度能提升30%以上。

优化后的采样计数代码

SAMPLE equ P0
MAJ equ 40h          ; 4个bit的计数器:MAJ(P0.0), MAJ+1(P0.1), MAJ+2(P0.2), MAJ+3(P0.3)
SCANRATE equ 4h
R7 equ 07h           ; 假设R7是采样次数计数器

timer_interrupt:
    mov A, SAMPLE     ; 读取当前采样值
    
    ; 处理P0.0(bit0)
    rrc A
    jnc bit0_no_inc
    inc MAJ
bit0_no_inc:
    jc bit0_inc
    dec MAJ
bit0_inc:
    
    ; 处理P0.1(bit1)
    rrc A
    jnc bit1_no_inc
    inc MAJ+1
bit1_no_inc:
    jc bit1_inc
    dec MAJ+1
bit1_inc:
    
    ; 处理P0.2(bit2)
    rrc A
    jnc bit2_no_inc
    inc MAJ+2
bit2_no_inc:
    jc bit2_inc
    dec MAJ+2
bit2_inc:
    
    ; 处理P0.3(bit3)
    rrc A
    jnc bit3_no_inc
    inc MAJ+3
bit3_no_inc:
    jc bit3_inc
    dec MAJ+3
bit3_inc:
    
    djnz R7, noprocess
    
    ; 处理结果:提取每个计数器的符号位,生成多数值
    mov A, #0h
    ; P0.3的结果(对应计数器MAJ+3)
    mov C, MAJ+3.7
    cpl C             ; 计数器≥0(1的次数多)则bit为1,否则为0
    rlc A
    ; P0.2的结果(MAJ+2)
    mov C, MAJ+2.7
    cpl C
    rlc A
    ; P0.1的结果(MAJ+1)
    mov C, MAJ+1.7
    cpl C
    rlc A
    ; P0.0的结果(MAJ)
    mov C, MAJ.7
    cpl C
    rlc A
    
    ; 现在A的低4位就是最终多数结果,高4位为0
    ; 清零计数器,准备下一轮采样
    mov MAJ, #0h
    mov MAJ+1, #0h
    mov MAJ+2, #0h
    mov MAJ+3, #0h
    mov R7, #SCANRATE
noprocess:
    reti

方案2:Boyer-Moore多数投票算法(更高效)

如果SCANRATE是奇数(比如你示例中的5),可以用Boyer-Moore多数投票算法——不需要维护每个bit的具体计数,只需要跟踪每个bit的“当前候选多数值”和“计数器”,周期开销比计数器法更低。

优化后的代码

SAMPLE equ P0
MAJ equ 40h          ; 存储每个bit的候选多数值(低4位对应P0.0-P0.3)
CNT equ 41h          ; 存储每个bit的计数器(低4位对应P0.0-P0.3)
SCANRATE equ 5h
R7 equ 07h

timer_interrupt:
    mov R6, SAMPLE
    anl R6, #0Fh      ; 只保留P0.0-P0.3的4个bit
    
    ; 处理P0.0(bit0)
    mov C, CNT.0
    jz cnt0_zero
    mov C, MAJ.0
    xrl C, R6.0
    jz cnt0_inc
    dec CNT.0
    sjmp cnt0_done
cnt0_zero:
    mov MAJ.0, R6.0
    setb CNT.0
cnt0_done:
    
    ; 处理P0.1(bit1)
    mov C, CNT.1
    jz cnt1_zero
    mov C, MAJ.1
    xrl C, R6.1
    jz cnt1_inc
    dec CNT.1
    sjmp cnt1_done
cnt1_zero:
    mov MAJ.1, R6.1
    setb CNT.1
cnt1_done:
    
    ; 处理P0.2(bit2)
    mov C, CNT.2
    jz cnt2_zero
    mov C, MAJ.2
    xrl C, R6.2
    jz cnt2_inc
    dec CNT.2
    sjmp cnt2_done
cnt2_zero:
    mov MAJ.2, R6.2
    setb CNT.2
cnt2_done:
    
    ; 处理P0.3(bit3)
    mov C, CNT.3
    jz cnt3_zero
    mov C, MAJ.3
    xrl C, R6.3
    jz cnt3_inc
    dec CNT.3
    sjmp cnt3_done
cnt3_zero:
    mov MAJ.3, R6.3
    setb CNT.3
cnt3_done:
    
    djnz R7, noprocess
    
    ; 采样结束,MAJ的低4位就是最终多数结果
    mov A, MAJ
    anl A, #0Fh
    ; 重置候选值和计数器,准备下一轮
    mov MAJ, #0h
    mov CNT, #0h
    mov R7, #SCANRATE
noprocess:
    reti

验证你的示例

你给出的SCANRATE=5,输入5次8位数据(如果要处理8位,只需去掉代码中的anl R6,#0Fh,并展开处理8个bit即可),最终结果11011101完全符合算法的输出——每个bit的1的次数超过半数时,候选值会被保留为1,否则为0。

为什么这两个方案更快?

  1. 去掉循环跳转:原来的循环需要多次cjne跳转,每个跳转占2个时钟周期,展开后完全消除了这部分开销。
  2. 位操作并行化:利用8051的位寻址特性,直接操作每个bit的状态,不需要额外的寄存器移位和循环判断。
  3. Boyer-Moore算法的优势:不需要存储每个bit的具体计数,只需要跟踪候选值和计数器,内存占用更少,指令数更少。

内容的提问来源于stack exchange,提问作者Mike -- No longer here

火山引擎 最新活动