Keil5使用教程:C语言串口通信项目应用

从零开始掌握Keil5串口通信:STM32底层驱动实战指南

你是否曾在点亮LED后,卡在“下一步该做什么”的瓶颈期?
你是否面对Keil5复杂的工程配置和一堆寄存器感到无从下手?
你是否想让单片机真正“开口说话”,却不知道如何建立稳定的数据通道?

别担心。今天我们就来解决这个关键问题——用C语言在Keil5中实现STM32的串口通信

这不是一份泛泛而谈的工具使用说明,而是一次深入硬件本质的实战演练。我们将绕过HAL库的封装,直接操作寄存器,带你理解每一行代码背后的物理意义。最终目标很明确:让你的STM32通过串口接收一个字符,并原样回传给电脑

整个过程将在Keil MDK-ARM(即Keil5)环境下完成,适用于STM32F1系列(如经典型号STM32F103C8T6)。无论你是刚入门的新手,还是希望夯实基础的老手,这篇文章都会给你带来实实在在的价值。


为什么是Keil5?它真的值得学吗?

在嵌入式开发领域,IDE选择众多:IAR、GCC+Eclipse、VS Code+PlatformIO……但为何我们仍要聚焦于Keil5?

因为它依然是工业界最主流、资料最丰富、兼容性最好的ARM Cortex-M开发环境之一

Keil5的核心优势在于它的成熟度与稳定性。Arm Compiler对Cortex-M内核做了深度优化,生成的代码紧凑高效;uVision5界面虽然不算现代,但功能完整、逻辑清晰;更重要的是,几乎所有国产MCU厂商(如GD32、APM32)都提供完整的Keil支持包。

而且对于初学者来说,Keil5的学习曲线相对平缓——你可以先专注于理解外设工作机制,而不必一开始就陷入Makefile、链接脚本、编译链配置等复杂细节中。

⚠️ 当然也有缺点:免费版限制代码大小为32KB,商业项目需授权;建议优先使用Arm Compiler 6以获得更好的C11标准支持和性能优化。

但这些都不是阻碍我们上手的理由。相反,正是因为它够“传统”,才更适合用来学习底层机制。


串口通信:嵌入式系统的“第一扇窗”

如果说GPIO控制LED是嵌入式开发的“Hello World”,那么串口通信就是你打开系统内部世界的“第一扇窗”

UART(通用异步收发器)协议简单、资源占用少、调试方便,几乎每一块开发板都配备了至少一个串口。它不仅能用于打印日志、发送传感器数据,还能作为Bootloader升级接口、远程命令行入口,甚至是Modbus等工业协议的基础载体。

更重要的是,掌握串口意味着你开始真正理解时钟、中断、DMA、状态机这些核心概念的实际应用方式

什么是UART?它怎么工作的?

UART是一种异步串行通信接口,只需要两根线就能完成全双工通信:
-TX:发送数据
-RX:接收数据

所谓“异步”,是指通信双方没有共享时钟线,而是依赖事先约定好的波特率(Baud Rate)来同步每一位的传输时间。例如115200bps表示每秒传送115200个符号。

典型帧格式为“8-N-1”:
- 1位起始位(低电平)
- 8位数据位(LSB先行)
- 无校验位
- 1位停止位(高电平)

只要两边设置一致,就可以可靠通信。当然,波特率误差最好控制在±2%以内,否则容易出现误码。


硬件准备与系统架构设计

我们的目标系统非常简洁:

[PC] ↓ (USB-TTL模块,如CH340G/CP2102) [TX ← PA10] [STM32F103C8T6] [PA9 → RX] ↓ [串口助手软件:XCOM / SSCOM / Tera Term]

MCU负责初始化USART1外设,开启接收中断。一旦收到数据,立即触发中断服务程序,读取并回传。

主循环则可以继续执行其他任务,比如闪烁LED或采集ADC值——这正是中断机制的魅力所在。


第一步:搭建Keil5工程框架

打开Keil uVision5,新建一个工程:

  1. Project → New uVision Project,保存路径不要含中文;
  2. 选择芯片型号:STMicroelectronics → STM32F103C8
  3. Keil会提示是否添加启动文件,点击“是”;
  4. 创建新组:Source(存放.c文件)、Inc(头文件)、Startup(启动代码);
  5. startup_stm32f103xb.s加入Startup组;
  6. 新建main.cusart.cusart.h,分别加入对应组。

此时你的工程结构应该像这样:

Project ├── Source │ ├── main.c │ └── usart.c ├── Inc │ └── usart.h └── Startup └── startup_stm32f103xb.s

接下来,我们要做的不是写代码,而是搞清楚一件事:芯片是怎么被唤醒的?


启动流程解析:从复位到main函数

当你按下复位按钮,CPU并不是直接跳转到main()函数。中间有一个关键过程:

  1. CPU从Flash首地址读取栈顶指针(SP),通常是0x2000_0000附近;
  2. 再读取复位向量,跳转到Reset_Handler
  3. 执行SystemInit() —— 这是由Keil提供的默认函数,配置HSE、PLL,将系统时钟升至72MHz;
  4. 最终调用__main,由编译器运行库引导进入用户main()函数。

这意味着:在你写下第一行C代码前,系统时钟已经准备就绪。这对后续外设初始化至关重要。


核心难点突破一:GPIO复用与时钟使能

STM32的强大之处在于其高度灵活的引脚复用机制。比如PA9和PA10,默认是普通GPIO,但我们可以通过配置让它变成USART1的TX/RX引脚。

但这需要两个前提条件:
1.必须先开启对应总线时钟,否则无法访问寄存器;
2.必须正确配置复用功能编号(AF),否则信号不会连通。

关键寄存器一览

寄存器功能
RCC->AHB1ENR开启GPIOA时钟(F1系列实际为APB2ENR)
RCC->APB2ENR开启USART1时钟
GPIOA->MODER设置模式:输入/输出/复用/模拟
GPIOA->OTYPER输出类型:推挽/开漏
GPIOA->OSPEEDR输出速度等级
GPIOA->PUPDR上下拉电阻配置
GPIOA->AFR[1]选择复用功能号(AF7 = USART1)

注意:STM32F103属于较早系列,GPIO时钟位于APB2而非AHB1,因此应使用RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

实战代码:手动配置PA9(TX)和PA10(RX)

// usart.c #include "stm32f10x.h" void GPIO_UART_Init(void) { // Step 1: 使能GPIOA和USART1时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN; // ------------------- 配置PA9为USART1_TX --------------------- // MODER: 清除原有设置,设为复用推挽输出 GPIOA->MODER &= ~GPIO_MODER_MODER9_Msk; GPIOA->MODER |= GPIO_MODER_MODER9_1; // 复用模式(10) GPIOA->OTYPER &= ~GPIO_OTYPER_OT_9; // 推挽输出 GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9_1; // 高速(50MHz) GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR9_Msk; // 无上下拉 // AFR[1]: 因为PA9 > 7,所以使用AFRH寄存器 GPIOA->AFR[1] |= (7U << (9 - 8)*4); // PA9 -> AF7 (USART1) // ------------------- 配置PA10为USART1_RX -------------------- GPIOA->MODER &= ~GPIO_MODER_MODER10_Msk; GPIOA->MODER |= GPIO_MODER_MODER10_1; // 复用模式 GPIOA->PUPDR |= GPIO_PUPDR_PUPDR10_0; // 上拉(增强抗干扰) GPIOA->AFR[1] |= (7U << (10 - 8)*4); // PA10 -> AF7 }

✅ 小贴士:复用功能编号必须查手册确认。STM32F103中,USART1映射到AF7。

这段代码看似繁琐,但它让你彻底掌握了硬件控制权。相比HAL库的一句HAL_UART_Init(),这种方式更能帮助你理解“为什么”。


核心难点突破二:USART初始化与波特率计算

接下来我们配置USART1本身。

主要涉及三个寄存器:
-USART1->BRR:波特率寄存器(形如DIV_Mantissa | (DIV_Fraction << 4))
-USART1->CR1:控制寄存器1,启用发送/接收
-USART1->SR:状态寄存器,反映当前通信状态

波特率怎么算?

公式如下:

Baud = f_PCLK / (16 * USARTDIV)

其中PCLK2通常为72MHz(APB2挂载高速外设),若要得到115200bps:

USARTDIV = 72000000 / (16 * 115200) ≈ 39.0625 → 整数部分 = 39, 小数部分 = 0.0625 × 16 ≈ 1 → BRR = 0x271

我们可以写成宏定义:

#define UART_BAUDRATE 115200 #define PCLK2_FREQ 72000000UL #define USART_DIV (PCLK2_FREQ + UART_BAUDRATE / 2) / (16 * UART_BAUDRATE) #define USART_BRR ((USART_DIV << 4) | (((PCLK2_FREQ * 10 / (16 * UART_BAUDRATE)) % 10) >= 5 ? 1 : 0))

不过更简单的做法是直接赋值:

USART1->BRR = 0x271; // 对应115200bps @72MHz PCLK2

初始化函数实现

void USART1_Init(uint32_t baudrate) { // 已在GPIO_UART_Init中开启时钟 GPIO_UART_Init(); // 计算BRR(简化处理,固定72MHz) uint32_t brr = 72000000 / (16 * baudrate); USART1->BRR = (brr << 4) | ((72000000 * 10 / (16 * baudrate)) % 10 >= 5 ? 1 : 0); // CR1配置:使能UE(使能UART), RE(接收), TE(发送) USART1->CR1 = USART_CR1_UE | USART_CR1_RE | USART_CR1_TE; // 可在此处使能中断(也可单独函数) }

核心难点突破三:NVIC中断配置与ISR编写

轮询方式太浪费CPU。我们采用中断驱动模式,当接收到数据时自动触发处理。

中断使能步骤

  1. 在USART控制寄存器中使能RXNE中断;
  2. 配置NVIC优先级;
  3. 开启全局中断通道。
void USART_EnableInterrupt(void) { // 使能接收中断 USART1->CR1 |= USART_CR1_RXNEIE; // 设置优先级(抢占优先级2,子优先级0) NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 2, 0)); // 使能NVIC中断源 NVIC_EnableIRQ(USART1_IRQn); }

编写中断服务函数

注意:中断函数名必须与启动文件中的向量表一致!

查看startup_stm32f103xb.s可知,USART1的中断服务函数名为:

DCD USART1_IRQHandler

所以我们必须定义同名函数:

// usart.c extern void USART_SendByte(USART_TypeDef* USARTx, uint8_t ch); // 声明发送函数 void USART1_IRQHandler(void) { if (USART1->SR & USART_SR_RXNE) { // 接收数据寄存器非空? uint8_t ch = USART1->DR; // 读DR自动清标志 USART_SendByte(USART1, ch); // 回显 } // 其他中断标志可在此扩展(如错误处理) }

发送函数实现

void USART_SendByte(USART_TypeDef* USARTx, uint8_t ch) { while (!(USARTx->SR & USART_SR_TXE)); // 等待发送寄存器空 USARTx->DR = ch; } void USART_SendString(USART_TypeDef* USARTx, const char* str) { while (*str) { USART_SendByte(USARTx, *str++); } }

主函数整合:构建完整通信系统

现在回到main.c,把所有模块串联起来:

// main.c #include "stm32f10x.h" #include "usart.h" int main(void) { SystemInit(); // Keil提供,设置系统时钟为72MHz USART1_Init(115200); // 初始化串口 USART_EnableInterrupt(); // 启用接收中断 USART_SendString(USART1, "Serial Communication Ready!\r\n"); while (1) { // 主循环可执行其他任务 // 如LED闪烁、ADC采样、PWM输出等 } }

编译、下载、打开串口助手(波特率115200,8-N-1),发送任意字符,你应该能看到相同字符被返回。

恭喜!你刚刚完成了第一个真正的嵌入式通信功能。


常见坑点与调试秘籍

❌ 问题1:串口没反应?什么也收不到!

检查以下几点:
-TX/RX是否交叉连接?MCU-TX → PC-RX,不能直连;
-电平是否匹配?STM32是3.3V TTL,若接RS232需加MAX3232转换;
-时钟开了吗?忘记开GPIO或USART时钟是最常见错误;
-复用功能选错了吗?查手册确认AF编号;
-波特率偏差太大?使用标准值如9600、115200,避免自定义频率。

❌ 问题2:收到乱码?

多半是波特率不匹配。确保:
- PCLK计算准确;
- PLL倍频正确(SystemInit后应为72MHz);
- 串口助手设置与代码一致。

可用示波器抓TX波形,测量位宽验证。

❌ 问题3:中断进不去?

  • 检查NVIC_EnableIRQ()是否调用;
  • 是否忘记使能USART_CR1_RXNEIE
  • 中断函数名拼写错误(区分大小写);
  • 优先级分组冲突(一般不用改,默认即可)。

总结与延伸思考

通过本次实践,我们不仅实现了串口回环功能,更重要的是建立了完整的知识链条:

  • Keil5工程创建流程
  • 启动文件与系统初始化机制
  • GPIO复用配置方法
  • USART工作原理与寄存器操作
  • NVIC中断管理与ISR编写规范

这些技能构成了嵌入式开发的基石。下一步你可以尝试:
- 加入DMA实现零负载数据接收;
- 实现简单的AT指令解析;
- 使用串口打印浮点数或结构体数据;
- 移植到FreeRTOS中作为日志输出通道。


如果你正在学习嵌入式,不妨把这篇文章收藏下来。下次遇到串口问题时,试着不用HAL库,回归寄存器层面去排查。你会发现,那些曾经神秘的“黑盒”,其实都有迹可循。

毕竟,真正的工程师,不仅要会“用工具”,更要懂“背后发生了什么”。

如果你在实现过程中遇到了挑战,欢迎在评论区留言交流。我们一起把每一个bug变成成长的机会。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1121290.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手把手搭建Nominatim开发环境:从零到精通地理编码开发 [特殊字符]️

手把手搭建Nominatim开发环境&#xff1a;从零到精通地理编码开发 &#x1f5fa;️ 【免费下载链接】Nominatim 项目地址: https://gitcode.com/gh_mirrors/nom/Nominatim 想要高效参与Nominatim地理编码系统的开发工作吗&#xff1f;一个完善的Nominatim开发环境配置是…

催化剂机器学习数据集选择终极指南:OC20/OC22/OC25如何帮您节省90%研发时间?

催化剂机器学习数据集选择终极指南&#xff1a;OC20/OC22/OC25如何帮您节省90%研发时间&#xff1f; 【免费下载链接】ocp Open Catalyst Projects library of machine learning methods for catalysis 项目地址: https://gitcode.com/GitHub_Trending/oc/ocp 您是否正…

解密Code Llama分词器:AI代码处理的幕后英雄

解密Code Llama分词器&#xff1a;AI代码处理的幕后英雄 【免费下载链接】codellama Inference code for CodeLlama models 项目地址: https://gitcode.com/gh_mirrors/co/codellama 你是否曾经好奇&#xff0c;Code Llama是如何理解并生成代码的&#xff1f;&#x1f9…

Llava模型迁移成本评估:从原始框架到ms-swift的转换代价

Llava模型迁移成本评估&#xff1a;从原始框架到ms-swift的转换代价 在多模态AI应用迅速落地的今天&#xff0c;越来越多企业希望将图文理解、视觉问答等能力快速集成到产品中。Llava&#xff08;Large Language and Vision Assistant&#xff09;作为当前主流的视觉-语言融合模…

GLM数学库全面配置与实战应用指南

GLM数学库全面配置与实战应用指南 【免费下载链接】glm OpenGL Mathematics (GLM) 项目地址: https://gitcode.com/gh_mirrors/gl/glm GLM&#xff08;OpenGL Mathematics&#xff09;是一个专为图形编程设计的C数学库&#xff0c;它完美复刻了GLSL&#xff08;OpenGL着…

RR引导系统终极指南:5分钟完成黑群晖专业部署

RR引导系统终极指南&#xff1a;5分钟完成黑群晖专业部署 【免费下载链接】rr Redpill Recovery (arpl-i18n) 项目地址: https://gitcode.com/gh_mirrors/rr2/rr 在DIY NAS领域&#xff0c;RR引导系统已经成为了构建稳定黑群晖环境的首选方案。无论您是想在老旧硬件上搭…

微信AI助手完整部署教程:零基础5分钟打造智能聊天机器人

微信AI助手完整部署教程&#xff1a;零基础5分钟打造智能聊天机器人 【免费下载链接】wechat-bot &#x1f916;一个基于 WeChaty 结合 DeepSeek / ChatGPT / Kimi / 讯飞等Ai服务实现的微信机器人 &#xff0c;可以用来帮助你自动回复微信消息&#xff0c;或者管理微信群/好友…

Flutter WebView Plugin终极指南:5个核心功能解锁移动端混合开发新境界

Flutter WebView Plugin终极指南&#xff1a;5个核心功能解锁移动端混合开发新境界 【免费下载链接】flutter_webview_plugin Community WebView Plugin - Allows Flutter to communicate with a native WebView. 项目地址: https://gitcode.com/gh_mirrors/fl/flutter_webv…

逆向工程师必备神器:retoolkit中的PDF恶意文档检测工具实战指南

逆向工程师必备神器&#xff1a;retoolkit中的PDF恶意文档检测工具实战指南 【免费下载链接】retoolkit Reverse Engineers Toolkit 项目地址: https://gitcode.com/gh_mirrors/re/retoolkit 在网络安全日益严峻的今天&#xff0c;恶意PDF文档已成为黑客攻击的常用手段。…

Cider音乐播放器:重新定义跨平台Apple Music聆听体验

Cider音乐播放器&#xff1a;重新定义跨平台Apple Music聆听体验 【免费下载链接】Cider A new cross-platform Apple Music experience based on Electron and Vue.js written from scratch with performance in mind. &#x1f680; 项目地址: https://gitcode.com/gh_mirr…

Alfred编码解码工作流终极使用指南:快速处理字符串转换

Alfred编码解码工作流终极使用指南&#xff1a;快速处理字符串转换 【免费下载链接】alfred-encode-decode-workflow Encoding and decoding a string into multiple variations. 项目地址: https://gitcode.com/gh_mirrors/al/alfred-encode-decode-workflow Alfred编码…

如何用Pock免费工具将MacBook Touch Bar打造成终极生产力中心

如何用Pock免费工具将MacBook Touch Bar打造成终极生产力中心 【免费下载链接】pock Widgets manager for MacBook Touch Bar 项目地址: https://gitcode.com/gh_mirrors/po/pock 还在为MacBook Touch Bar的功能单一而烦恼吗&#xff1f;每次想要快速切换应用或调节系统…

鸿蒙投屏终极指南:免费开源工具HOScrcpy让远程调试如此简单

鸿蒙投屏终极指南&#xff1a;免费开源工具HOScrcpy让远程调试如此简单 【免费下载链接】鸿蒙远程真机工具 该工具主要提供鸿蒙系统下基于视频流的投屏功能&#xff0c;帧率基本持平真机帧率&#xff0c;达到远程真机的效果。 项目地址: https://gitcode.com/OpenHarmonyTool…

如何快速掌握StabilityMatrix:AI绘画包管理器的完整使用指南

如何快速掌握StabilityMatrix&#xff1a;AI绘画包管理器的完整使用指南 【免费下载链接】StabilityMatrix Multi-Platform Package Manager for Stable Diffusion 项目地址: https://gitcode.com/gh_mirrors/st/StabilityMatrix 想要轻松管理各种AI绘画工具和模型&…

Keil uVision5安装编译器配置要点:一文说清

Keil uVision5 编译器配置全攻略&#xff1a;从安装到实战避坑 你是不是也遇到过这样的场景&#xff1f;刚下载完 Keil uVision5&#xff0c;兴冲冲打开工程准备编译&#xff0c;结果弹出一个红色警告&#xff1a;“The selected compiler toolchain is not available.” 或者…

物理信息神经网络终极指南:从零基础到实战高手的完整学习路径

物理信息神经网络终极指南&#xff1a;从零基础到实战高手的完整学习路径 【免费下载链接】PINNpapers Must-read Papers on Physics-Informed Neural Networks. 项目地址: https://gitcode.com/gh_mirrors/pi/PINNpapers 还在为复杂的微分方程求解而头疼吗&#xff1f;…

终极指南:使用Mirai Console构建企业级QQ机器人系统

终极指南&#xff1a;使用Mirai Console构建企业级QQ机器人系统 【免费下载链接】mirai-console mirai 的高效率 QQ 机器人控制台 项目地址: https://gitcode.com/gh_mirrors/mi/mirai-console 在当今数字化时代&#xff0c;QQ机器人已成为企业客户服务、社群管理和自动…

Pixel Art XL终极指南:8步生成专业级像素艺术

Pixel Art XL终极指南&#xff1a;8步生成专业级像素艺术 【免费下载链接】pixel-art-xl 项目地址: https://ai.gitcode.com/hf_mirrors/nerijs/pixel-art-xl 还在为像素艺术创作而烦恼吗&#xff1f;Pixel Art XL让每个人都能成为像素艺术家&#xff01;这款基于Stabl…

Catppuccin iTerm2主题终极配置指南:简单步骤打造个性化终端

Catppuccin iTerm2主题终极配置指南&#xff1a;简单步骤打造个性化终端 【免费下载链接】iterm &#x1f36d; Soothing pastel theme for iTerm2 项目地址: https://gitcode.com/gh_mirrors/it/iterm 想要为你的iTerm2终端注入一抹温柔的色彩吗&#xff1f;Catppuccin…

BizHawk终极指南:快速掌握多系统游戏模拟器完整使用教程

BizHawk终极指南&#xff1a;快速掌握多系统游戏模拟器完整使用教程 【免费下载链接】BizHawk BizHawk is a multi-system emulator written in C#. BizHawk provides nice features for casual gamers such as full screen, and joypad support in addition to full rerecordi…