STM32F30x 禁止 ADC 已关闭情况下再次关闭 ADC 前言 STM32F30x 系列的 12 位 SAR ADC 有很多鲜明的特色性能, 比如采样率可以达到 5 MSPS, 可支持差分输入, 等等 但是, 由于设计的不同, 在使用上也有不少不太一样的地方, 我们在使用 STM32F30x 的 ADC 外设的时候, 还是要仔细了解一些使用的细节 问题 某客户在其产品的设计中, 使用了 STM32F302CCT6 客户在使用过程发现 ADC 在工作情况下会有各种各样奇奇怪怪的问题 调研 1. 了解问题客户在开发中使用了 STM32F30x 的标准外设库 STM32F30x_DSP_StdPeriph_Lib_V1.2.3, 在其程序设计中,ADC1 是在使用的时候才打开的, 在不使用的时候将 ADC1 关闭 通过调用 void ADC_DisableCmd(ADC_TypeDef* ADCx) 子程序, 执行 ADC_DisableCmd(ADC1) 将 ADC1 关闭 仔细察看程序, 发现程序中在 ADC1 的 打开 关闭 打开 关闭 循环的关闭中, 执行了两次 ADC_DisableCmd(ADC1) 2. 问题分析 通过学习 STM32F30x 的参考手册, 可以知道 STM32F30x 在对 ADC 进行关闭的操作与其他系列是不一样的 ; 其他系列只要 将 ADON 位清零就可以停止转换并使 ADC 进入掉电模式, 而 STM32F30x 则不一样, 它是通过置位 ADDIS 位来关闭 ADC 的 在 void ADC_DisableCmd(ADC_TypeDef* ADCx) 程序中也可以看到这一点 void ADC_DisableCmd(ADC_TypeDef* ADCx) /* Check the parameters */ assert_param(is_adc_all_periph(adcx)); /* Set the ADDIS bit */ ADCx->CR = ADC_CR_ADDIS; 但是, 问题来了,ADDIS 位是在什么情况下都可以置位的吗? 我们来看一下参考手册中对关闭 ADC 的软件流程的描述 :
从描述中, 我们可以知道 : 在置位 ADDIS 之前, 必须先检测 ADSTART 位和 JADSTART 位, 确保他们为零, 也就是说没有 正在进行的 A/D 转换 在 ADC Control register - ADCx_CR 寄存器中对 ADDIS 的描述也注明了 : 关于这一点, 大多数人都是会注意到的 但是, 注意这个就够了吗? 关于 ADC 的控制位, 在参考手册特别使用一个小节对向 控制位写访问的限制进行详细描述, 此小节为 Contraints when writing the ADC control bits, 在这一小节中, 有一句话值 得注意 : 注意这里的用词 only if, 它的意思是 只有在 ADC 是打开状态, 而且没有正在等待的关闭 ADC 的请求的情况下, 也就是在 ADEN=1 且 ADDIS=0 的情况下, 才允许软件对 ADCx_CR 寄存器中的 ADSTART,JADSTART 和 ADDIS 位进行操作 在底 下的 Note 注意中写道 : 这个注意说, 这些禁止的 ADC 写访问行为是没有硬件保护去禁止的, 错误的操作行为将导致 ADC 进入一个未知的状态 要 恢复这种状态, 必须将 ADC 彻底关闭 ( 将 ADCx_CR 中的所有位全清零 ) 所以, 现在可以知道,ADC1 工作不正常的原因正是因为连续执行了两次 ADC_DisableCmd(ADC1) 第一次执行 ADC_DisableCmd(ADC1) 时, 当 ADC1 已经有效关闭时,ADEN 和 ADDIS 都被硬件清零, 这个时候第二次再去写 ADDIS 位 就是个错误的行为了, 将会导致 ADC1 进入未知状态 3. 问题解决在解决问题之前, 先来看一下 STM32Cube_FW_F3_V1.2.0 库中对 ADC 进行关闭的操作 打开 stm32f3xx_hal_adc_ex.c 文件, 找到 static HAL_StatusTypeDef ADC_Disable(ADC_HandleTypeDef* hadc) 函数, 其程序内容为 :
static HAL_StatusTypeDef ADC_Disable(ADC_HandleTypeDef* hadc) uint32_t tickstart = 0; /* Verification if ADC is not already disabled: */ /* Note: forbidden to disable ADC (set bit ADC_CR_ADDIS) if ADC is already */ /* disabled. */ if ( HAL_ADC_IS_ENABLED(hadc)!= RESET ) /* Check if conditions to disable the ADC are fulfilled */ if ( HAL_ADC_DISABLING_CONDITIONS(hadc)!= RESET) /* Disable the ADC peripheral */ HAL_ADC_DISABLE(hadc); else /* Update ADC state machine to error */ hadc->state = HAL_ADC_STATE_ERROR; /* Set ADC error code to ADC IP internal error */ hadc->errorcode = HAL_ADC_ERROR_INTERNAL; return HAL_ERROR; /* Wait for ADC effectively disabled */ tickstart = HAL_GetTick(); while(hal_is_bit_set(hadc->instance->cr, ADC_CR_ADEN)) if((hal_gettick()-tickstart) > ADC_DISABLE_TIMEOUT) /* Update ADC state machine to error */ hadc->state = HAL_ADC_STATE_ERROR; /* Set ADC error code to ADC IP internal error */ hadc->errorcode = HAL_ADC_ERROR_INTERNAL; return HAL_ERROR; /* Return HAL status */ return HAL_OK;
在这个函数注释中有个 Note: forbidden to disable ADC (set bit ADC_CR_ADDIS) if ADC is already disabled., 再次告诉 我们 禁止在 ADC 已经被关闭的情况下再次关闭 ADC 然后, 程序在运行中先对 ADC 是否已经被关闭进行了判断, 如果 已经被关闭, 则不进行关闭操作 ; 未被关闭情况下才会执行关闭操作 所以, 在使用标准外设库的时候, 我们也可以参考 Cube 库中的这种操作来进行改善 可以考虑对 void ADC_DisableCmd(ADC_TypeDef* ADCx) 程序进行修改 : void ADC_DisableCmd(ADC_TypeDef* ADCx) /* Check the parameters */ assert_param(is_adc_all_periph(adcx)); if (((ADCx->CR & (ADC_CR_ADEN ADC_CR_ADDIS)) == ADC_CR_ADEN) && ((ADCx->ISR & ADC_FLAG_RDY) == ADC_FLAG_RDY)) if (( ADCx->CR & (ADC_CR_JADSTART ADC_CR_ADSTART ADC_CR_ADEN)) == ADC_CR_ADEN) /* Set the ADDIS bit */ ADCx->CR = ADC_CR_ADDIS; /* Clear ADC_FLAG_EOSMP and ADC_FLAG_RDY flags */ ADCx->ISR = (uint32_t)adc_flag_eosmp; ADCx->ISR = (uint32_t)adc_flag_rdy; /* Wait for ADC effectively disabled */ while ((ADCx->CR & ADC_CR_ADEN) == ADC_CR_ADEN) 当然, 不动 void ADC_DisableCmd(ADC_TypeDef* ADCx) 程序也可以, 只需在用户程序中对这些限制进行判断即可 上面的 while 循环中, 也可以加入超时退出机制 结论 连续两次对 ADDIS 控制位进行写 1, 是错误的操作行为, 将会导致 ADC 进入未知状态, 工作不正常 处理 修改程序, 避免错误的操作行为 建议 在使用 STM32F30x 系列的 ADC 外设时, 必须要对控制位的操作限制有明确的了解 在 STM32F30x 的 快速 ADC 模块 培训资料中, 也有对此进行描述, 如下 :
最重要还是要看参考手册, 多注意一些小细节
重要通知 - 请仔细阅读 意法半导体公司及其子公司 ( ST ) 保留随时对 ST 产品和 / 或本文档进行变更 更正 增强 修改和改进的权利, 恕不另行通知 买方在订货之前应获取关于 ST 产品的最新信息 ST 产品的销售依照订单确认时的相关 ST 销售条款 买方自行负责对 ST 产品的选择和使用, ST 概不承担与应用协助或买方产品设计相关的任何责任 ST 不对任何知识产权进行任何明示或默示的授权或许可 转售的 ST 产品如有不同于此处提供的信息的规定, 将导致 ST 针对该产品授予的任何保证失效 ST 和 ST 徽标是 ST 的商标 所有其他产品或服务名称均为其各自所有者的财产 本文档中的信息取代本文档所有早期版本中提供的信息 2015 STMicroelectronics - 保留所有权利