[STM32F1] 基于STM32F103的ADC应用与实现

[复制链接]
5270|57
 楼主| 万图 发表于 2024-10-11 08:10 | 显示全部楼层 |阅读模式

STM32F103是一款非常流行的32位微控制器,基于Cortex-M3内核,具备较强的处理能力和丰富的外设资源,广泛应用于工业控制、消费电子等领域。在嵌入式系统中,ADC(模拟数字转换器)功能非常重要,它可以将模拟信号(如温度、电压)转换为数字信号,供MCU进行处理。本篇文章将介绍如何在STM32F103中使用ADC,并通过示例代码展示如何采集模拟信号。

1. STM32F103硬件概述STM32F103拥有多达16通道的12位ADC,可以同时采集多个模拟信号。其ADC模块具备以下特点:
  • 12位分辨率,高精度采集模拟信号
  • 多通道支持,可以采集多个信号源
  • DMA支持,可以通过DMA方式自动采集数据,减少CPU负担
  • 转换速度快,适用于实时采集需求

2. 硬件准备
  • STM32F103开发板
  • 一个可调电位器(作为输入模拟信号)
  • 若干杜邦线

3. 开发环境
  • IDE: Keil MDK5
  • 库文件: STM32F1 HAL库

4. ADC配置步骤在STM32F103中使用ADC主要分为以下几个步骤:
  • GPIO配置:将模拟输入信号引脚配置为模拟模式
  • ADC初始化:设置ADC的分辨率、时钟、触发方式等参数
  • 采集数据:通过启动ADC并读取转换结果获取模拟信号的数字值
  • 处理数据:将采集到的数字信号进行处理或显示

5. 示例代码:使用STM32F103采集模拟信号下面的代码展示了如何使用STM32F103的ADC功能,读取一个电位器的电压值并通过串口发送到PC端。
  1. #include "stm32f10x.h"
  2. #include "stm32f10x_adc.h"
  3. #include "stm32f10x_gpio.h"
  4. #include "stm32f10x_usart.h"

  5. // ADC初始化
  6. void ADC_Configuration(void) {
  7.     GPIO_InitTypeDef GPIO_InitStructure;
  8.     ADC_InitTypeDef ADC_InitStructure;

  9.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);  // 开启ADC1和GPIOA时钟

  10.     // 配置PA0为模拟输入
  11.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  12.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;  // 设置为模拟输入模式
  13.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  14.     // 配置ADC1
  15.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  // 独立模式
  16.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;  // 单通道模式
  17.     ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  // 连续转换模式
  18.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  // 软件触发
  19.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  // 右对齐
  20.     ADC_InitStructure.ADC_NbrOfChannel = 1;  // 采集通道数
  21.     ADC_Init(ADC1, &ADC_InitStructure);

  22.     // 配置ADC通道
  23.     ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);  // 采集通道0(PA0)

  24.     // 启用ADC
  25.     ADC_Cmd(ADC1, ENABLE);

  26.     // 校准ADC
  27.     ADC_ResetCalibration(ADC1);
  28.     while (ADC_GetResetCalibrationStatus(ADC1));
  29.     ADC_StartCalibration(ADC1);
  30.     while (ADC_GetCalibrationStatus(ADC1));

  31.     // 启动ADC转换
  32.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);
  33. }

  34. // 串口初始化
  35. void USART_Configuration(void) {
  36.     GPIO_InitTypeDef GPIO_InitStructure;
  37.     USART_InitTypeDef USART_InitStructure;

  38.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

  39.     // 配置PA9为USART1_TX
  40.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  41.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  42.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  43.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  44.     // 配置PA10为USART1_RX
  45.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  46.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  47.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  48.     // 配置USART1
  49.     USART_InitStructure.USART_BaudRate = 9600;
  50.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  51.     USART_InitStructure.USART_StopBits = USART_StopBits_1;
  52.     USART_InitStructure.USART_Parity = USART_Parity_No;
  53.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  54.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  55.     USART_Init(USART1, &USART_InitStructure);

  56.     // 启用USART1
  57.     USART_Cmd(USART1, ENABLE);
  58. }

  59. // 串口发送字符
  60. void USART_SendChar(char c) {
  61.     USART_SendData(USART1, c);
  62.     while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
  63. }

  64. // 串口发送字符串
  65. void USART_SendString(char* str) {
  66.     while (*str) {
  67.         USART_SendChar(*str++);
  68.     }
  69. }

  70. int main(void) {
  71.     uint16_t adc_value;

  72.     ADC_Configuration();  // 初始化ADC
  73.     USART_Configuration();  // 初始化串口

  74.     while (1) {
  75.         adc_value = ADC_GetConversionValue(ADC1);  // 读取ADC值
  76.         char buffer[10];
  77.         sprintf(buffer, "%d\n", adc_value);  // 将ADC值转为字符串
  78.         USART_SendString(buffer);  // 通过串口发送ADC值
  79.         for (int i = 0; i < 1000000; i++);  // 延时
  80.     }
  81. }

6. 代码解析
  • ADC_Configuration函数:初始化ADC模块,包括使能时钟、配置引脚和设置ADC工作模式等。ADC1被配置为单通道模式,并通过软件触发方式进行采样。
  • USART_Configuration函数:配置USART1用于串口通信。通过PA9和PA10作为串口的TX和RX引脚,串口波特率设置为9600。
  • main函数:主循环中不断读取ADC的转换值,并通过串口将结果发送到PC。通过电位器调节输入电压,ADC值会随之变化。

7. 实际应用在实际应用中,ADC可以用于采集多种模拟信号,如电压、电流、温度等。STM32F103的ADC模块性能强大,采样速度快,适用于需要精确采集信号的场景。通过本文的代码示例,开发者可以快速掌握STM32F103 ADC的使用方法,并将其应用于各种传感器或电压采集场景。

8. 常见问题
  • ADC采样精度不高:可能是因为参考电压不稳定或信号有干扰,可以考虑使用滤波器或稳定的外部参考电压。
  • 串口数据乱码:检查串口的波特率设置是否正确,确保PC端的波特率与STM32F103一致。
  • ADC转换速度慢:可以适当调整ADC的采样时间,或通过DMA方式提升数据传输效率。

9. 结论STM32F103的ADC模块功能强大,能够方便地采集模拟信号并将其转换为数字信号,供MCU处理。通过本文的示例代码,大家可以快速上手使用STM32的ADC功能,应用于各种模拟信号的采集场景。

公羊子丹 发表于 2024-10-11 08:10 | 显示全部楼层
STM32F103确实好用,ADC加串口这套组合,实战性能很强。
周半梅 发表于 2024-10-11 08:11 | 显示全部楼层
我用的也是电位器做输入,代码完美跑起来了。
帛灿灿 发表于 2024-10-11 08:11 | 显示全部楼层
终于找到一篇详细的教程了,之前卡在ADC初始化好久。
童雨竹 发表于 2024-10-11 08:11 | 显示全部楼层
代码很清晰,按部就班就能实现ADC和串口通信。
Wordsworth 发表于 2024-10-11 08:12 | 显示全部楼层
有没有办法提高ADC的转换速度,感觉有点慢?
Bblythe 发表于 2024-10-11 08:12 | 显示全部楼层
我用了温度传感器,直接拿这个代码改了下,效果很好。
Pulitzer 发表于 2024-10-11 08:12 | 显示全部楼层
串口发送数据那里很简洁,搞定PC端通信太省心了。
Uriah 发表于 2024-10-11 08:12 | 显示全部楼层
太及时了,我正好在研究STM32的ADC,这篇文章讲得很清楚。
Uriah 发表于 2024-10-11 08:13 | 显示全部楼层
我是用的STM32F103C8,代码稍作调整就跑通了,效果不错。
iyoum 发表于 2024-10-11 19:46 | 显示全部楼层
可以使用HSE或HSI作为时钟源,并通过RCC配置适当的预分频器。
Stahan 发表于 2024-10-13 22:31 | 显示全部楼层
这个adc精度怎么样啊
macpherson 发表于 2024-10-14 09:36 | 显示全部楼层
温度变化可能影响ADC的转换精度和稳定性。
在设计时考虑温度补偿措施,以提高ADC在不同温度下的性能表现。
mmbs 发表于 2024-10-14 11:12 | 显示全部楼层
尽量缩短 ADC 输入信号线的长度,过长的信号线容易引入外界干扰,并且会增加信号的寄生电容和电感,影响信号的完整性。
jtracy3 发表于 2024-10-14 22:16 | 显示全部楼层
要将模拟地和数字地分开布线,避免数字信号对模拟信号的干扰。在合适的位置将模拟地和数字地单点连接,通常在电源的滤波电容附近。
uiint 发表于 2024-10-15 11:55 | 显示全部楼层
ADC 的转换结果与参考电压密切相关。确保提供给 ADC 的参考电压稳定,任何参考电压的波动都会直接影响转换精度。例如,如果使用外部参考电压源,要选择纹波小、噪声低的电源芯片。
sdlls 发表于 2024-10-16 19:41 | 显示全部楼层
如果使用多个ADC通道,合理安排转换顺序和时间,避免通道间的相互干扰。
timfordlare 发表于 2024-10-17 10:00 | 显示全部楼层
对于STM32F103,需要使能ADC1或ADC2的时钟,通常是通过RCC_APB2PeriphClockCmd函数来实现。
primojones 发表于 2024-10-18 18:01 | 显示全部楼层
考虑进行 ADC 数据校准。STM32F103 提供了一些校准功能,可以提高 ADC 的转换精度。在初始化或者定期执行校准操作,以补偿 ADC 内部的失调误差等。
gygp 发表于 2024-10-18 21:33 | 显示全部楼层
ADC 对电源噪声非常敏感。在硬件设计时,要对电源进行良好的滤波处理。在靠近 STM32F103 芯片的电源引脚处添加去耦电容,如 100nF 和 1μF 的电容组合,以滤除高频和低频噪声。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

169

主题

6161

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部