1. STM32F103C8T6 ADC开发环境搭建
STM32F103C8T6作为STMicroelectronics推出的经典Cortex-M3内核微控制器,其ADC模块在嵌入式硬件开发中扮演着重要角色。这款芯片内置12位逐次逼近型ADC,采样率最高可达1MHz,支持多达16个外部通道和2个内部通道(温度传感器和VREFINT)。
1.1 硬件准备要点
开发板选择上,市面上常见的"蓝色药丸"开发板(Blue Pill)就是基于STM32F103C8T6的方案,价格低廉且资源丰富。硬件连接时需要注意:
- ADC输入引脚需根据数据手册选择,例如PA0~PA7对应ADC1的通道0~7
- 模拟信号输入范围严格限定在0~3.3V,超出可能损坏芯片
- 对于高阻抗信号源,建议在输入端增加RC滤波(如1kΩ电阻串联100nF电容到地)
重要提示:调试阶段务必在ADC输入引脚与信号源之间串联100Ω限流电阻,防止误操作导致芯片损坏。
1.2 软件工具链配置
推荐使用STM32CubeIDE作为开发环境,它集成了STM32CubeMX配置工具和Eclipse开发环境。具体配置步骤:
- 新建工程时选择STM32F103C8系列
- 在Pinout & Configuration界面启用ADC1模块
- 配置ADC参数:
- Resolution: 12位
- Scan Conversion Mode: Enabled(多通道扫描)
- Continuous Conversion Mode: Disabled(单次转换)
- DMA Continuous Requests: Enabled(DMA连续传输)
- 生成代码前勾选"Generate peripheral initialization as a pair of .c/.h files"
2. ADC模块配置详解
2.1 时钟树配置原理
代码中的SystemClock_Config()函数设置了72MHz主频,这是STM32F103的典型工作频率。ADC时钟由APB2总线分频得到,示例中配置为RCC_ADCPCLK2_DIV6,即12MHz(72MHz/6)。根据数据手册,ADC时钟不应超过14MHz。
时钟配置关键参数:
c复制RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 8MHz HSE * 9 = 72MHz
PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6; // ADC时钟=72MHz/6=12MHz
2.2 ADC初始化参数解析
MX_ADC1_Init()函数(由CubeMX生成)包含关键配置:
c复制hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 3; // 3个转换通道
多通道采样时,还需要配置ADC通道的采样顺序和时间:
c复制sConfig.Channel = ADC_CHANNEL_0; // 通道0(PA0)
sConfig.Rank = 1; // 转换序列中的第1个
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5; // 采样时间71.5周期
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
采样时间计算:71.5个ADC时钟周期@12MHz ≈ 5.96μs,加上12.5个周期的固定转换时间,总转换时间约18.46μs(54.1kSPS)。
3. DMA传输实现技巧
3.1 DMA缓冲配置
示例代码中使用uint16_t value[3]数组存储ADC结果,这是典型的多通道采样方案。DMA配置要点:
- 内存地址递增(每个通道结果存入不同位置)
- 外设地址固定(ADC数据寄存器地址不变)
- 数据宽度为半字(16位)
CubeMX生成的DMA配置:
c复制hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
3.2 中断回调实战
HAL_ADC_ConvCpltCallback是转换完成回调函数,这里将三个通道的值通过串口发送:
c复制void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){
if(hadc == &hadc1){
sprintf(message, "%d %d %d", value[0], value[1], value[2]);
HAL_UART_Transmit(&huart1, (uint8_t*)message, strlen(message), 100);
}
}
经验分享:在实时性要求高的场景,建议直接在DMA传输完成中断中处理数据,避免使用sprintf等耗时函数。可以预先定义好通信协议,直接发送原始二进制数据。
4. 主循环优化策略
示例中的主循环每500ms启动一次ADC转换:
c复制while(1){
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)value, 3);
HAL_Delay(500);
}
4.1 定时触发方案
更专业的做法是使用定时器触发ADC采样,实现精确的采样间隔。修改步骤:
- 配置TIM2为1kHz触发频率(72MHz/(719+1)/(99+1) = 1kHz)
- 修改ADC触发源为TIM2_TRGO
- 移除主循环中的手动启动代码
4.2 低功耗优化
持续采样时功耗较高,可以:
- 降低采样率
- 在两次转换间进入STOP模式
- 使用ADC的间断模式(Discontinuous mode)
5. 常见问题排查指南
5.1 ADC读数不稳定
可能原因及解决方案:
- 电源噪声:在VDDA和VSSA引脚就近放置1μF+100nF去耦电容
- 信号源阻抗过高:增加电压跟随器或降低采样速率
- 接地不良:确保模拟地和数字地单点连接
5.2 DMA传输异常
典型故障现象及处理:
- 数据错位:检查DMA的MemInc和PeriphInc配置
- 传输不完整:确认DMA缓冲区大小匹配NbrOfConversion
- 数据覆盖:启用DMA双缓冲模式
5.3 校准失败处理
ADC校准是精度保证的关键:
c复制HAL_ADCEx_Calibration_Start(&hadc1); // 执行校准
if(HAL_ADC_GetState(&hadc1) == HAL_ADC_STATE_ERROR){
// 校准失败处理
}
校准前需确保:
- ADC时钟稳定
- 供电电压在2.4V~3.6V范围
- 芯片温度在-40°C~+85°C工作范围内
6. 进阶应用实例
6.1 内部温度传感器使用
STM32F103内置温度传感器连接在ADC1通道16:
c复制sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; // 需要更长采样时间
温度计算公式:
code复制温度(°C) = ((V25 - Vsense)/Avg_Slope) + 25
其中:
V25 = 1.43V @25°C
Avg_Slope = 4.3mV/°C
6.2 多ADC同步采样
STM32F103支持ADC1和ADC2同步采样(需要注入通道):
- 配置ADC1为主模式,ADC2为从模式
- 使用ADC1触发ADC2
- 同步读取两个ADC的数据寄存器
我在实际项目中发现,当需要同时采集电压和电流信号时,这种同步采样方式能有效避免相位差带来的计算误差。
