You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ATmega328p光敏传感器ADC汇编代码详解及MUX原理问询

我来帮你把这段AVR汇编代码和ATmega328P的ADC MUX机制拆解清楚,一步步来:


一、汇编代码逐行解析

先把代码按功能拆分,逐个模块解释:

1. 预处理与段定义

.equ F_CPU = 16000000;  ; 定义CPU时钟为16MHz,和Arduino UNO的时钟一致
.DEF rmp = R16           ; 给通用寄存器起别名,让代码更易读、复用性更强
.DEF tmp = R17
.DEF tmp2 = R18
.DEF reg1 = r20
.DEF reg2 = r21
.EQU ALFA=250            ; 这个常量在当前代码里没用到,应该是预留的参数
.DSEG                    ; 声明数据段(用于存储变量)
.ORG 0X0100              ; 指定数据段起始地址
.CSEG                    ; 声明代码段(存储程序指令)
.ORG $0000               ; 代码段从0地址开始(AVR的复位向量入口)
jmp Main                 ; 复位后直接跳转到主程序,这是AVR单片机的标准启动流程

2. 主程序初始化与循环

Main:
    ; 初始化栈指针——AVR汇编必须先做这个,否则函数调用(rcall)会崩溃
    ldi rmp, HIGH(RAMEND) ; 取RAM最高地址的高位字节
    out SPH,rmp           ; 写入栈指针高位寄存器SPH
    ldi rmp, LOW(RAMEND)  ; 取RAM最高地址的低位字节
    out SPL,rmp           ; 写入栈指针低位寄存器SPL

    ; 初始化Port B为输出模式
    ldi rmp,0xff          ; 把rmp设为0xFF(所有位为1)
    out DDRB,rmp          ; 写入DDRB寄存器,设置Port B所有引脚为输出(用来显示采样结果)

    rcall ad              ; 调用ADC初始化函数

    ; 以下两行是睡眠模式相关,当前代码没用到睡眠,属于预留代码
    ldi rmp,1<<SE         ; 设置SE位,允许睡眠模式
    out MCUCR,rmp
    sei                   ; 开启全局中断(当前代码用轮询方式采样,这行其实可以去掉,不影响功能)

Loop:
    rcall adcRead         ; 调用ADC采样函数
    out PORTB,r18         ; 把ADCH(采样结果的高2位)输出到Port B
    rjmp loop             ; 无限循环,持续采样输出

注意:代码里只输出了ADCH的值,因为ADMUX的ADLAR位默认是0(右对齐模式),ADCH只存储10位采样结果的高2位,所以输出范围是0-3。如果需要完整的10位结果(0-1023),需要把ADCL和ADCH组合起来,比如 mov reg1, r17 + lsl r18 + lsl r18 + or reg1, r18。

3. ADC初始化函数(ad)

ad:
    ; 设置ADC参考电压为AVCC(Arduino板上的5V电源)
    ldi rmp, (1<<REFS0)   ; REFS0=1、REFS1=0,对应AVCC作为参考电压
    sts ADMUX, rmp        ; 写入ADMUX寄存器(ADC多路选择寄存器)

    ; 使能ADC并设置预分频系数
    ldi rmp, (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
    sts ADCSRA, rmp       ; 写入ADCSRA寄存器:
                          ; - ADEN=1:开启ADC模块
                          ; - ADPS2/ADPS1/ADPS0=1:预分频系数设为128
                          ;   16MHz / 128 = 125kHz,正好在ADC最佳采样频率(50-200kHz)范围内,保证转换精度
    ret

4. ADC采样读取函数(adcRead)

adcRead:
    ; 启动ADC转换
    lds rmp, ADCSRA       ; 读取ADCSRA寄存器当前值
    ori rmp, (1<<ADSC)    ; 置位ADSC位,触发一次ADC转换
    sts ADCSRA, rmp

    ; 轮询等待转换完成
loopx:
    lds rmp, ADCSRA
    sbrc rmp,ADSC         ; 检查ADSC位:如果ADSC=1(转换中),跳过下一条指令,继续循环
    rjmp loopx            ; 直到ADSC=0(转换完成),跳出循环

    ; 读取采样结果——顺序必须是先读ADCL再读ADCH!
    lds r17,ADCL          ; 读取低8位结果到r17
    lds r18,ADCH          ; 读取高2位结果到r18
    ret

关键注意点:AVR的ADC结果寄存器是10位的,读ADCH会自动锁定结果寄存器,必须先读ADCL再读ADCH,否则会得到错误的采样值。


二、ATmega328P ADC MUX工作机制

ADC MUX(多路复用器)的核心作用是选择哪一路模拟信号进入ADC模块进行转换,ATmega328P支持8个单端模拟输入(A0-A7)和多种差分输入模式,所有选择都通过ADMUX寄存器控制。

1. ADMUX寄存器的关键位

  • REFS1/REFS0:参考电压选择,决定ADC转换的基准电压:
    • 00:使用AREF引脚的外部参考电压
    • 01:使用AVCC(Arduino的5V电源)作为参考
    • 11:使用内部1.1V基准电压
  • ADLAR:结果对齐模式:
    • 0:右对齐(默认),ADCL存低8位,ADCH存高2位
    • 1:左对齐,ADCH存高8位,ADCL存低2位(适合只需要8位精度的场景)
  • MUX4-MUX0:通道选择位,用来指定要采样的模拟输入:
    • 单端输入:00000对应A0,00001对应A1,…,00111对应A7
    • 差分输入:比如01000对应A0与A1的差分输入(增益1x),还有多种增益选项(20x、10x等)

2. 代码中的MUX配置

你同事的代码里,ADMUX只设置了REFS0=1,MUX位全为0,所以默认选择A0引脚作为模拟输入——你的光敏传感器应该接在A0上才能正常采样。如果需要切换到其他通道,比如A1,只需要修改ADMUX的赋值:ldi rmp, (1<<REFS0)|(1<<MUX0)。

3. ADC转换完整流程

  1. 初始化:设置参考电压、通道选择、预分频系数,开启ADC(ADEN=1)
  2. 启动转换:置位ADSC位,ADC开始转换
  3. 等待完成:轮询ADSC位(或使用ADC转换完成中断),直到ADSC自动清零
  4. 读取结果:按先ADCL后ADCH的顺序读取转换值

内容的提问来源于stack exchange,提问作者AleBerto56

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:59:42