3. 开发环境搭建(下):工程模板创建、编译与下载、LED点灯实验、串口打印Hello World
上一节我们把工具链、IDE、调试器驱动都装好了。现在,是时候让代码真正跑起来了。
这一节我们要做四件事:创建工程模板、编译下载、点灯、打印Hello World。听起来简单,但每一步都有坑。我当年第一次点灯,折腾了整整一个下午——后来发现是芯片型号选错了。
3.1 创建工程模板——别每次都从零开始
我个人习惯,每个项目都从模板开始。为什么?因为芯片初始化代码(启动文件、链接脚本、时钟配置)每次几乎一样。你想想看,每次新建工程都重新配一遍时钟,多浪费时间。
3.1.1 选择芯片型号
以STM32F103C8T6为例(蓝板子最常见)。打开Keil,点Project → New μVision Project,然后:
- 选一个干净的文件夹,比如
D:\workspace\bp_monitor_template - 输入工程名:
bp_monitor - 在弹出的芯片选择窗口,搜索
STM32F103C8
3.1.2 添加必要的文件
一个最小工程需要这些文件:
| 文件/文件夹 | 说明 |
|---|---|
| startup_stm32f103xb.s | 启动文件,负责初始化堆栈、跳转到main |
| system_stm32f10x.c | 系统时钟配置 |
| stm32f10x.h | 外设寄存器定义 |
| main.c | 你的主程序 |
这些文件可以从STM32标准外设库(StdPeriph_Lib)里复制。我个人习惯把库文件放在工程目录下的 Libraries 文件夹里,方便管理。
3.1.3 配置魔术棒(Target & C/C++)
点工具栏的「魔术棒」图标,进入工程配置:
- Target标签:晶振频率填
8.0(外部晶振8MHz) - C/C++标签:Define框里加
USE_STDPERIPH_DRIVER, STM32F10X_MD - Debug标签:选
ST-Link Debugger,点Settings,确认SWD模式
.\Libraries\CMSIS 和 .\Libraries\StdPeriph_Driver\inc 加进去。这样写代码时头文件不会报红。
3.2 编译与下载——第一次跑通
模板建好了,先写一个空main函数,编译试试:
int main(void)
{
while(1);
}
点 Build 按钮(或者按F7)。如果0错误0警告,恭喜你,环境没问题。
然后点 Load 按钮(或者按F8),把程序下载到板子里。如果一切顺利,调试器会提示 Flash Download finished。
No target connected,先检查ST-Link的4根线(SWDIO、SWCLK、GND、3.3V)有没有接反。我遇到过最离谱的一次,是杜邦线内部断了——换了根线就好了。
3.3 LED点灯实验——嵌入式界的Hello World
点灯,说白了就是控制GPIO的电平。在血压计项目里,LED可以用来指示电源状态、测量进度等。
3.3.1 硬件连接
假设板子上有一个LED接在PC13(高电平点亮)。我们需要:
- 使能GPIOC的时钟
- 配置PC13为推挽输出
- 写高电平点亮,写低电平熄灭
3.3.2 代码实现
#include "stm32f10x.h"
void delay(void)
{
volatile uint32_t i;
for(i = 0; i < 500000; i++);
}
int main(void)
{
// 使能GPIOC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
// 配置PC13为推挽输出
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
while(1)
{
GPIO_SetBits(GPIOC, GPIO_Pin_13); // 点亮
delay();
GPIO_ResetBits(GPIOC, GPIO_Pin_13); // 熄灭
delay();
}
}
编译下载后,如果LED以大约1秒的频率闪烁,说明成功了。
RCC_APB2PeriphClockCmd 没调用。记住:STM32的所有外设,用之前必须先开时钟。
3.4 串口打印Hello World——让板子开口说话
点灯只能看,串口才能「听」。在血压计开发中,串口用来输出调试信息、传感器数据等。
3.4.1 硬件连接
用USB转串口模块(比如CH340G)连接板子:
- 板子TX(PA9) → 模块RX
- 板子RX(PA10) → 模块TX
- GND → GND
3.4.2 串口初始化代码
void USART1_Init(void)
{
// 使能USART1和GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 配置PA9为复用推挽输出(TX)
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置PA10为浮空输入(RX)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1:115200波特率,8位数据,1位停止位,无校验
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART1, &USART_InitStructure);
// 使能USART1
USART_Cmd(USART1, ENABLE);
}
// 发送一个字符
void USART1_SendChar(uint8_t ch)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, ch);
}
// 发送字符串
void USART1_SendString(char *str)
{
while(*str)
{
USART1_SendChar(*str++);
}
}
3.4.3 主函数调用
int main(void)
{
USART1_Init();
while(1)
{
USART1_SendString("Hello World from BP Monitor!\r\n");
delay();
}
}
打开串口助手(波特率115200),你应该能看到板子每隔1秒打印一次 Hello World from BP Monitor!。
- 串口配置的三个关键参数:波特率、数据位、停止位——两边必须一致
- TX引脚要配成复用推挽输出,RX引脚配成浮空输入
- 发送数据前要等待发送缓冲区空(TXE标志位)
3.5 小结
这一节我们完成了嵌入式开发最基础的三件事:建模板、点灯、打印。别小看这些,它们是后续所有复杂功能(ADC采集、传感器驱动、无线通信)的基石。
下一节,我们会正式进入血压计的核心——压力传感器数据采集。到时候,串口打印的数据就不再是Hello World,而是真实的血压值了。
嗯,先到这里。记得把今天的代码保存好,后面会反复用到。