☰
实验2 3
2026/9/30 17:14:56 网站建设 项目流程

实验任务2:标准外设库方式控制LED流水灯

1)工程项目创建与标准外设库文件添加详细过程

(1)新建工程文件夹结构

一、创建一个keil工程(寄存器版)
打开keil,点击Project,选择New uVision Project
选择 CPU 型号,根据开发板进行选择

在线添加库文件
用寄存器控制 STM32 时,不需要在线添加库文件,可以直接关掉。
添加文件
①添加已经存在文件
在新建的工程中添加启动文件(startup_stm32f10x_hd.s),该文件可以先到固件库中复制到此处startup_stm32f10x_hd.s。
②创建新文件
stm32f10x.h
手动新建,用于存放寄存器映射的代码,暂时为空。
main.c
手动新建,用于存放 main 函数,暂时为空。
配置魔术棒选项卡
①Target设置

②Output设置

③Listing设置

④Debug设置

⑤Utilities设置

⑥Debug Settings设置

(3)标准外设库方式LED流水灯代码

#include “stm32f10x.h”
#include “stm32f10x_gpio.h”
#include “stm32f10x_rcc.h”

/* 软件延时函数,约1秒 */
void Delay_ms(unsigned int ms)
{
unsigned int i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 7200; j++);
}

int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

/* 使能GPIOA、GPIOB、GPIOC时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE); /* 配置PA0、PB9、PC13为推挽输出 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_Init(GPIOC, &GPIO_InitStructure); /* 初始全部熄灭 */ GPIO_SetBits(GPIOA, GPIO_Pin_0); GPIO_SetBits(GPIOB, GPIO_Pin_9); GPIO_SetBits(GPIOC, GPIO_Pin_13); while(1) { /* PA0点亮 */ GPIO_ResetBits(GPIOA, GPIO_Pin_0); GPIO_SetBits(GPIOB, GPIO_Pin_9); GPIO_SetBits(GPIOC, GPIO_Pin_13); Delay_ms(1000); /* PB9点亮 */ GPIO_SetBits(GPIOA, GPIO_Pin_0); GPIO_ResetBits(GPIOB, GPIO_Pin_9); GPIO_SetBits(GPIOC, GPIO_Pin_13); Delay_ms(1000); /* PC13点亮 */ GPIO_SetBits(GPIOA, GPIO_Pin_0); GPIO_SetBits(GPIOB, GPIO_Pin_9); GPIO_ResetBits(GPIOC, GPIO_Pin_13); Delay_ms(1000); }

2)LED亮/灭周期分析与Keil逻辑分析仪观察



可以观察并计算得知

1. GPIO端口输出波形分析

从 Logic Analyzer 中可以看到三路输出波形(红、绿、蓝),分别对应代码中操作 GPIOA 的 PA0、PA1、PA2 端口。

  • 波形特征:三路波形呈现顺序脉冲的形态,并非同时跳变。
  • 代码逻辑对应:结合下方main.c第71行GPIO_Write(GPIOA, led_pins[led_index]);以及注释001 -> 010 -> 100 -> 001可以断定:
    • 红色波形(通道0/PA0):表现为高电平(1),其余为低电平(0),对应001。
    • 绿色波形(通道1/PA1):表现为高电平(1),其余为低电平(0),对应010。
    • 蓝色波形(通道2/PA2):表现为高电平(1),其余为低电平(0),对应100。
    • 三者按照红 -> 绿 -> 蓝的顺序依次输出高电平,形成循环流水灯效果。

2. 时序状态正确性判断

时序状态完全正确。

  • 逻辑正确:波形展示了1->0,0->1的交替,完美执行了代码中依次递增led_index并查表输出的逻辑。
  • 无重叠(No Overlap):从波形可以清晰看出,当一路(如红色)拉高时,另外两路必定处于低电平,未出现两个或三个LED同时点亮的情况。这严格符合代码注释中的要求No overlap between ON states。

3. 高低电平转换周期(LED闪烁周期)计算

可以通过 Command 窗口底部的精准打印信息(UVA measured均为代码内部计时打印)来进行确切计算,这比肉眼观察示波器的时间轴更精确:

  • 高电平持续时间:PA0 HIGH = 0.99999994 s≈1.0 秒(即单个LED点亮的时间)
  • 低电平持续时间:PA0 LOW = 1.99999903 s≈2.0 秒(即该LED熄灭、等待其他两个LED点亮的时间)
  • LED闪烁总周期:PERIOD = 2.99999847 s≈3.0 秒(高电平 + 低电平 = 1 + 2 = 3秒)

结论:
代码逻辑正确,时序无重叠。系统以 1 秒为间隔顺序切换三路LED(每一路亮1秒、灭2秒),单个LED完整的闪烁周期为3秒。

实验任务3:HAL库+按键中断控制流水灯暂停/恢复

1)实验要求

改用HAL库控制流水灯,增加一个按键:第一次按下按键时暂停流水灯(当前LED保持点亮),再次按下按键时恢复运行,接着暂停前剩余时间继续计时。

2)HAL库按键中断配置

#include “stm32f1xx_hal.h”

#define KEY_PIN GPIO_PIN_11
#define KEY_PORT GPIOB

static void KEY_EXTI_Init(void)
{
GPIO_InitTypeDef gpio = {0};

__HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_AFIO_CLK_ENABLE(); gpio.Pin = KEY_PIN; gpio.Mode = GPIO_MODE_IT_FALLING; /* 下降沿触发 */ gpio.Pull = GPIO_PULLUP; /* 内部上拉 */ HAL_GPIO_Init(KEY_PORT, &gpio); /* 使能EXTI15_10中断线 */ HAL_NVIC_SetPriority(EXTI15_10_IRQn, 1U, 0U); HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

4)中断处理与流水灯控制逻辑

采用“中断提交请求、主循环处理请求”的方法:中断函数只翻转一个状态标志,主循环检测标志后执行暂停/恢复逻辑。这样可以缩短中断执行时间,避免在中断中使用阻塞延时

/* 全局变量/
volatile uint8_t run_flag = 1; /
1=运行,0=暂停 */
uint32_t last_tick = 0;
uint8_t led_index = 0;

/* 中断回调函数(由HAL_GPIO_EXTI_IRQHandler调用)/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == KEY_PIN)
{
run_flag = !run_flag; /
翻转运行状态 */
}
}

/* LED控制函数/
void LED_Set(uint8_t index)
{
/
先全部熄灭 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2, GPIO_PIN_RESET);

/* 点亮指定LED */ switch(index) { case 0: HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); break; /* 蓝 */ case 1: HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); break; /* 红 */ case 2: HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); break; /* 绿 */ } /* 主循环 */ while (1) { if (run_flag) { uint32_t now = HAL_GetTick(); if (now - last_tick >= 1000) { last_tick = now; led_index = (led_index + 1) % 3; LED_Set(led_index); } } /* run_flag=0时,什么都不做,当前LED保持点亮 */ }

5)中断处理流程说明

当按键按下时,PB11电平由高变低,产生下降沿,触发EXTI11中断:

  1. 硬件自动进入EXTI15_10_IRQHandler()

  2. HAL库调用HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11)

  3. 该函数清除中断标志位,然后调用弱定义的HAL_GPIO_EXTI_Callback()

  4. 重写的回调函数翻转run_flag

流水灯hal控制

五、问题与改进

  1. 软件延时不准:实际项目应使用定时器中断或系统滴答定时器产生精确延时。
  2. 按键抖动:机械按键可能多次触发中断,可加 10~20ms 软件消抖或硬件 RC 滤波。
  3. PC13 板载 LED:低电平点亮,编程时注意逻辑反转。
  4. 逻辑分析仪使用:需勾选 Debug Information,添加信号时写完整名称如GPIOA_ODR.0,否则可能显示 Unknown Signal。

六、心得体会

通过本次实验,掌握了 STM32 GPIO 的三种开发方式,理解了时钟使能、端口配置、电平控制、外部中断和软件仿真调试的基本流程。寄存器方式有助于深入理解硬件,标准库和 HAL 库则提高了开发效率。逻辑分析仪是验证时序的有效工具,按键中断实验进一步强化了“中断快进快出、主循环处理任务”的设计思想,为后续定时器、串口、RTOS 等复杂应用打下基础。
Information,添加信号时写完整名称如GPIOA_ODR.0,否则可能显示 Unknown Signal。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询