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转换完整流程
- 初始化:设置参考电压、通道选择、预分频系数,开启ADC(ADEN=1)
- 启动转换:置位ADSC位,ADC开始转换
- 等待完成:轮询ADSC位(或使用ADC转换完成中断),直到ADSC自动清零
- 读取结果:按先ADCL后ADCH的顺序读取转换值
内容的提问来源于stack exchange,提问作者AleBerto56
相关产品推荐
相关产品推荐

