SAMR21调试时ADC采样后LED控制代码执行异常问题
ADC采样平均控制LED异常:程序跳回main开头而非执行LED逻辑
需求
- 完成ADC连续3次采样并取平均值
- 12位ADC结果低于满量程一半(<2048)时关闭板载LED,高于则点亮LED
问题现象
ADC采样功能正常,但当samp >=3进入数值比较环节时,LED控制代码未执行,程序直接跳回main函数起始位置,随后进入while(1)循环。
调试细节
使用Atmel Studio逐行调试时,预期执行PORT->Group[0].OUTSET或OUTCLR语句,但实际程序触发跳转,回到main开头进入主循环。
原始代码
/* * Include header files for all drivers that have been imported from * Atmel Software Framework (ASF). */ /* * Support and FAQ: visit <a href="https://www.microchip.com/support/">Microchip Support</a> */ #include <asf.h> int main (void) { system_init(); uint32_t result = 0; uint8_t samp = 0; PORT->Group[0].DIRSET.reg |= 1<<19; ADC->DBGCTRL.reg = 1; SYSCTRL->OSC8M.reg |= 1<<30 | 1<<6 | 0b10; //ADC Pin6 Config PORT->Group[0].DIRCLR.reg |= 1<<6; PORT->Group[0].PMUX[3].reg = 0x1; PORT->Group[0].PINCFG[6].reg &= ~(0b11); PORT->Group[0].PINCFG[6].reg |= 1; //ADC CLK Config PM->APBCMASK.reg |= PM_APBCMASK_ADC; //Enable clk bus /* Enable GCLK1 for the ADC */ GCLK->GENDIV.reg = 0; while (GCLK->STATUS.bit.SYNCBUSY) {}; GCLK->GENCTRL.reg = 1<<21 | 1<<19 | 1<<17 | 1<<16 | 1<<10 | 1<<9; while (GCLK->STATUS.bit.SYNCBUSY) {}; GCLK->CLKCTRL.reg = 1<<14 | 0x1E; /* Wait for bus synchronization. */ while (GCLK->STATUS.bit.SYNCBUSY) {}; uint32_t bias = (*((uint32_t *) ADC_FUSES_BIASCAL_ADDR) & ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos; uint32_t linearity = (*((uint32_t *) ADC_FUSES_LINEARITY_0_ADDR) & ADC_FUSES_LINEARITY_0_Msk) >> ADC_FUSES_LINEARITY_0_Pos; linearity |= ((*((uint32_t *) ADC_FUSES_LINEARITY_1_ADDR) & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos) << 5; /* Wait for bus synchronization. */ while (ADC->STATUS.bit.SYNCBUSY) {}; /* Write the calibration data. */ ADC->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | ADC_CALIB_LINEARITY_CAL(linearity); /* Wait for bus synchronization. */ while (ADC->STATUS.bit.SYNCBUSY) {}; /* Use the internal VCC reference. This is 1/2 of what's on VCCA. since VCCA is typically 3.3v, this is 1.65v. */ ADC->REFCTRL.reg = 2; /* Only capture one sample. The ADC can actually capture and average multiple samples for better accuracy, but there's no need to do that for this example. */ ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1; /* Set the clock prescaler to 512, which will run the ADC at 8 Mhz / 512 = 31.25 kHz. Set the resolution to 12bit. */ while (ADC->STATUS.bit.SYNCBUSY) {}; ADC->CTRLB.reg = 0; /* Configure the input parameters. - GAIN_DIV2 means that the input voltage is halved. This is important because the voltage reference is 1/2 of VCCA. So if you want to measure 0-3.3v, you need to halve the input as well. - MUXNEG_GND means that the ADC should compare the input value to GND. - MUXPOST_PIN3 means that the ADC should read from AIN3, or PB09. This is A2 on the Feather M0 board. */ while (ADC->STATUS.reg == STATUS_BUSY) {} ADC->INPUTCTRL.reg = 0b1111<<24 | 0b11 << 11 | 6; /* Wait for bus synchronization. */ while (ADC->STATUS.bit.SYNCBUSY) {}; /* Enable the ADC. */ ADC->CTRLA.reg = 0b10; /* Wait for bus synchronization. */ while (ADC->STATUS.bit.SYNCBUSY) {}; while (1) { while (ADC->STATUS.bit.SYNCBUSY) {}; /* Wait for the result ready flag to be set. */ ADC->SWTRIG.bit.START = 1; while(ADC->INTFLAG.bit.RESRDY == 0){}; /* Clear the flag. */ ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY; /* Read the value. */ result += ADC->RESULT.reg; samp++; if(samp >= 3){ if((result/3) < 2045){ PORT->Group[0].OUTSET.reg |= 1<<19; } else{ PORT->Group[0].OUTCLR.reg |= 1<<19; } result = 0; samp = 0; } } }
问题定位与修复
核心原因
程序跳回main开头是硬件复位导致的,根源是ADC和GCLK的寄存器配置非法,触发总线错误或非法访问复位。
具体错误点
- GCLK GENCTRL配置非法:
原代码中GCLK->GENCTRL.reg的手动位操作错误,没有正确指定时钟源和使能位,导致ADC时钟不稳定。 - ADC INPUTCTRL的GAIN设置错误:
注释说明要设置GAIN_DIV2,但实际写了0b1111<<24——GAIN字段仅3位(位24-26),值范围0-7,0b1111(15)超出范围,直接导致ADC配置非法触发复位。 - ADC BUSY等待语句错误:
while (ADC->STATUS.reg == STATUS_BUSY) {}中,STATUS_BUSY未限定为ADC的STATUS寄存器,应直接使用ADC->STATUS.bit.BUSY。
修复后的关键代码片段
// 修复GCLK1配置:使能GCLK1,选择OSC8M为时钟源 GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSC8M | GCLK_GENCTRL_ID(1); while (GCLK->STATUS.bit.SYNCBUSY) {}; // 修复ADC INPUTCTRL配置:使用宏定义确保参数合法,GAIN_DIV2 + GND负端 + PIN6正端 ADC->INPUTCTRL.reg = ADC_INPUTCTRL_GAIN_DIV2 | ADC_INPUTCTRL_MUXNEG_GND | ADC_INPUTCTRL_MUXPOS_PIN6; while (ADC->STATUS.bit.SYNCBUSY) {}; // 修复BUSY等待逻辑 while (ADC->STATUS.bit.BUSY) {}
额外优化
- 12位ADC满量程一半是2048,建议将判断条件从
2045改为2048,逻辑更准确。 - 调试时可读取
RSTC->RSTFR.reg查看复位原因,快速定位硬件类问题。
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

