This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] CC2651R3SIPA:有关 CC2651R3SIPA TI-RTOS 中 ADC 中断处理的查询

Guru**** 2322270 points
Other Parts Discussed in Thread: CC2651R3SIPA
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/wireless-connectivity/bluetooth-group/bluetooth/f/bluetooth-forum/1510867/cc2651r3sipa-query-regarding-adc-interrupt-handling-in-ti-rtos-for-cc2651r3sipa

器件型号:CC2651R3SIPA

工具/软件:

尊敬的 TI 支持团队:

我们正在使用 TI-RTOS 开发 CC2651R3SIPA 固件、目前正在实现一个 ADC 中断处理程序(adc_complete_isr)、该中断处理程序从 FIFO 中读取数据并执行一些处理。
就是这个中断 不由操作系统内核管理 —我们将其配置为 零延迟中断 直接修改 NVIC 矢量表而不使用IntRegister(),以避免干扰 TI-RTOS 管理的中断。

在 ISR 中、我们AUX_ADC_IRQAUX_ADC_DONE使用以下行清除 ADC 中断标志(和):

c.
HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR) |= (AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_IRQ | AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_DONE ); HwiP_clearInterrupt(INT_AUX_ADC_IRQ);

我们最初放置了这个标志清除逻辑 发展 ISR 的组成部分。 但是、这样做时、系统会持续重新进入 ISR、就好像中断标志从未被清除过一样。

移动标志清除逻辑 到达 ISR 的末尾 、该行为将按预期实现、并且每次转换仅触发一次中断。

我们希望了解:

  1. 为什么在 ISR 开始时清除中断标志会使其连续重新进入?

  2. 为什么将标志清除移至结尾会解决问题?

  3. 在结尾而不是开头清除旗帜是否有任何副作用?

如果您能提供任何技术见解或文档参考、我们将不胜感激。

此外、我们还将附加adc_openadc_complete_isr实现设置和 ISR 处理的函数的源代码。

void ADC_OPEN (void)

//防止转换到待机状态
Power_setConstraint (PowerCC26XX_SB_disallow);
AUXADCDisable ();

//启用 ADC 并设置触发器
AUXADCEnableSync (ADCCC26XX_VDDS_REFERENCE、ADCCC26XX_SAMPLING_DURATION_2P7_US、ADCCC26XX_TRIGGER_MANUAL);

//获得用于 ADC 转换校正的增益和偏移量
S_adcCtx.adcGain = AUXADCGetAdjustmentGain (AUXADC_REF_VDDS_REL);
S_adcCtx.adcOffset = NOROM_AUXADCGetAdjustmentOffset (AUXADC_REF_VDDS_REL);

//清除中断标志
HWREG (AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR)|=(AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_IRQ | AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_DONE);
HwiP_clearInterrupt (INT_AUX_ADC_IRQ);

// IntRegister()可用于注册 ISR、但使用它会干扰操作系统管理的中断。
//因此、我们避免使用它、而是直接将入口指针添加到向量表中。
uint32_t ui32value;
ui32value = HWREG (NVIC_vtable);
HWREG (ui32value +(INT_AUX_ADC_IRQ * 4))=(uint32_t) ADC_FULL_ISR;//将矢量数乘以地址大小(4字节)以移动指针

//启用中断
IntPrioritySet (INT_AUX_ADC_IRQ、INT_PRI_LEVEL0);
IntEnable (INT_AUX_ADC_IRQ);
}

void ADC_COMPLET_ISR (void)

//从 FIFO 检索数据
S_adcMem[s_adcCtx.convertIndex]= HWREG (AUX_ANAIF_BASE + AUX_ANAIF_O_ADCFIFO);

S_adcCtx.singleChannelFlag = true;

//对 ADC 输入值执行校正、因为它们是按比例缩放的。
//驱动程序提供了一个用于更正的 API、但为了最大限度地减少开销、我们直接执行计算。
S_adcMem[s_adcCtx.convertIndex]=(int16_t)(((( int32_t) s_adcMem[s_adcCtx.convertIndex]+ s_adcCtx.adcOffset)* s_adcCtx.adcGain)+ 16384)/ 32768);

if (s_adcMem[s_adcCtx.convertIndex]< 0){
S_adcMem[s_adcCtx.convertIndex]= 0;
}如果(s_adcMem[s_adcCtx.convertIndex]> 4095){、则为"其他"
S_adcMem[s_adcCtx.convertIndex]= 4095;
}

if (s_adcCtx.state == ADC_STATE_QUADE_CHANNEL_STAGE_2 && s_adcCtx.convertIndex == 4){
// index4的 ADC 转换完成中断(电池电压)。
//由于不需要连续转换、因此将 convertIndex 初始化为0以防止进一步转换。
S_adcCtx.convertIndex = 0;
}其他{
s_adcCtx.convertIndex =(s_adcCtx.convertIndex + 1)% s_adcCtx.convertCount;
}

if (s_adcCtx.state!= ADC_STATE_SINGLE_CHANNEL && s_adcCtx.convertIndex == 0){
//一旦完成一个完整的转换周期
S_adcCtx.stateSwitchPeriod++;
const ADC_CONV_TABLE_t* adcTable =&s_adcStateTable[(s_adcCtx.state - 1)& 0x03];
if(s_adcCtx.stateSwitchPeriod >= adcTable->maxPeriod){
s_adcCtx.state = adcTable->nextConvertState;
s_adcCtx.convertCount = adcTable->convertCount;
S_adcCtx.stateSwitchPeriod = 0;
}
}

if (s_adcCtx.state == ADC_STATE_QUADE_CHANNEL_STAGE_2 && s_adcCtx.convertIndex == 3){
//跳过 index3 (温度传感器)的 ADC 转换并执行 index4 (电池电压)的转换。
S_adcCtx.convertIndex = 4;
}

//选择 ADC 输入通道
p_hard_api->SelectADCCompBInput (s_adcConvertChannel[s_adcCtx.convertIndex]);

if (s_adcCtx.convertIndex)

//生成手动触发器
HWREG (AUX_ANAIF_BASE + AUX_ANAIF_O_ADCTRIG)= 0;
}

//清除中断标志
HWREG (AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR)|=(AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_IRQ | AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_DONE);
HwiP_clearInterrupt (INT_AUX_ADC_IRQ);
}

感谢您的支持。

此致、

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    您应该始终如此 在 ISR 的早期清除中断标志 以免丢失在 ISR 运行时触发的可能中断。

    当中断标志清除在函数的开头时、会连续重新进入 ISR、而当中断标志清除在函数末尾时、ISR 是固定的、这可能是因为在 ISR 中登录时手动触发 ADC 中断。

    我看到在 adc_complete_isr函数即将结束时、您可以通过执行手动触发 ADC 中断 HWREG (AUX_ANAIF_BASE + AUX_ANAIF_O_ADCTRIG)= 0 当 s_adcCtx.convertIndex 不为0时。 ISR 中的此条件是否始终为真、从而始终被重新触发?  

    检查是否出现这种情况的一种方法是尝试在手动触发条件之前清除中断标志、然后在出现这种情况后尝试清除中断标志。 如果您在预期中看到行为发生变化。

    简而言之、尝试adc_complete_isr将您的结束代码更改为

    if(s_adcCtx.convertIndex)
    {
        // Generate manual trigger
        HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCTRIG) = 0;
    }

    // Clear interrupt flags
    HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR) |= (AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_IRQ | AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_DONE );
    HwiP_clearInterrupt(INT_AUX_ADC_IRQ);

    然后尝试将其更改为

    // Clear interrupt flags
    HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR) |= (AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_IRQ | AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_DONE );
    HwiP_clearInterrupt(INT_AUX_ADC_IRQ);

    if(s_adcCtx.convertIndex)
    {
        // Generate manual trigger
        HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCTRIG) = 0;
    }

    并查看它是否会改变行为。

    此致、
    Maxence

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Maxence、

    感谢您的支持,感谢您发送编修。

    根据您的建议、我们如下方式修改了代码、并移动了中断标志清除操作 解决方案 手动触发:

    //清除中断标志
    HWREG (AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR)|=
    (AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_IRQ | AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_DONE);
    HwiP_clearInterrupt (INT_AUX_ADC_IRQ);

    if (s_adcCtx.convertIndex)

    //生成手动触发器
    HWREG (AUX_ANAIF_BASE + AUX_ANAIF_O_ADCTRIG)= 0;
    }

    行为保持不变—系统的行为 亮起 实际上是这个修改。

    我们尝试 ISR 中的其他逻辑块中将中断标志清除移动到较早的位置、但是 我们标志清除移动 ISR 的起始位置时 我们观察到重复的 ISR 触发行为。

    如果可能发生的原因有任何进一步的建议或见解、我们将非常感谢您提供的建议。

    此致、
    Hiroki

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、

    所以,如果我理解正确,这个代码的工作正常:

    void adc_complete_isr(void)
    {
        // Retrieve data from the FIFO
        s_adcMem[s_adcCtx.convertIndex] = HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCFIFO);
        
        // Clear interrupt flags
        HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR) |= (AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_IRQ | AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_DONE );
        HwiP_clearInterrupt(INT_AUX_ADC_IRQ);
        
        s_adcCtx.singleChannelFlag = true;
    
        ...
    }

    但此代码在 ISR 中循环:

    void adc_complete_isr(void)
    {
    
    // Clear interrupt flags
    HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR) |= (AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_IRQ | AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_ADC_DONE );
    HwiP_clearInterrupt(INT_AUX_ADC_IRQ);
    
    
    // Retrieve data from the FIFO
    s_adcMem[s_adcCtx.convertIndex] = HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCFIFO);
    
    s_adcCtx.singleChannelFlag = true;
    
    ...
    }

    我是否理解正确?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、 Maxence、

    是的、您的理解是正确的。
    感谢您的确认。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、

    我很难找到问题所在。 我看到一些人使用此代码清除了他们的中断:

    interruptStatus = HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGS) &
                            (AUX_EVCTL_EVTOMCUFLAGS_ADC_IRQ |
                             AUX_EVCTL_EVTOMCUFLAGS_ADC_DONE);
    
    /* Clear the ADC_IRQ flag in AUX_EVTCTL */
    HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR) = interruptStatus;
    
    /* Clear the ADC_IRQ within the NVIC as AUX_EVTCTL will only set the
     * relevant flag in the NVIC it will not clear it.
     */
    HwiP_clearInterrupt(INT_AUX_ADC_IRQ);

    它与您的略有不同、因为它不是一个|=运算、而是一个&=运算。 也许这可能是问题所在。

    可以尝试一下吗?

    您是否还可以尝试在 ISR 的末尾放置一个断点以查看是否到达该断点?

    此致、
    Maxence

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    感谢您的建议。

    通过将以下代码添加 ISR 的开头和结尾处对其进行了测试:

    c.
    uint32_t interruptStatus = HWREG (AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGS)和
    (AUX_EVCTL_EVTOMCUFLAGS_AUX_ADC_IRQ | AUX_EVCTL_EVTOMCUFLAGS_AUX_ADC_DONE);
    HWREG (AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR)=中断状态;
    HwiP_clearInterrupt (INT_AUX_ADC_IRQ);

    以下AUX_EVCTL每种情况下的寄存器值结果:

    中添加代码时 开始 其中 ISR→ISR连续触发的。
     (ISR 开始)

      (ISR 端)

    中添加代码时 结束 对于 ISR→、ISR预期定期触发。
       (ISR 开始)

      (ISR 端)

    ①的情况下、AUX_EVCTL的寄存器值AUX_ADC_IRQ清除(保持为1)
    如果是②、则正确 清除0 接地平面。

    如果看一看告诉您的想法、我将不胜感激。

    此致、

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、

    在深入研究之后、我对在 ISR 中清除标志的位置有误、如果在 F2 SDK 上查看 ADC 驱动程序的源代码(ADCCC26XX_convertChain在的函数C:\ti\simplelink_cc13xx_cc26xx_sdk_8_30_01_01\source\ti\drivers\adc\ADCCC26XX.c)、中断在 ISR 中被清除。

    这样做的原因是、您需要首先从 ADC FIFO 栈中检索所需的所有数据、将这些数据存储在变量中、然后清除中断、否则其他中断可能会覆盖 FIFO 栈、导致您无法获取所需的数据。

    我怀疑 ADC 通过在读取 FIFO 后重新启用 ADC_IRQ 和 ADC_DONE 标志来防止发生该错误、从而强制您在之后而不是之前清除标志。

    在您的情况下、我建议您将中断清除代码放在末尾、就像您之前所做的那样。

    此致、
    Maxence

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    非常感谢您的解释。
    现在明白了读取 FIFO 后应清除中断标志。

    关于实际行为、确认一点:
    读取 FIFO 时、硬件是否会自动重新设置ADC_IRQ标志?

    本例中、 ISR 开始时清除中断标志时、观察到一个类似如下的循环:

    1. 进入 ISR

    2. 清除中断标志

    3. FIFO读取

    4. 硬件重置 ADC_IRQ

    5. ISR 再次被触发

    我的理解是否正确?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    尽管 TRM 中有关 ADC 的文档(链接)没有提到您观察到的行为、但 我假设您是对的、理解是正确的。 我无法访问 ADC 的内部逻辑、但根据您遇到的情况、读取 FIFO 会设置 ADC IRQ 标志。

    此致、
    Maxence

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Maxence
    非常感谢您的支持。 问题您的指导下得到解决。 非常感谢您的及时有用的回答。