前言
本文是本人备赛物联网赛项的学习笔记,主要供本人学习、复习,不是经验分享或教学,若有错误,大佬轻喷。
第一章 核心外设理论基础
动手配置前,先快速了解每个外设的核心原理,避免只抄参数不懂逻辑。
1.1 TIM 定时器:精准定时的核心
STM32WL 系列芯片内置丰富的定时器资源,本次使用TIM17 通用 16 位定时器,核心作用是实现精准定时中断,是嵌入式系统 "时序控制" 的核心。
核心计算公式(重中之重)
bash
定时时间(秒) = (分频系数PSC + 1) × (自动重装载值ARR + 1) / 定时器主频(Hz)
- 分频系数 PSC:对定时器主频分频,16 位寄存器,取值范围 0~65535
- 自动重装载值 ARR:定时器计数上限,计到该值触发中断,16 位寄存器,取值范围 0~65535
- 定时器主频:本次配置系统主频为 48MHz,因此定时器主频为 48MHz
实战配置示例 :要实现 1ms 定时中断,设置PSC=47,ARR=999,代入公式:
bash
定时时间 = (47+1)×(999+1)/48000000 = 48×1000/48000000 = 0.001秒 = 1ms
即每隔 1ms,定时器触发一次中断,可在回调函数中编写定时执行的逻辑。
1.2 ADC 模数转换:把模拟量变成数字量
ADC 的作用是把引脚输入的模拟电压(如电位器、传感器输出的 0~3.3V 电压)转换成单片机能识别的数字量。本次使用 STM32WL 内置 12 位 ADC,核心参数:
- 分辨率:12 位,采集数值范围 0~4095(2¹²-1)
- 电压对应关系:0V 对应数值 0,3.3V 对应数值 4095,可通过数值换算实际电压
- 采集通道:使用 ADC 通道 3,对应开发板 PB4 引脚
1.3 USART 串口:单片机和电脑的 "桥梁"
我们最常用UART 异步串行通信,仅需 TX(发送)、RX(接收)两根线,即可实现单片机与电脑的数据传输,是调试代码、查看数据的核心工具。
蓝桥杯物联网赛项标准串口参数(全程使用该配置):
- 波特率:115200 Bits/s
- 数据位:8 位
- 校验位:无
- 停止位:1 位简称
8N1,电脑端串口助手必须与该参数完全一致,否则会出现乱码。
同时我们使用IDLE 中断 + DMA实现串口不定长数据接收,相比传统轮询接收,不占用 CPU 资源,接收效率更高。
1.4 其他外设基础
- RTC 实时时钟:单片机内置时钟外设,掉电后可通过备用电池继续走时,用于获取年、月、日、时、分、秒
- OLED 显示屏:通过 I2C 总线驱动,0.96 寸 128*64 分辨率,用于显示采集数据、时间等
- GPIO 通用输入输出口:用于驱动 LED 灯(输出模式)、读取按键状态(输入模式)
第二章 STM32CubeMX 全流程配置
所有配置严格对应 STM32WLE5CCU6 主控(CT127C 开发板),每一步都标注了操作位置和参数,跟着操作零出错。
步骤 1:新建工程,选择目标芯片
- 打开 STM32CubeMX 软件,点击首页的「ACCESS TO MCU SELECTOR」,进入芯片选择界面
- 搜索框输入
STM32WLE5CCU6,右侧列表选中该型号,点击右上角「Start Project」完成工程新建
步骤 2:调试接口配置(必做,否则程序无法下载)
- 左侧「System Core」下拉菜单,选择「SYS」
- 右侧「Debug」选项,下拉选择「Serial Wire」(串行线调试模式,对应开发板 DAPLink 调试器)
- 其余参数保持默认
步骤 3:系统时钟树配置(核心,决定外设时序准确性)
这一步是所有外设正常工作的基础,我们将系统主频配置为 48MHz。
- 左侧「System Core」下拉菜单,选择「RCC」
- 右侧「High Speed Clock (HSE)」选择「Crystal/Ceramic Resonator」(外部高速晶振);「Low Speed Clock (LSE)」选择「Crystal/Ceramic Resonator」(外部低速晶振,给 RTC 用)
- 顶部切换到「Clock Configuration」时钟树选项卡,按以下参数配置:
- HSE 输入频率:32MHz(开发板板载晶振参数)
- 系统时钟源选择 MSI,MSI 范围配置为 RCC_MSIRANGE_11,系统主频 SYSCLK 设置为 48MHz
- 所有 AHB/APB 分频器均设置为 / 1,确保所有外设主频均为 48MHz
- USART2 时钟源选择系统时钟 SYSCLK,确保波特率准确
- 配置完成后,界面无红色报错提示,说明时钟配置正确
步骤 4:TIM17 定时器配置(1ms 定时中断)
配置 TIM17 实现 1ms 一次的定时中断,用于 LED 闪烁、计时等功能。
- 左侧「Timers」下拉菜单,选择「TIM17」
- 模式配置:
- 「Mode」栏勾选「Activated」,激活定时器
- 「One Pulse Mode」选择「Disable」,关闭单脉冲模式,实现循环定时
- 参数配置(Counter Settings):
- 「Prescaler (PSC)」分频系数:47
- 「Counter Mode」计数模式:Up(向上计数)
- 「Counter Period (ARR)」自动重装载值:999
- 「Internal Clock Division (CKD)」:不分频
- 「auto-reload preload」:Enable,使能自动重装载预装载
- 中断配置:
- 点击「NVIC Settings」选项卡,勾选「TIM17 global interrupt」,开启定时器全局中断
- 中断优先级保持默认即可

步骤 5:ADC 采集配置
配置 ADC 通道 3(对应 PB4 引脚),实现模拟电压采集。
- 左侧「Analog」下拉菜单,选择「ADC」
- 模式与参数配置:
- 「Clock Prescaler」:ADC_CLOCK_SYNC_PCLK_DIV2,时钟 2 分频
- 「Resolution」:ADC_RESOLUTION_12B,12 位分辨率
- 「Data Alignment」:ADC_DATAALIGN_RIGHT,数据右对齐
- 「Scan ConvMode」:ADC_SCAN_DISABLE,关闭扫描模式(单通道采集)
- 「Continuous ConvMode」:DISABLE,关闭连续转换,单次触发采集
- 「External TrigConv」:ADC_SOFTWARE_START,软件触发转换
- 其余参数保持默认
- 通道配置:
- 「Channel」选择 ADC_CHANNEL_3
- 「Rank」选择 ADC_REGULAR_RANK_1
- 「SamplingTime」选择 ADC_SAMPLETIME_1CYCLE_5,采样周期 1.5 个时钟
- 配置完成后,CubeMX 会自动把 PB4 引脚配置为 ADC 模拟输入模式,无需手动设置
步骤 6:USART2 串口配置(IDLE 中断 + DMA 接收)
配置 USART2,实现串口发送和 DMA 不定长接收,对应开发板 PA2 (TX)、PA3 (RX) 引脚。
- 引脚映射:芯片引脚预览图中,PA2 引脚选择「USART2_TX」,PA3 引脚选择「USART2_RX」,配置完成后引脚变为绿色高亮
- 串口参数配置:
- 左侧「Connectivity」下拉菜单,选择「USART2」
- 「Mode」选择「Asynchronous」,异步通信模式
- 「Configuration」按蓝桥杯标准配置:
- Baud Rate 波特率:115200 Bits/s
- Word Length 数据位:8 Bits
- Parity 校验位:None
- Stop Bits 停止位:1
- Data Direction 数据方向:Receive and Transmit(收发双向)
- Oversampling 过采样:16 Samples
- DMA 配置:
- 点击「DMA Settings」选项卡,点击「Add」添加 DMA 通道
- 「Direction」选择「Peripheral To Memory」(外设到内存,串口接收)
- 「Request」选择「USART2_RX」
- 「Mode」选择「Normal」,正常模式
- 「Data Width」选择「Byte」,字节宽度
- 「Increment Address」:Memory 勾选,Peripheral 不勾选(内存地址自增,外设地址固定)
- 中断配置:
- 点击「NVIC Settings」选项卡,勾选「USART2 global interrupt」,开启串口全局中断
- 确保「DMA1 Channel1 global interrupt」已勾选(DMA 通道 1 中断)
步骤 7:I2C1 配置(OLED 屏驱动)
配置 I2C1,用于驱动 0.96 寸 OLED 屏,对应开发板 PB6 (I2C1_SCL)、PB7 (I2C1_SDA) 引脚。
- 引脚映射:芯片引脚预览图中,PB6 引脚选择「I2C1_SCL」,PB7 引脚选择「I2C1_SDA」
- 左侧「Connectivity」下拉菜单,选择「I2C1」
- 「Mode」选择「I2C」,标准 I2C 模式
- 参数保持默认:「I2C Speed Mode」为 Fast Mode,频率 400KHz
- 无需额外开启中断,阻塞式 I2C 传输即可满足 OLED 显示需求
步骤 8:RTC 实时时钟配置
配置 RTC 外设,实现实时时钟功能。
- 左侧「Timers」下拉菜单,选择「RTC」
- 模式配置:
- 勾选「Activate Clock Source」,激活时钟源
- 勾选「Activate Calendar」,激活日历功能
- 时钟配置:RTC 时钟源选择 LSE 外部低速晶振(32.768KHz)
- 日期与时间初始化:可在「Parameter Settings」设置初始的年、月、日、时、分、秒
- 其余参数保持默认
步骤 9:GPIO 配置(LED 与按键)
配置 GPIO 引脚,用于驱动 LED 灯和读取按键状态,CT127C 开发板对应引脚:
- LED1:PB2,LED2:PB12,LED3:PB10(推挽输出,低电平点亮)
- 按键 ASW1:PA8(上拉输入,低电平按下)
- 引脚映射:芯片引脚预览图中,对应引脚选择「GPIO_Output」(LED)或「GPIO_Input」(按键)
- 左侧「System Core」下拉菜单,选择「GPIO」,按以下参数配置:
- LED 引脚(PB2/PB12/PB10):
- 「GPIO output level」:High,初始高电平(LED 熄灭)
- 「GPIO mode」:Output Push Pull,推挽输出
- 「GPIO Pull-up/Pull-down」:No pull-up and no pull-down,无上下拉
- 「Maximum output speed」:Low,低速
- 按键引脚(PA8):
- 「GPIO mode」:Input mode,输入模式
- 「GPIO Pull-up/Pull-down」:Pull-up,上拉模式
- LED 引脚(PB2/PB12/PB10):
步骤 10:工程管理与代码生成配置(必做)
- 顶部切换到「Project Manager」选项卡
- 「Project」栏目配置:
- Project Name:工程名称(严禁使用中文、空格、特殊字符)
- Project Location:工程保存路径(严禁使用中文路径)
- Toolchain/IDE:下拉选择「MDK-ARM V5」(对应 Keil5 开发环境)
- 左侧切换到「Code Generator」选项卡,核心勾选项:
- ✅ Generate peripheral initialization as a pair of '.c/.h' files per peripheral(每个外设生成独立的.c/.h 文件)
- ✅ Keep User Code when re-generating(重新生成代码时保留用户代码)
- 其余参数保持默认,点击右上角「GENERATE CODE」,等待代码生成完成后,点击「Open Project」直接打开 Keil MDK 工程
第三章 Keil MDK 工程配置与代码编写
⚠️ 所有用户代码必须写在/* USER CODE BEGIN X */和/* USER CODE END X */之间,否则重新生成 CubeMX 代码时,用户编写的内容会被直接覆盖!我们将所有自定义功能封装在app.c和app.h文件中,替代原有的zsdz相关文件。
3.1 Keil 工程基础配置(必做,解决 printf 卡死问题)
- 打开 Keil 工程后,点击顶部魔术棒图标(Options for Target),打开配置界面
- 切换到「Target」选项卡,勾选「Use MicroLIB」
- 点击「OK」保存配置
关键说明:MicroLIB 是 Keil 针对嵌入式系统优化的微型 C 标准库,不勾选该选项,使用 printf 会因半主机模式导致程序卡死,是初学者最高频踩坑点。
3.2 新建 app.c 与 app.h 自定义文件
我们把所有自定义功能函数放在这两个文件中,实现模块化管理,方便复用。
3.2.1 新建 app.h 头文件(函数声明与宏定义)
- Keil 左侧工程文件树,右键点击「Application/User/Core」文件夹,选择「Add New Item to Group 'Application/User/Core'...」
- 弹出窗口中,选择「Header File (.h)」,文件名为
app.h,点击「Add」 - 在
app.h中写入以下代码:
cpp
#ifndef __APP_H
#define __APP_H
// 包含必要的HAL库头文件
#include "stm32wlxx_hal.h"
#include "string.h"
#include "stdio.h"
#include "gpio.h"
#include "oled.h"
/************************* 宏定义 *************************/
// LED状态宏定义
#define APP_ON 1
#define APP_OFF 2
#define APP_TOGGLE 3
// OLED显示行号定义
#define APP_OLED_LINE1 0
#define APP_OLED_LINE2 2
/************************* 结构体定义 *************************/
// RTC时间结构体
typedef struct
{
unsigned char Year;
unsigned char Month;
unsigned char WeekDay;
unsigned char Date;
unsigned char Hours;
unsigned char Minutes;
unsigned char Seconds;
}RTC_APP;
/************************* 全局变量声明 *************************/
extern RTC_APP rtc_value;
/************************* 函数声明 *************************/
// LED操作函数
void app_write_AL(unsigned short int ALx,unsigned char state);
unsigned char app_read_AL(unsigned short int ALx);
// 按键读取函数
unsigned char app_read_ASW(GPIO_TypeDef *ASWx);
// 串口发送函数
void app_tx_UART(const unsigned char *Data);
void app_UART_IDLE_rx(void);
void app_UART_rx_loop(void);
void app_UART_rx_deal(void);
// RTC时间读取函数
void app_read_RTC(void);
// OLED显示函数
void app_write_OLED(unsigned char Lin, unsigned char *Data);
// ADC采集函数
unsigned short int app_read_ADC(void);
// 外设初始化函数
void app_init(void);
#endif
3.2.2 新建 app.c 源文件(函数功能实现)
- 同样右键点击「Application/User/Core」文件夹,选择「Add New Item to Group 'Application/User/Core'...」
- 选择「C File (.c)」,文件名为
app.c,点击「Add」 - 在
app.c中写入以下代码:
cpp
#include "app.h"
/************************* 全局变量定义 *************************/
// 串口DMA接收相关定义
#define UART_RX_BUFFER_LEN 100 // 串口接收缓存区最大长度
unsigned char g_u8uart_rx_len; // 接收到的一帧数据长度
unsigned char g_u8uart_rx_end_flag; // 一帧数据接收完成标志位
unsigned char g_u8uart_rx_buffer[UART_RX_BUFFER_LEN]; // 接收数据缓存数组
// RTC时间全局变量
RTC_APP rtc_value;
/************************* LED操作函数 *************************/
// 函数名:app_write_AL
// 作用:设置LED灯状态
// 形参:ALx - LED引脚号;state - LED状态(APP_ON/APP_OFF/APP_TOGGLE)
void app_write_AL(unsigned short int ALx,unsigned char state)
{
switch (state)
{
case APP_ON:
HAL_GPIO_WritePin(GPIOB, ALx, GPIO_PIN_RESET); // 低电平点亮LED
break;
case APP_OFF:
HAL_GPIO_WritePin(GPIOB, ALx, GPIO_PIN_SET); // 高电平熄灭LED
break;
case APP_TOGGLE:
HAL_GPIO_TogglePin(GPIOB, ALx); // 电平翻转
break;
default:
break;
}
}
// 函数名:app_read_AL
// 作用:读取LED灯当前状态
// 形参:ALx - LED引脚号
// 返回值:LED当前状态(APP_ON/APP_OFF)
unsigned char app_read_AL(unsigned short int ALx)
{
unsigned char state=HAL_GPIO_ReadPin(GPIOB, ALx);
if(state == GPIO_PIN_SET)
return APP_OFF;
else
return APP_ON;
}
/************************* 按键读取函数 *************************/
// 函数名:app_read_ASW
// 作用:读取按键状态,带消抖处理
// 形参:ASWx - 按键对应的GPIO端口
// 返回值:按键状态(APP_ON-按下 / APP_OFF-松开)
unsigned char app_read_ASW(GPIO_TypeDef *ASWx)
{
unsigned char state = APP_OFF;
if(HAL_GPIO_ReadPin(ASWx,GPIO_PIN_8) == GPIO_PIN_RESET)
{
HAL_Delay(20); // 延时消抖
if(HAL_GPIO_ReadPin(ASWx,GPIO_PIN_8) == GPIO_PIN_RESET)
{
state = APP_ON;
while(HAL_GPIO_ReadPin(ASWx,GPIO_PIN_8) == GPIO_PIN_RESET); // 等待按键松开
}
}
return state;
}
/************************* 串口操作函数 *************************/
// 函数名:app_tx_UART
// 作用:串口发送字符串
// 形参:Data - 要发送的字符串首地址
void app_tx_UART(const unsigned char *Data)
{
extern UART_HandleTypeDef huart2;
HAL_UART_Transmit(&huart2,Data,strlen((const char *)Data),0xFFFF);
}
// 函数名:fputc
// 作用:printf函数重定向,实现printf串口输出
// 说明:重写C库fputc底层函数,将printf输出指向USART2
int fputc(int ch,FILE *f)
{
extern UART_HandleTypeDef huart2;
HAL_UART_Transmit(&huart2,(unsigned char*)&ch,1,0xFFFF);
return ch;
}
// 函数名:app_UART_IDLE_rx
// 作用:串口IDLE中断处理函数,判断一帧数据是否接收完成
// 说明:在串口中断服务函数中调用
void app_UART_IDLE_rx(void)
{
extern UART_HandleTypeDef huart2;
extern DMA_HandleTypeDef hdma_usart2_rx;
if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE)==SET) // 检测到IDLE空闲标志位
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2); // 清除IDLE标志位
HAL_UART_DMAStop(&huart2); // 停止DMA传输
// 计算接收到的数据长度:总缓存长度 - 剩余未传输长度
g_u8uart_rx_len = UART_RX_BUFFER_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
g_u8uart_rx_end_flag = 1; // 置位接收完成标志
}
}
// 函数名:app_UART_rx_loop
// 作用:串口接收循环处理函数,在main函数主循环中调用
void app_UART_rx_loop(void)
{
extern UART_HandleTypeDef huart2;
if(g_u8uart_rx_end_flag) // 判断是否接收到一帧完整数据
{
app_UART_rx_deal(); // 处理接收到的数据
// 清除标志与缓存,重新开启DMA接收
g_u8uart_rx_len = 0;
g_u8uart_rx_end_flag = 0;
HAL_UART_Receive_DMA(&huart2,g_u8uart_rx_buffer,UART_RX_BUFFER_LEN);
}
}
// 函数名:app_UART_rx_deal
// 作用:串口接收数据处理函数,这里实现串口回环(收到什么发回什么)
// 可根据需求修改,比如解析指令、控制LED等
void app_UART_rx_deal(void)
{
extern UART_HandleTypeDef huart2;
HAL_UART_Transmit(&huart2,g_u8uart_rx_buffer,g_u8uart_rx_len,0xFFFF);
}
/************************* RTC时间读取函数 *************************/
// 函数名:app_read_RTC
// 作用:读取RTC实时时钟,存入rtc_value全局变量
void app_read_RTC(void)
{
extern RTC_HandleTypeDef hrtc;
RTC_DateTypeDef data_value;
RTC_TimeTypeDef time_value;
// 必须先读时间,再读日期,否则会出现数据不同步问题
HAL_RTC_GetTime(&hrtc,&time_value,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&data_value,RTC_FORMAT_BIN);
// 赋值到全局结构体
rtc_value.Year = data_value.Year;
rtc_value.Month = data_value.Month;
rtc_value.WeekDay = data_value.WeekDay;
rtc_value.Date = data_value.Date;
rtc_value.Hours = time_value.Hours;
rtc_value.Minutes = time_value.Minutes;
rtc_value.Seconds = time_value.Seconds;
}
/************************* OLED显示函数 *************************/
// 函数名:app_write_OLED
// 作用:OLED屏字符串显示封装
// 形参:Lin - 显示行号;Data - 要显示的字符串
void app_write_OLED(unsigned char Lin, unsigned char *Data)
{
OLED_ShowString(0,Lin,Data,16);
}
/************************* ADC采集函数 *************************/
// 函数名:app_read_ADC
// 作用:单次ADC采集,返回采集到的原始数值
// 返回值:12位ADC原始值(0~4095)
unsigned short int app_read_ADC(void)
{
extern ADC_HandleTypeDef hadc;
HAL_ADC_Start(&hadc); // 启动ADC转换
// 等待转换完成,超时时间200ms
while(HAL_ADC_PollForConversion(&hadc, 200) != HAL_OK);
return HAL_ADC_GetValue(&hadc); // 返回采集结果
}
/************************* 外设初始化函数 *************************/
// 函数名:app_init
// 作用:所有自定义外设初始化,在main函数中调用
void app_init(void)
{
extern UART_HandleTypeDef huart2;
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); // 使能串口IDLE中断
HAL_UART_Receive_DMA(&huart2,g_u8uart_rx_buffer,UART_RX_BUFFER_LEN); // 开启串口DMA接收
OLED_Init(); // OLED屏初始化
}
3.3 中断回调函数编写
3.3.1 定时器中断回调函数
定时器中断回调函数是 HAL 库的标准弱函数,我们重写该函数实现 1ms 中断逻辑。打开工程中的stm32wlxx_it.c文件,找到/* USER CODE BEGIN 1 */代码块,写入以下代码:
cpp
/* USER CODE BEGIN 1 */
#include "app.h"
// 定时器更新中断回调函数,1ms触发一次
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
extern TIM_HandleTypeDef htim17;
static unsigned int count = 0; // 静态计数变量
if(htim->Instance == TIM17) // 判断是否是TIM17触发的中断
{
if(++count >= 1000) // 1ms×1000=1s,每秒执行一次
{
count = 0;
app_write_AL(GPIO_PIN_2, APP_TOGGLE); // 翻转LED1状态,实现秒闪
}
}
}
/* USER CODE END 1 */
3.3.2 串口中断服务函数修改
在stm32wlxx_it.c文件中,找到USART2_IRQHandler函数,修改为以下内容,调用 IDLE 中断处理函数:
cpp
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
app_UART_IDLE_rx(); // 调用串口IDLE中断处理函数
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
3.4 main 函数编写
打开main.c文件,按以下步骤修改,实现完整的业务逻辑。
3.4.1 包含头文件
找到/* USER CODE BEGIN Includes */代码块,添加自定义头文件:
cpp
/* USER CODE BEGIN Includes */
#include "app.h"
#include "oled.h"
/* USER CODE END Includes */
3.4.2 外设初始化
找到main函数中的/* USER CODE BEGIN 2 */代码块,调用初始化函数,开启定时器中断:
cpp
/* USER CODE BEGIN 2 */
app_init(); // 自定义外设初始化
HAL_TIM_Base_Start_IT(&htim17); // 开启TIM17定时器更新中断
/* USER CODE END 2 */
3.4.3 主循环逻辑编写
找到while(1)主循环,修改为以下内容,实现 ADC 采集、串口打印、OLED 显示、按键检测、串口数据处理:
cpp
/* Infinite loop */
/* USER CODE BEGIN WHILE */
unsigned char oled_buf[32]; // OLED显示缓存数组
unsigned short adc_value; // ADC采集值
while (1)
{
// 1. ADC采集与串口打印
adc_value = app_read_ADC();
printf("ADC采集值:%d,对应电压:%.2fV\r\n",adc_value,(float)adc_value*3.3/4095);
// 2. RTC时间读取与OLED显示
app_read_RTC();
sprintf((char *)oled_buf,"Time:%02d:%02d:%02d",rtc_value.Hours,rtc_value.Minutes,rtc_value.Seconds);
app_write_OLED(APP_OLED_LINE1, oled_buf);
// 3. ADC数值OLED显示
sprintf((char *)oled_buf,"ADC:%d",adc_value);
app_write_OLED(APP_OLED_LINE2, oled_buf);
// 4. 按键检测,按下按键切换LED2状态
if(app_read_ASW(GPIOA) == APP_ON)
{
app_write_AL(GPIO_PIN_12, APP_TOGGLE);
}
// 5. 串口接收数据处理
app_UART_rx_loop();
// 延时500ms,控制刷新频率
HAL_Delay(500);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
3.5 代码编译
所有代码编写完成后,点击 Keil 界面顶部的 Build 按钮(编译图标),等待编译完成,确保 0 错误 0 警告。
第四章 实验现象与验证
4.1 程序下载
- 用 USB 线连接开发板与电脑,开发板自动上电
- 点击 Keil 界面顶部的 Download 按钮(下载图标),等待程序下载完成
- 下载完成后,开发板自动复位,运行编写的程序
4.2 实验现象验证
现象 1:LED 秒闪效果
程序运行后,开发板上的 LED1 每隔 1 秒翻转一次状态,亮灭交替,说明 TIM17 定时器 1ms 中断配置成功,回调函数逻辑正常。
现象 2:串口数据打印
- 打开电脑端串口调试助手(推荐 SSCOM、野火串口助手)
- 串口参数配置:
- 端口号:选择开发板对应的 COM 口(设备管理器中查看)
- 波特率:115200
- 数据位:8
- 停止位:1
- 校验位:无
- 流控:无
- 点击「打开串口」,即可看到串口助手每隔 500ms 打印一次 ADC 采集值和对应的电压值
- 串口回环测试:串口助手发送框输入任意字符,点击发送,开发板会把收到的内容原封不动发回,说明串口 DMA+IDLE 接收功能正常
现象 3:OLED 屏显示
OLED 屏第一行显示当前的 RTC 时间(时:分: 秒),第二行显示当前的 ADC 采集值,数值会随着输入电压的变化实时更新。
【配图位置:现象 3 后,配图内容:OLED 屏显示时间和 ADC 数值的实物效果图】
现象 4:按键控制
按下开发板上的 ASW1 按键,LED2 会翻转一次状态(亮→灭 / 灭→亮),说明按键检测功能正常。