news 2026/5/10 16:42:17

Keil uVision5使用教程:ADC采样程序设计完整示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Keil uVision5使用教程:ADC采样程序设计完整示例

从零开始:在Keil中实现STM32的ADC采样,一文搞懂全流程

你有没有遇到过这样的场景?
手头有个电位器、一个STM32最小系统板,想读取模拟电压却卡在ADC配置上——寄存器不会设、采样值跳得离谱、调试时连数据都看不到……别急,这几乎是每个嵌入式新手都会踩的坑。

今天我们就用最“接地气”的方式,带你从硬件连接到代码烧录,完整走一遍基于Keil uVision5 + STM32F103C8T6的ADC单通道采样流程。不讲虚的,只讲你能马上用上的实战经验。


为什么选Keil做ADC开发?

虽然现在有VS Code + PlatformIO、CubeIDE等新工具,但Keil uVision5依然是国内企业与高校最主流的选择,尤其在使用标准外设库(StdPeriph Lib)的老项目中几乎无处不在。

它的优势很实在:
- 图形化界面友好,适合初学者;
- 对ST芯片支持完善,启动文件、头文件自动加载;
- 配合ST-Link调试体验流畅,变量监视、寄存器查看一步到位;
- 编译稳定,工程结构清晰,团队协作方便。

更重要的是——很多公司还在用它。掌握Keil,就是掌握了一把打开实际项目的钥匙。


硬件准备:最小系统也能玩转ADC

我们以常见的“蓝 pill”开发板(STM32F103C8T6)为例,搭建一个简单的电压采集系统:

[电位器] → PA0 (ADC1_IN0) → [STM32] ↓ USART1_TX (PA9) → USB-TTL → PC串口助手

接线说明:
- 电位器两端分别接VDD(3.3V)和GND,滑动端接入PA0;
- ST-Link连接SWCLK、SWDIO、GND、VCC四根线;
- USB-TTL模块TX接PA10(RX),RX接PA9(TX),记得共地。

⚠️ 小贴士:PA0默认是GPIO,要让它变成ADC输入,必须开启时钟并配置为模拟输入模式!否则读出来的值可能是乱码。


第一步:创建Keil工程(别跳过细节)

打开Keil uVision5,新建Project,命名为ADC_Sample_Project

1. 选择芯片型号

在弹出窗口中搜索并选择:STM32F103C8

Keil会自动为你加载:
- 启动文件(startup_stm32f10x_md.s)
- 基本头文件路径
- 寄存器定义(stm32f10x.h)

2. 添加源文件组

右键Source Group 1→ Add New Item to Group…
创建两个文件:
-main.c—— 主程序
- (可选)usart.c/adc.c—— 模块化管理更利于后期扩展

3. 包含必要的库文件

由于我们将使用标准外设库(不是HAL库),需要手动添加:
1. 下载STM32F1xx_StdPeriph_Lib官方库包
2. 将以下目录复制到工程文件夹:
-Libraries/STM32F10x_StdPeriph_Driver/src/
-Libraries/STM32F10x_StdPeriph_Driver/inc/
3. 在Keil中将.c驱动文件加入Source Group
4. 在Options for Target > C/C++ > Include Paths中添加头文件路径

✅ 提示:如果你不想折腾库文件,可以直接操作寄存器或使用寄存器宏定义(后续代码示例采用此方式简化理解)


第二步:编写ADC采样代码(重点来了!)

下面这段代码是你能跑通ADC的关键。我会逐行解释关键点。

#include "stm32f10x.h" // 延时函数 void Delay(volatile uint32_t nCount) { while(nCount--) { __NOP(); } } // 串口1初始化(用于打印结果) void USART1_Init(void) { // 开启GPIOA和USART1时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN; // PA9 配置为复用推挽输出(TX) GPIOA->CRH &= ~GPIO_CRH_MODE9; GPIOA->CRH |= GPIO_CRH_MODE9_1; // 输出速度50MHz GPIOA->CRH &= ~GPIO_CRH_CNF9; GPIOA->CRH |= GPIO_CRH_CNF9_1; // 复用功能推挽 // 配置USART1 USART1->BRR = 72000000 / 115200; // 波特率设置(PCLK=72MHz) USART1->CR1 = USART_CR1_TE | USART_CR1_UE; // 使能发送 + 使能USART } // 发送一个字符 void USART_SendChar(char ch) { while (!(USART1->SR & USART_SR_TXE)); // 等待发送完成 USART1->DR = ch; } // 发送字符串 void USART_SendString(char *str) { while (*str) { USART_SendChar(*str++); } } // ADC1初始化(PA0作为输入) void ADC1_Init(void) { // 1. 开启GPIOA和ADC1时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_ADC1EN; // 2. 配置PA0为模拟输入 GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0); // 清除模式和配置位 // MODE0=00(输入),CNF0=00(模拟输入)——无需设置,默认即为此状态 // 3. ADC时钟配置:PCLK2分频(最大不能超过14MHz) RCC->CFGR &= ~RCC_CFGR_ADCPRE; // 清除原有分频设置 RCC->CFGR |= RCC_CFGR_ADCPRE_DIV8; // PCLK2=72MHz → ADCCLK=9MHz // 4. 配置ADC控制寄存器 ADC1->SMPR2 |= ADC_SMPR2_SMP0_2 | ADC_SMPR2_SMP0_1 | ADC_SMPR2_SMP0_0; // 通道0采样时间:239.5周期 ADC1->SQR1 = 0; // 单通道转换 ADC1->SQR3 = 0; // 规则序列第1个通道 = 通道0 ADC1->CR2 |= ADC_CR2_ADON; // 开启ADC Delay(1000); // 等待稳定 ADC1->CR2 |= ADC_CR2_CAL; // 启动校准 while (ADC1->CR2 & ADC_CR2_CAL); // 等待校准完成 }

关键参数解读(必看!)

设置项说明
ADC时钟分频 = DIV8PCLK2=72MHz → ADCCLK=9MHz < 14MHz,符合手册要求
采样时间 = 239.5周期高阻信号源(如电位器)需长采样时间防止误差
ADON两次写入第一次开启ADC,第二次触发校准
PA0模拟输入必须关闭数字功能,否则引入噪声

主循环:读取ADC值并串口输出

int main(void) { uint16_t adc_value; char buffer[20]; // 初始化外设 ADC1_Init(); USART1_Init(); USART_SendString("ADC Sampling Started!\r\n"); while (1) { // 手动启动一次转换 ADC1->CR2 |= ADC_CR2_SWSTART; // 等待转换完成(EOC标志置位) while (!(ADC1->SR & ADC_SR_EOC)); // 读取数据寄存器(低16位有效) adc_value = (uint16_t)(ADC1->DR & 0xFFFF); // 转换为字符串发送 sprintf(buffer, "ADC Value: %d\r\n", adc_value); USART_SendString(buffer); Delay(500000); // 简单延时约500ms } }

这里有几个“坑”,我都替你踩过了:

🔴问题1:为什么第一次读数总是偏高?
→ 因为首次启动ADC未充分稳定。建议在ADON后加微小延时(如Delay(100))再校准。

🔴问题2:采样值波动大?
→ 检查电源是否干净,VDDA旁路电容是否到位(推荐0.1μF陶瓷电容靠近芯片引脚)。

🔴问题3:串口乱码?
→ 确保波特率计算正确。若主频非72MHz,请调整BRR值。


如何调试?这才是Keil的真正强大之处!

别再靠“printf式猜错”了,Keil的调试功能完全可以让你看到每一刻的变化。

实战调试技巧:

  1. 设置断点观察ADC_DR
    - 在adc_value = ADC1->DR;处设断点
    - 运行程序 → 触发转换 → 查看寄存器窗口中的ADC1->DR

  2. 使用Memory Viewer查看内存
    - 输入&ADC1->DR,实时监控地址变化

  3. 逻辑分析仪模拟波形(Simulation Trace)
    - 在Debug模式下启用ITM Port,配合SWO引脚可输出时间戳数据(进阶玩法)

  4. 查看NVIC中断状态
    - 如果你改用中断方式采样,可在System Viewer > NVIC中确认EOC中断是否触发


可以怎么升级?给你的下一步方向

你现在跑通的是轮询模式下的单次采样,但这只是起点。接下来你可以尝试这些提升:

✅ 升级1:使用中断方式

让ADC转换结束后自动通知CPU,释放主循环资源。

// 使能EOC中断 ADC1->CR1 |= ADC_CR1_EOCIE; NVIC_EnableIRQ(ADC1_2_IRQn); // 中断服务函数 void ADC1_2_IRQHandler(void) { if (ADC1->SR & ADC_SR_EOC) { uint16_t val = ADC1->DR; // 处理数据(如存入缓冲区) } }

✅ 升级2:启用DMA进行连续采样

适合高速采集场景(如音频采样、振动监测)

// 示例伪代码 RCC->AHBENR |= RCC_AHBENR_DMA1EN; DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR; DMA1_Channel1->CMAR = (uint32_t)adc_buffer; DMA1_Channel1->CNDTR = SAMPLE_COUNT; DMA1_Channel1->CCR = DMA_CCR_EN | DMA_CCR_CIRC | DMA_CCR_TCIE;

✅ 升级3:结合滤波算法提高精度

原始ADC值有抖动很正常,加上软件滤波立马变稳:

// 移动平均滤波 #define FILTER_SIZE 8 uint16_t filter_buf[FILTER_SIZE]; uint8_t idx = 0; uint16_t moving_average(uint16_t new_val) { filter_buf[idx++] = new_val; if (idx >= FILTER_SIZE) idx = 0; uint32_t sum = 0; for (int i = 0; i < FILTER_SIZE; i++) { sum += filter_buf[i]; } return sum / FILTER_SIZE; }

常见问题与避坑指南(血泪总结)

问题现象可能原因解决方案
ADC值始终为0或4095PA0未设为模拟输入检查GPIOx->CRL配置
数值跳动剧烈采样时间太短或电源干扰延长采样时间,增加去耦电容
无法下载程序SWD接线错误或BOOT模式不对检查BOOT0接地,确认ST-Link供电正常
串口无输出波特率错误或TX引脚配置错使用示波器测PA9是否有波形
校准失败卡死ADC时钟超限确保ADC_PRESCALER后频率≤14MHz

写在最后:这不是终点,而是起点

当你第一次在串口助手中看到那个随着旋钮转动而变化的数字时,你就已经跨过了嵌入式开发的一道重要门槛。

本文没有堆砌术语,也没有照搬手册,而是还原了一个真实开发者从接线 → 工程搭建 → 写代码 → 调试 → 出结果的全过程。

你学到的不仅是“如何配置ADC”,更是一种解决问题的方法论
发现问题 → 分析寄存器 → 修改代码 → 借助工具验证。

未来你可以继续深入:
- 使用STM32CubeMX自动生成初始化代码;
- 接入温度传感器(内部通道16)、电池电压检测;
- 把ADC数据绘制成曲线图,实现简易示波器;
- 结合FreeRTOS实现多任务采集与处理。


如果你觉得这篇文章帮你少熬了一晚上,欢迎点赞收藏。
也欢迎在评论区留下你在ADC开发中遇到的难题,我们一起讨论解决。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/4 0:18:13

案例研究:一次完整的信息收集流程复盘

第一部分&#xff1a;开篇明义 —— 定义、价值与目标 定位与价值 信息收集&#xff0c;作为渗透测试生命周期的第一步&#xff0c;其战略地位常被比作战争中的“侦察”或外科手术前的“全面体检”。它不是简单的工具堆砌&#xff0c;而是一个系统性、分析驱动的智力过程。其核…

作者头像 李华
网站建设 2026/5/10 18:02:34

通信工程毕业设计最新开题报告怎么选

【单片机毕业设计项目分享系列】 &#x1f525; 这里是DD学长&#xff0c;单片机毕业设计及享100例系列的第一篇&#xff0c;目的是分享高质量的毕设作品给大家。 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的单片机项目缺少创新和亮点…

作者头像 李华
网站建设 2026/5/8 16:03:32

开发者入门必看:Z-Image-Turbo+CSDN镜像一键部署实战推荐

开发者入门必看&#xff1a;Z-Image-TurboCSDN镜像一键部署实战推荐 1. 背景与技术价值 随着AI生成内容&#xff08;AIGC&#xff09;的快速发展&#xff0c;文生图&#xff08;Text-to-Image&#xff09;模型已成为开发者和创作者关注的核心工具之一。在众多开源模型中&…

作者头像 李华
网站建设 2026/5/9 18:40:48

中文ITN文本标准化实践|基于FST ITN-ZH镜像快速实现

中文ITN文本标准化实践&#xff5c;基于FST ITN-ZH镜像快速实现 在语音识别&#xff08;ASR&#xff09;和自然语言处理&#xff08;NLP&#xff09;的实际应用中&#xff0c;一个常被忽视但至关重要的环节是逆文本标准化&#xff08;Inverse Text Normalization, ITN&#xf…

作者头像 李华
网站建设 2026/5/9 15:32:11

Supertonic部署详解:4090D显卡的最佳配置方案

Supertonic部署详解&#xff1a;4090D显卡的最佳配置方案 1. 技术背景与选型动机 随着边缘计算和本地化AI应用的快速发展&#xff0c;设备端文本转语音&#xff08;TTS&#xff09;系统的需求日益增长。用户对低延迟、高隐私性、强可定制性的要求推动了轻量级、高性能TTS框架…

作者头像 李华
网站建设 2026/5/1 11:59:19

YOLOv11如何高效部署?Jupyter Notebook操作详解

YOLOv11如何高效部署&#xff1f;Jupyter Notebook操作详解 YOLOv11 是 Ultralytics 推出的最新目标检测算法&#xff0c;作为 YOLO 系列的迭代升级版本&#xff0c;在保持轻量化优势的同时进一步提升了检测精度与推理速度。该模型在 COCO 数据集上展现出卓越的性能&#xff0…

作者头像 李华