ARM裸机学习9——ADC模块详解与应用实践

张开发
2026/4/8 17:04:47 15 分钟阅读

分享文章

ARM裸机学习9——ADC模块详解与应用实践
目录1. ADC基础概念2. i.MX6ULL ADC核心特性3. 关键寄存器解析3.1 控制寄存器ADCx_HC03.2 配置寄存器ADCx_CFG3.3 通用控制寄存器ADCx_GC4. ADC模块函数开发流程1. 初始化函数 (adc1_init)​2. 校准函数 (adc1_calibration)​3. 数据采集函数 (get_adc_value)​4. 滤波优化 (get_average_value)​5. 关键问题与优化​6.结果与分析5. 常见问题与优化6、核心要点摘要本文基于NXP i.MX6ULL处理器的参考手册及开发实践深入解析其内置ADC模块的架构特性、寄存器配置及驱动开发要点并提供关键代码实现参考。1. ADC基础概念ADC概念ADC模拟-数字转换器将连续模拟信号如电压转换为离散数字信号。i.MX6ULL采用逐次逼近型SAR架构通过二分法逐步逼近输入电压值。其分辨率可选8/10/12位转换结果范围对应0~255、0~1023或0~4095。ADC的分辨率指adc的位数。常见的分辨率有8位10位12位16位基准电压是ADC进行转换的参考标准如3.3V, 5v,可以理解为量程ADC的工作原理采用逐次逼近法将输入的模拟电压值与基准电压进行比较从而确定对应的数字值。具体步骤为Ⅰ.输入将连续模拟信号输入ADCⅡ.采样与保持 : 在特定时刻捕获并保持电压值Ⅲ.量化: 将电压值归入最接近的离散电平Ⅳ.编码: 将离散电平转换为二进制数字代码Ⅵ.输出结果输出数字信号输出结果vadc采样值/2^(adc位数)*参考电压2. i.MX6ULL ADC核心特性通道配置支持最多16个单端外部模拟输入通道如ADC1_IN0~ADC1_IN15复用至GPIO引脚如GPIO1_IO00~GPIO1_IO09。性能参数最高采样率1 MS/s每秒百万次采样精度最高10位有效位数ENOB内置硬件平均器与比较器支持自动校准。时钟源支持ipg_clk、ipg_clk/2及专用低功耗时钟ADACK默认20MHz。3. 关键寄存器解析3.1 控制寄存器ADCx_HC0通道选择ADCH位写入通道号0~15启动单次转换切换通道会触发新转换。中断使能AIEN位置1时转换完成触发中断。3.2 配置寄存器ADCx_CFG时钟分频ADIV位可选1/2/4/8分频确保ADCK频率在2.5~30 MHz范围内。采样时间配置ADLSMPADSTS位组合控制采样周期2~24个ADCK周期适应不同阻抗信号源。3.3 通用控制寄存器ADCx_GC校准使能CAL位置1启动校准复位后必须执行以保证精度。硬件平均使能AVGE位启用多采样值自动平均。4. ADC模块函数开发流程i.MX6ULL的ADC采用逐次逼近型SAR架构支持8/10/12位分辨率需重点关注初始化、校准、数据采集及滤波设计。开发例程分为四个核心阶段1.初始化函数 (adc1_init)​初始化是ADC工作的前提需配置引脚模式、时钟源和寄存器引脚配置将GPIO设为模拟模式如GPIO1_IO01。代码中IOMUXC_SetPinMux和IOMUXC_SetPinConfig设置复用功能与电气特性禁用上拉/下拉确保信号无干扰。寄存器设置ADC1-CFG配置时钟分频ADIV位、采样时间ADLSMPADSTS位。例如代码中设置ADIV24分频和ADSTS3长采样模式适应高阻抗信号源。ADC1-GC使能ADCADCO1但校准前需关闭硬件平均和比较功能。关键点时钟源可选ipg_clk或ADACK默认20MHz需确保ADCK频率在2.5–30 MHz范围内。2.校准函数 (adc1_calibration)​校准是保证精度的必要步骤必须在复位后执行流程清除校准失败标志ADC1-GS | (1 1)。启动校准ADC1-GC | (1 7)等待CAL位清零代码中while循环。检查CALF标志若为0则成功代码返回1否则失败。注意事项校准期间禁止写入寄存器或进入低功耗模式否则会中止流程。校准耗时约14000 ADCK周期代码中未显式处理超时需添加超时判断。3.数据采集函数 (get_adc_value)​启动转换并读取结果启动转换向ADCx_HC0写入通道号代码中ADC1-HC[0] 0x01切换通道会触发新转换。等待完成轮询ADCx_HS的COCO位代码中while ((ADC1-HS (1 0)) 0)。读取结果从ADCx_R0读取12位数据ADC1-R[0] 0x0FFF。转换时间典型值约700 ns8位模式40MHz时钟受采样周期和时钟分频影响。4.滤波优化 (get_average_value)​裸机程序需软件滤波提升稳定性中值滤波采集100个样本 → 冒泡排序 → 剔除头尾20个极值。均值处理计算中间60个样本的平均值sum/60避免整数除法精度损失代码中转为浮点计算。电压转换最终结果按公式转换电压值 (ADC值 × 3.3V) / 4096。5.关键问题与优化​误差来源量化误差固定、引脚漏电流硬件布局优化、噪声干扰增加滤波。低功耗场景进入STOP模式前需清除数据阻塞标志避免意外唤醒系统。Linux驱动扩展需在设备树配置参考电压如vref-supply reg_3v3和通道映射。完整流程总结配置GPIO为模拟模式 → 2. 初始化ADC时钟/寄存器 → 3. 执行校准 → 4. 启动转换并读取原始值 → 5. 中值滤波均值处理 → 6. 转换为电压值输出。完整代码//main.c #include MCIMX6Y2.h #include fsl_iomuxc.h #include interrupt.h #include clock.h #include epit.h #include gpt.h #include uart.h #include stdio.h #include string.h #include adc.h int main(void) { clock_init(); system_interrupt_init(); led_init(); beep_init(); gpt1_init(); uart1_init(); adc1_init(); float value 0.0; while (1) { #if 0 value get_volt_value(); #endif value get_average_value(); int m (int)(value * 1000) / 1000; int n (int)(value * 1000) % 1000; printf(volt %d.%03d\n, m, n); //%03d 3位数不足3位前面补0,防止区域得85显示3.85而不是3.085 delay_ms(100); } return 0; }//adc.c #include adc.h #include MCIMX6Y2.h #include fsl_iomuxc.h #include stdio.h int adc1_calibration(void) { ADC1-GS | (1 1); ADC1-GC | (1 7); while ((ADC1-GC (1 7)) ! 0); return ((ADC1-GS (1 1)) 0); } void adc1_init(void) { //复用功能 IOMUXC_SetPinMux(IOMUXC_GPIO1_IO01_GPIO1_IO01, 0); //电气特性 IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01, IOMUXC_SW_PAD_CTL_PAD_PKE(1)); IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01, IOMUXC_SW_PAD_CTL_PAD_PUE(0)); //ADC ADC1-CFG 0; unsigned int t ADC1-CFG; t | (2 2); t | (3 0); ADC1-CFG t; ADC1-GC 0; ADC1-GC | (1 0); printf(adc1_calibration() ? Calibration completed normally. : Calibration failed.); } unsigned short get_adc_value(void) { ADC1-HC[0] 0x1F; ADC1-HC[0] 0x01; while ((ADC1-HS (1 0)) 0); return (unsigned short)(ADC1-R[0] 0x0FFF); } float get_volt_value(void) { return get_adc_value() * 3.3 / 4096; } float get_average_value(void) { unsigned int temp[100]; unsigned long sum 0; // 使用更宽的类型避免溢出 unsigned int temp_swap; // 用于交换的临时变量 int i 0; int j 0; // 初始化数组 for (i 0; i 100; i) { temp[i] get_adc_value(); } // 冒泡排序 for (i 0; i 99; i) { for (j 0; j 99 - i; j) { if (temp[j] temp[j 1]) // 大的往后排 { temp_swap temp[j]; temp[j] temp[j 1]; temp[j 1] temp_swap; } } } // 计算temp数组第20-第80个元素的平均值 (去掉了头尾各20个极值) for (i 20; i 80; i) { sum temp[i]; } // 直接使用浮点数计算避免整数除法精度损失 float average (float)sum / 60.0f; float value average * 3.3f / 4096.0f; return value; }6.结果与分析滤波方法本裸机程序采用中值滤波均值处理提升了结果的稳定性。补充常用的滤波方法5. 常见问题与优化在实际应用中如何选择ADC?(1)首先看量程:确保你要测量的信号电压范围在ADC的量程之内。如果信号太小需要先用运放放大;如果信号太大,需要用电阻分压。(2)然后看分辨率:根据你需要的测量精细度选择位数。例如测量锂电池电压(3.0V-4.2V)一个12位的ADC(有4096个等级)可能就足够了。(3)最后看精度:在对测量结果的绝对准确性要求极高的场合(如精密仪器、科学测量)必须仔细研究数据手册中的精度指标了偏移误差、增益误差、INL、DNL)而不能只看它的位数。高精度的ADC价格也更高。转换时间计算短时间配置8位模式40MHz时钟典型值约700 ns。误差来源量化误差、引脚漏电流、噪声干扰等需通过硬件布局与校准抑制。6、核心要点①ADC概念:模数转换器②基准电压是ADC进行转换的参考标准如3.3V, 5v,可以理解为量程③ADC的工作原理采用逐次逼近法将输入的模拟电压值与基准电压进行比较从而确定对应的数字值。具体步骤为Ⅰ.输入将连续模拟信号输入ADCⅡ.采样与保持 : 在特定时刻捕获并保持电压值Ⅲ.量化: 将电压值归入最接近的离散电平Ⅳ.编码: 将离散电平转换为二进制数字代码Ⅵ.输出结果输出数字信号④ADC的分辨率指adc的位数。常见的分辨率有8位10位12位16位⑤输出结果vadc采样值/2^(adc位数)*参考电压

更多文章