stm32学习日志-ADC单通道模拟电压信号转离散数字量
什么是 ADC为什么 MCU 需要 ADC答ADC就是模拟数字转换器是STM32芯片上的一个外设因为外界信号并不只有高低电平两种状态而是相对连续的介于二者之间的值但是普通引脚输入只有高低电平所以MCU需要ADC感知这些介于二者之间的值将从引脚输入的信号转换成连续的数字。概念模拟电压信号离散数字量采样量化数字编码数字值纠正ADCAnalog-to-Digital Converter是模拟数字转换器是 MCU 中用于采集模拟信号的外设。现实中的很多传感器输出的是连续变化的模拟电压例如温度、光照、压力等而 GPIO 普通数字输入只能识别高低电平无法获取具体电压值。因此 MCU 需要通过 ADC 对模拟输入电压进行采样和量化将其转换为离散的数字量供 CPU 进行处理。ADC 的分辨率是什么意思12 位 ADC 为什么能够表示 04095 的数字值答ADC的分辨率就是ADC对模拟电压信号进行采样加量化后的离散数字量的变化范围ADC的分辨率越高对模拟电压信号的感知越精确。2的12次方刚好等于4096所以可以表示4096个数字所以可以表示0到4095的数字值。概念量化等级范围ADC的分辨率ADC 位数纠正ADC 分辨率表示 ADC 对输入模拟电压的最小分辨能力通常由 ADC 位数决定。位数越高量化等级越多对输入电压的划分越细能够检测到更小的电压变化。12 位 ADC 表示输出数字量有 12 位因此共有 $2^{12}4096$ 个量化等级对应数字范围为 04095。ADC 的参考电压 Vref、分辨率和 ADC 转换结果之间有什么关系如何根据 ADC 数值计算实际输入电压答ADC对模拟电压信号的转化结果就是离散数字量分辨率3.3V/(量化等级范围由ADC位数决定)ADC 的参考电压 Vref分辨率*ADC 转换结果ADC 的参考电压 Vref就是实际输入电压的估计值。概念参考电压Verf量化等级范围ADC 位数ADC转换结果纠正ADC 的参考电压 Vref 决定 ADC 的输入测量范围例如 Vref3.3V 时ADC 可以测量 03.3V 的输入电压。ADC 位数决定量化等级例如 12 位 ADC 有 4096个等级。ADC 转换结果表示输入电压在这些等级中的位置分辨率参考电压Verf/(量化等级范围-1)因此实际输入电压可以通过 ADC转换结果*ADC分辨率 计算得到。ADC 采样时间是什么意思为什么采样时间不能设置得太短答ADC采样时间是指ADC对模拟电压信号采样使用了多长时间如果ADC采样时间过短那么因噪声的干扰导致这段时间内电压偏高那么ADC转换结果就会偏高不符合实际。概念采样保持电路输入电压 采样电容保存电压ADC量化转换输出阻抗纠正ADC采样时间表示 ADC 对输入模拟信号进行采集的时间。在采样阶段ADC内部的采样电容需要通过输入信号进行充电使电容电压接近输入电压。如果采样时间过短采样电容无法充分充电导致采样电压与实际输入电压存在偏差从而造成 ADC 转换结果误差。因此对于输入阻抗较大的信号需要设置更长的采样时间。ADC 转换完成后CPU 如何知道转换结束ADC 查询、中断和 DMA 三种方式有什么区别答ADC转化完成后可以通过给模拟看门狗针对AD数据寄存器设置一个检测范围这样当ADC转换结果数据超出一定范围后就会发出中断请求到NVIC,再到CPU好吧这个我真不知道。ADC查询和DMA这两种方式不需要CPU频繁参与数据读取相比于中断更加节省CPU资源;DMA方式一次性可以获取多个值。概念EOC标志位置1EOC纠正ADC转换完成后通常通过转换完成标志EOC通知CPU。ADC查询方式中CPU不断读取EOC标志位等待转换完成后读取数据ADC中断方式中ADC转换完成后产生中断请求通过NVIC通知CPU执行中断服务函数读取数据DMA方式中ADC转换后的数据由DMA自动搬运到内存CPU无需参与每次数据读取只需要在数据达到一定数量后进行处理。三种方式中查询方式CPU占用最高中断方式适合低频采样DMA适合大量连续数据采集。讲讲ADC独立模式答独立模式本质上是两个片上的ADC具有独立关系在独立模式下ADC可以选择不同的触发源也能使用不同的DMA通道读取数据。概念输入通道触发方式转换流程纠正ADC 独立模式是指 MCU 中多个 ADC 外设之间相互独立工作每个 ADC 可以单独配置自己的输入通道、触发方式和转换流程不需要与其他 ADC 同步。例如 ADC1 和 ADC2 可以分别采集不同传感器的数据各自完成采样、转换和数据读取。相比之下双 ADC 模式会让多个 ADC 按照一定方式协同工作用于提高采样速度或者实现同步采样。左对齐和右对齐区别答左对齐就是从左往右依次往寄存器中填入数字右对齐就是从右往左依次往寄存器中填入数字前者的值会比后者大。纠正左对齐和右对齐是ADC 转换结果在数据寄存器中的存储位置。右对齐时ADC结果存放在低位高位补0读取后可以直接作为ADC数值使用左对齐时ADC结果存放在高位低位补0读取时通常需要进行移位处理。两种方式不会改变ADC转换结果只影响数据读取方式。通道号、Rank、采样时间三者关系答通道号就是输入通道的编号Rank意思是排号采样时间影响估计模拟电压信号的值的准确度三者的关系是不同通道具有不同的Rank值Rank值越高采样时间越长对模拟电压信号的值的估计越准确。概念ADC 多通道转换序列中的位置编号Rank纠正ADC通道号表示 ADC 输入的模拟通道用于指定采集哪个 GPIO 上的模拟信号Rank 表示多个通道转换时的先后顺序ADC 会按照 Rank 从小到大的顺序依次转换采样时间表示 ADC 对某个通道进行采样的持续时间影响采样电容充电是否充分从而影响转换精度。三者关系是ADC 根据 Rank 确定转换顺序每次转换时选择对应 Channel并按照该 Channel 配置的采样时间完成采样。采样时间为什么影响转换结果答因为当模拟电压信号输入到ADC时首先会给采样电容充电然后才会将模拟电压信号转换为离散数字量如果采样时间过短那么采样电容充电不充分则转换结果将偏低。ADC为什么需要校准答ADC的输入通道是GPIO引脚的功能复用由于GPIO引脚所处的物理环境导致引脚带有电荷不是理想的电势所以需要校准。纠正ADC需要校准是因为ADC内部模拟电路存在非理想误差例如零点偏移和增益误差导致转换结果与实际输入电压存在偏差。校准过程可以让ADC测量并补偿自身误差提高转换结果的准确性。在STM32中通常在ADC初始化后、开始转换前执行校准操作。如何使用 STM32 的 ADC 读取一个模拟传感器电压请描述完整的软件实现流程。答梳理事件链模拟电压信号输入到GPIOA引脚ADC输入通道对模型电压信号采样量化ADC将设置EOC标志CPU读取并判断EOC标志;CPU读取ADC数据寄存器中的值。代码实现流程这里先使用查询方式读取数据启动GPIOA与ADC外设的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);初始化GPIOA配置GPIOA为模拟输入模式GPIO_InitStruct.GPIO_Pin GPIO_Pin_0; // 比如PA0 GPIO_InitStruct.GPIO_Mode GPIO_Mode_AIN; // 模拟输入重点 // 模拟输入模式下GPIO_Speed 不生效可以随便写 GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStruct);// 3. 调用初始化函数初始化ADC配置ADC工作模式{独立模式双ADC模式}数据对齐方式{左对齐右对齐}通道配置{ADC通道号Rank顺序采样时间}ADC校准ADC_InitTypeDef ADC_InitStruct; ADC_InitStruct.ADC_ScanConvMode DISABLE; ADC_InitStruct.ADC_ContinuousConvMode DISABLE; ADC_InitStruct.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStruct.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStruct.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStruct);通道配置{ADC通道号Rank顺序采样时间}// 函数原型 void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime); 参数 ADCxADC1 / ADC2 ADC_Channel通道号如 ADC_Channel_0PA0 Rank1~16规则序列顺序 ADC_SampleTim采样时间例如 ADC_SampleTime_55Cycles5设置ADC时钟频率RCC_ADCCLKConfig(RCC_PCLK2_Div6);使能ADC外设ADC_Cmd(ADC1, ENABLE);ADC校准ADC_ResetCalibration(ADC1); // 复位校准 while(ADC_GetResetCalibrationStatus(ADC1)); // 等待复位校准完成 ADC_StartCalibration(ADC1); // 启动校准 while(ADC_GetCalibrationStatus(ADC1)); // 等待校准完成启动ADC转换ADC_SoftwareStartConvCmd(ADC1, ENABLE);CPU读取并判断EOC标志在合适条件下CPU读取ADC数据寄存器中的值。FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint16_t ADC_FLAG); // 等待EOC直到转换完成 while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) RESET); // 读取ADC数据 uint16_t adc_val ADC_GetConversionValue(ADC1); // 清除EOC标志F1读DR之后硬件自动清除也可以手动清 ADC_ClearFlag(ADC1, ADC_FLAG_EOC);E代码一直卡在了while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) RESET); 为什么没有设置EOC标志位置为1流程模拟电压信号输入到GPIOA引脚ADC输入通道对模型电压信号采样量化ADC将设置EOC标志CPU读取并判断EOC标志;CPU读取ADC数据寄存器中的值。这个流程相对来说有点问题1.引脚GPIOA_pin0接在了ADC的通道0上这没问题所以问题出现在ADC输入通道对模型电压信号采样量化ADC将设置EOC标志这个步骤这个步骤对应于初始化ADC外设。ADC_InitStruct.ADC_ExternalTrigConv ADC_ExternalTrigConv_None;代表用软件触发依靠ADC_SoftwareStartConvCmd()函数启动转换不用定时器、外部引脚来触发 ADC。2.初始化ADC也排除了错误并在检测ADC_FLAG_EOC开启了软件触发其实这时候我应该问ADC初始化有几个步骤或者我是否忽略了与初始化ADC同级的某些步骤初始化ADC设置 ADC 模块工作的时钟频率使能ADC外设然后依次排查这些步骤