一、项目介绍
本项目完成任务一的基本任务。
本项目基于 MAX32690EVKIT 开发板,利用其板载按键和 GPIO 中断功能,实现了一个 按键控制 LED 闪烁 的系统。系统初始化 GPIO 下降沿中断,通过单次按键触发 LED 的闪烁状态切换:按一下按键,LED 开始以约 1Hz 的频率持续闪烁;再次按下按键,LED 停止闪烁。设计中加入了按键消抖处理,确保逻辑清晰、无误触发,实现了简洁可靠的人机交互控制。
二、简短的所有使用到的硬件介绍
1. 核心控制器:MAX32690 微控制器
- 内核架构:搭载 Arm® Cortex®-M4F CPU,主频高达 120MHz,并集成浮点运算单元(FPU),可高效完成复杂计算与算法处理。
- 存储资源:内置 3MB 闪存(Flash)与 1MB SRAM,满足代码存储与数据缓存需求。
- 低功耗协处理器:集成超低功耗 32位 RISC-V (RV32) 协处理器,可独立处理部分数据任务,进一步优化系统功耗。
- 无线连接:支持 Bluetooth® 5.2 低功耗(LE)射频,适用于物联网(IoT)、可穿戴设备及工业传感器等无线应用场景。
- 丰富外设接口:提供 I2C、SPI、UART/USART、USB 等多种通信接口。
- 宽工作温度范围:支持 -40°C 至 +105°C工业级温度范围,适应严苛环境。
2. 评估开发板:MAX32690EVKIT 评估套件
- 板载资源:
- 预编程的 MAX32690主芯片
- 128×128 分辨率 TFT 彩色显示屏(1.45英寸)
- 立体声音频编解码器(带 3.5mm 线路输入/输出插孔)
- CAN 总线 2.0接口(3引脚接线板)
- 板载 HyperRAM 扩展存储
- 两个通用 LED 指示灯,以及一个通用按键开关
- 蓝牙 2.4GHz 铰链天线(SMA 连接器)
- 可选板载高精度基准电压源
- 调试与供电:
- 附赠 MAX32625PICO 调试器及配套电缆,支持 SWD 调试
- 双 USB 2.0 Micro-B 接口(分别用于设备连接与串行 UART 通信)
- 板载 3.3V、1.8V 和 1.1V LDO 稳压器,并支持通过跳线测量各电源轨功耗
- 扩展接口:
- 所有 GPIO 信号通过 0.1 英寸排针引出,便于外设扩展
- 三个模拟输入(带可选滤波)通过 0.1 英寸排针引出
- I2C 端口可选上拉电阻跳线
- SWD 10 引脚调试接头
3. 项目中实际使用到的硬件资源
- 板载按键:用于触发 LED 闪烁模式的切换。
- 板载 LED:作为被控对象,根据按键指令进行闪烁或停止闪烁。
三、方案框图和项目设计思路介绍
方案框图:

框图说明:
按键按下触发 GPIO 中断 → 消抖后切换全局标志 blink_enabled。
定时器 0 每 500ms 产生一次中断,根据 blink_enabled 决定是否翻转 LED 输出引脚。
主循环空闲,可扩展其他功能。
项目设计思路介绍:
1. 设计目标
基于 MAX32690EVKIT 开发板,实现单按键控制 LED 闪烁;按一次按键 → LED 以约 1Hz 频率持续闪烁;再按一次按键 → LED 停止闪烁(保持熄灭)。要求消抖可靠,无误触发,逻辑清晰。
2. 软件架构
采用中断驱动 + 主循环轮询的混合架构,关键功能由硬件中断完成,确保实时性。
GPIO 中断服务函数:
检测到按键下降沿后,先进行软件消抖(MXC_Delay(100ms)),再执行 blink_enabled = !blink_enabled,仅切换状态标志,不阻塞其他中断。
定时器中断服务函数:
每 500ms 自动调用一次。若 blink_enabled == true,则调用 MXC_GPIO_OutToggle() 翻转 LED 状态;否则调用 MXC_GPIO_OutClr() 强制熄灭 LED。这样既实现了精确的 1Hz 对称闪烁,又能在停止闪烁时确保 LED 完全熄灭。
主循环:
本项目主循环为空(或可加入低功耗休眠指令),所有控制逻辑由中断完成,CPU 可在空闲时进入睡眠模式,降低功耗。
3. 关键设计细节
消抖处理:在 GPIO 中断中直接延时 100ms,简单有效,避免了外部 RC 滤波电路。
定时器配置:使用 MAX32690 的 TMR0 模块,工作在连续计数模式,比较值设为 PeripheralClock / (prescaler+1) 对应 500ms(即 0.5 秒),实现 1Hz 闪烁(亮 0.5s,灭 0.5s)。
标志变量:blink_enabled 声明为 volatile,确保编译优化时不会从寄存器读取过期值。
中断优先级:未显式设置,默认所有中断均为相同优先级。为避免嵌套,GPIO 中断中已禁用其他中断(通过 MXC_Delay 内部不依赖中断),实际测试中稳定可靠。
4. 扩展性说明
修改 period_ticks 即可调整闪烁频率(如 timer_tick_freq * 2 变为 2 秒周期)。
如需增加长按、双击等功能,可在 GPIO 中断中增加计时状态机。
四、调试软件介绍、软件流程图及关键代码介绍
调试软件介绍:
本项目采用 Visual Studio Code(VS Code) 作为集成开发环境,并基于 Analog Devices 官方提供的 CodeFusion Studio™ 扩展进行嵌入式开发与调试。CodeFusion Studio 是一个以 VS Code 为基础、面向 ADI 处理器和 MCU 的专业嵌入式软件开发平台,为开发者提供了从新建项目、代码编辑、编译烧录到硬件调试的全流程一体化支持。MSDK 则包含寄存器文件、外设驱动、系统启动文件、示例代码、文档和工具链等,是实现 MAX32690 开发的完整软件包。具体调试与开发配置分为以下两个层次:
编译工具链:采用 ARM GNU Toolchain(arm-none-eabi-gcc) 作为交叉编译器,编写 tasks.json 构建任务管理编译与链接。
调试适配器:采用 MAX32625PICO 板载调试器(或 J‑Link),通过 SWD 接口与目标芯片通信。OpenOCD 开源调试工具作为连接 GDB 与调试适配器的桥梁,支持 JTAG/SWD 调试、Flash 编程和脚本扩展。
软件流程图:

关键代码介绍:
1. 全局标志变量
volatile bool blink_enabled = false; // 闪烁使能标志,在中断中修改
作用:作为按键中断与定时器中断之间的信息传递通道。volatile 关键字防止编译器优化,确保两个中断都能读取到最新的标志值。
状态:false 表示第二个 LED 常灭;true 表示第二个 LED 以 1Hz 频率闪烁。
2. GPIO 初始化(按键输入 + LED 控制引脚)
/* 配置按键输入引脚(下降沿中断) */
gpio_interrupt.port = MXC_GPIO_PORT_IN;
gpio_interrupt.mask = MXC_GPIO_PIN_IN;
gpio_interrupt.pad = MXC_GPIO_PAD_NONE;
gpio_interrupt.func = MXC_GPIO_FUNC_IN;
gpio_interrupt.vssel = MXC_GPIO_VSSEL_VDDIOH;
gpio_interrupt.drvstr = MXC_GPIO_DRVSTR_0;
MXC_GPIO_Config(&gpio_interrupt);
MXC_GPIO_RegisterCallback(&gpio_interrupt, gpio_callback, &gpio_interrupt_status);
MXC_GPIO_IntConfig(&gpio_interrupt, MXC_GPIO_INT_FALLING);
MXC_GPIO_EnableInt(gpio_interrupt.port, gpio_interrupt.mask);
引脚功能:将按键引脚配置为输入模式,并启用下降沿触发中断(按键按下瞬间产生下降沿)。
回调注册:MXC_GPIO_RegisterCallback 将自定义回调函数 gpio_callback 绑定到该引脚的中断事件。
中断使能:MXC_GPIO_EnableInt 允许引脚的中断信号传递到 NVIC。
/* 配置第一个灯的输出引脚(跟随按键亮灭) */
gpio_out.port = MXC_GPIO_PORT_OUT;
gpio_out.mask = MXC_GPIO_PIN_OUT;
gpio_out.func = MXC_GPIO_FUNC_OUT;
MXC_GPIO_Config(&gpio_out);
输出方向:将该引脚设置为推挽输出模式,控制第一个 LED 的亮灭。
3. 定时器 TMR0 初始化(核心闪烁节拍)
void Timer_Init(void)
{
mxc_tmr_cfg_t tmr_cfg = {
.pres = MXC_TMR_PRES_1,
.mode = MXC_TMR_MODE_CONTINUOUS,
.bitMode = MXC_TMR_BIT_MODE_32,
.clock = MXC_TMR_APB_CLK,
};
MXC_TMR_Init(MXC_TMR0, &tmr_cfg, false);
uint32_t pclk_freq = PeripheralClock;
uint32_t timer_tick_freq = pclk_freq / (tmr_cfg.pres + 1);
uint32_t period_ticks = timer_tick_freq / 2; // 500ms
MXC_TMR_SetCompare(MXC_TMR0, period_ticks);
MXC_TMR_EnableInt(MXC_TMR0);
NVIC_EnableIRQ(TMR0_IRQn);
MXC_TMR_Start(MXC_TMR0);
}
定时器模式:配置为 32 位连续计数模式,时钟源为 APB 总线时钟,预分频为 1。
周期计算:period_ticks = (APB频率 / 分频值) × 0.5s,产生精确的 500ms 中断周期。
中断配置:使能定时器溢出中断,并注册对应的中断服务函数 TMR0_IRQHandler。
4. 中断服务函数
void gpio_callback(void *cbdata)
{
blink_enabled = !blink_enabled; // 切换闪烁模式
}
void TMR0_IRQHandler(void)
{
MXC_TMR_ClearFlags(MXC_TMR0);
if (blink_enabled) {
MXC_GPIO_OutToggle(MXC_GPIO_PORT_INTERRUPT_STATUS, MXC_GPIO_PIN_INTERRUPT_STATUS);
} else {
MXC_GPIO_OutClr(MXC_GPIO_PORT_INTERRUPT_STATUS, MXC_GPIO_PIN_INTERRUPT_STATUS);
}
}
void gpio_isr(void)
{
MXC_Delay(MXC_DELAY_MSEC(100)); // 软件消抖
MXC_GPIO_Handler(MXC_GPIO_GET_IDX(MXC_GPIO_PORT_IN));
}
gpio_callback:按键回调,仅在中断上下文中翻转 blink_enabled,不执行任何耗时操作,确保中断响应迅速。
TMR0_IRQHandler:定时器中断,每 500ms 自动进入,根据标志对第二个 LED 执行 OutToggle(闪烁)或 OutClr(熄灭)。
gpio_isr:GPIO 中断入口,通过 MXC_Delay 实现 100ms 消抖,消除按键机械抖动带来的多次误触发。
5. 主循环(第一个 LED 跟随逻辑)
while (1) {
if (MXC_GPIO_InGet(gpio_interrupt.port, gpio_interrupt.mask)) {
MXC_GPIO_OutClr(gpio_out.port, gpio_out.mask); // 按键释放 → 第一灯灭
} else {
MXC_GPIO_OutSet(gpio_out.port, gpio_out.mask); // 按键按下 → 第一灯亮
}
}
轮询方式:主循环持续读取按键电平。由于按键引脚已配置为输入模式且未启用上拉/下拉,外部电路决定了按下时的电平状态(低电平触发)。MXC_GPIO_InGet 为 0 代表按键按下,立即点亮第一个 LED;为 1 代表按键释放,立即熄灭。
实时响应:该循环不包含任何延时或阻塞代码,保证第一个 LED 对按键动作的跟随达到微秒级实时响应。
五、功能展示图及说明
功能效果见视频。
六、项目中遇到的难题及解决方法
1. 编译错误:头文件 mxc_tick.h 不存在
问题描述:在尝试使用非阻塞延时方案时,代码中包含了 #include "mxc_tick.h",但 MAX32690 的 MSDK 中并不存在该头文件,导致编译失败。
解决方法:放弃依赖不存在的系统 tick 库,转而直接使用 硬件定时器 TMR0 来产生精确的 500ms 中断,在中断服务函数中直接控制 LED 翻转。同时移除所有对 mxc_tick.h 的引用,改用标准的 tmr.h 驱动接口。
2. 第二个 LED 在“常亮”与“闪烁”之间切换,而非“常灭”与“闪烁”
问题描述:按照初始逻辑,当 blink_enabled == false 时执行 MXC_GPIO_OutClr() 熄灭 LED,但实际效果却是 LED 保持常亮。这表明该引脚的硬件极性为 低电平点亮,而代码假设了高电平点亮。
解决方法:分析硬件电路后,将 else 分支中的 MXC_GPIO_OutClr() 改为 MXC_GPIO_OutSet(),使得在停止闪烁时输出高电平,对应 LED 熄灭。同时调整初始化时的默认状态为 OutSet,确保上电后 LED 为灭。
七、心得体会
1. 中断驱动与轮询的合理分工是系统实时性的关键
最初我将闪烁逻辑放在主循环中,导致 LED 跟随响应迟钝。后来将时间敏感的任务(定时器翻转 LED)交由硬件中断独立处理,主循环只负责轻量级轮询,系统响应立刻变得流畅。这让我明白:中断处理时间敏感事件,主循环处理非阻塞任务 是嵌入式实时系统的基本设计原则。
2. 硬件定时器是实现精确延时的最佳选择
起初尝试用 MXC_Delay 累加实现非阻塞延时,但闪烁频率不稳定且主循环被拖慢。改用硬件 TMR0 后,闪烁频率精确到毫秒级,且完全不影响其他任务。这让我认识到:对于周期性精确操作,硬件定时器是唯一正确的工具,软件延时只适合短时等待。