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

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的寄存器配置非法,触发总线错误或非法访问复位。

具体错误点

  1. GCLK GENCTRL配置非法:
    原代码中GCLK->GENCTRL.reg的手动位操作错误,没有正确指定时钟源和使能位,导致ADC时钟不稳定。
  2. ADC INPUTCTRL的GAIN设置错误:
    注释说明要设置GAIN_DIV2,但实际写了0b1111<<24——GAIN字段仅3位(位24-26),值范围0-7,0b1111(15)超出范围,直接导致ADC配置非法触发复位。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 07:29:55