4、STM32基础工程搭建:使用CubeMX生成HAL库工程、配置GPIO、USART、SysTick定时器

好,咱们正式开始动手了。

这一章,我带你从零搭建一个STM32的基础工程。说白了,就是让芯片先“活”起来——能点灯、能打印、能跑时间。这是后面所有云端通信的基石。

我个人习惯用STM32CubeMX来生成初始化代码。为什么?省事。你想想看,以前用标准库的时候,配置一个时钟树就得翻半天手册,现在图形化点一点,代码自动生成,多舒服。

4.1 使用CubeMX生成HAL库工程

打开CubeMX,第一步是选芯片。我这边用的是STM32F103C8T6,也就是大家常说的“蓝色 pill”板子。你在实际项目中可能用F4、F7或者G0系列,操作流程完全一样。

小提示: 选芯片时注意封装和Flash大小。F103C8T6是48引脚,64KB Flash。如果你的代码比较大,记得选大容量型号。

选好芯片后,进入Pinout & Configuration界面。这里我要配置三样东西:

  • GPIO —— 控制LED,作为运行指示灯
  • USART —— 串口打印,调试必备
  • SysTick —— 系统滴答定时器,HAL库的时基

嗯,这里要注意:SysTick其实CubeMX默认就帮你配好了,但我们要理解它怎么工作的。

4.2 配置GPIO:让LED亮起来

我习惯把LED接在PC13上,因为很多STM32最小系统板默认就是这个引脚。你在自己的板子上可能不一样,记得查原理图。

在CubeMX的Pinout视图中,找到PC13,右键选择GPIO_Output。然后去GPIO配置页面:

参数 设置值 说明
GPIO output level High 初始状态高电平,LED灭
GPIO mode Output Push-Pull 推挽输出,驱动能力强
GPIO Pull-up/Pull-down No pull-up/pull-down 外部已有上拉,内部不用
Maximum output speed Low LED不需要高速,Low省电

我在项目中遇到过一个问题:LED死活不亮。查了半天,发现是GPIO的初始电平设反了。你想想看,如果外部电路是低电平点亮,你初始设成High,那LED当然不亮。所以一定要先看原理图。

4.3 配置USART:让芯片开口说话

串口是嵌入式开发者的“眼睛”。没有串口打印,调试就像蒙着眼睛走路。

我选USART1,引脚是PA9(TX)和PA10(RX)。在Pinout视图中把这两个引脚配成USART1_TXUSART1_RX。然后去USART1配置页面:

参数 设置值 说明
Mode Asynchronous 异步模式,最常用
Baud Rate 115200 波特率,我习惯用这个
Word Length 8 Bits 数据位8位
Parity None 无校验
Stop Bits 1 1位停止位
注意: 波特率要和你的串口工具一致。我曾经因为波特率设成了9600,而调试工具用的是115200,结果打印出来全是乱码。折腾了半小时才发现。

配置完USART后,记得在NVIC设置中使能USART1全局中断。虽然我们暂时只用轮询方式发送,但后面做数据接收时要用到中断。

4.4 SysTick定时器:HAL库的心跳

SysTick是Cortex-M内核自带的24位递减计数器。HAL库用它来做HAL_Delay()和超时判断。

在CubeMX的SYS配置页面,Timebase Source默认就是SysTick。我建议你不要改它。为什么?因为SysTick是内核级的外设,所有Cortex-M芯片都有,移植性最好。

SysTick的配置其实很简单:

  • 时钟源:一般是HCLK/8,或者直接HCLK
  • 重装载值:决定中断频率
  • 使能中断:让HAL库的HAL_IncTick()每1ms调用一次

CubeMX会自动帮你算好这些参数。你只需要在SystemClock_Config()里确认时钟树正确就行。

核心理解: SysTick每1ms产生一次中断,HAL库的HAL_GetTick()返回的就是从启动到现在经过的毫秒数。所有延时、超时判断都依赖这个计数器。

4.5 生成代码与验证

点击GENERATE CODE,CubeMX会生成一个完整的MDK-ARM或IAR工程。我用的是MDK,所以选MDK-ARM V5

生成后,打开工程,在main.cwhile(1)里写点测试代码:

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();

  while (1)
  {
    HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    HAL_Delay(500);
    printf("Hello from STM32!\r\n");
  }
}

等等,printf怎么用?你需要重定向fputc函数:

int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
  return ch;
}

把这个函数加到main.c里,记得包含<stdio.h>。这样printf就能通过串口输出了。

避坑指南: 我曾经在重定向fputc时忘了加FILE *f参数,编译报错。还有一次是HAL_UART_Transmit的超时时间设得太短,数据没发完就返回了。建议超时时间至少设100ms。

4.6 验证结果

编译下载后,打开串口工具(我用的是Putty或SSCOM),设置115200波特率。你应该能看到:

  • 板子上的LED以1Hz的频率闪烁
  • 串口每500ms打印一次"Hello from STM32!"

如果LED不闪,检查GPIO引脚和电平配置。如果串口没输出,检查波特率、引脚连接、还有fputc重定向是否正确。

嗯,到这里,基础工程就搭好了。你想想看,一个能点灯、能打印、能跑时间的STM32,是不是已经活过来了?下一章,我们就让它连上网络,开始和云端对话。