0基础入门嵌入式记录:STM32按键中断控制LED项目总结
本篇为个人零基础学习 STM32 点灯项目的复盘与记录旨在梳理自己的理解与踩坑过程并非专业的系统教学教程。本人也是初学阶段文中若有表述不严谨或理解偏差的地方欢迎各位前辈和读者批评指正共同学习进步。对芯片引脚和运作方式的简单理解在第一次搭建硬件前一定要看清它的引脚编号不要接错了。在运作中实际连线与代码是一一对应的。在运作时注意记得将GND引脚接地3.3V引脚接在面包板正极。在芯片上电时引脚默认处于“高阻悬空”的状态可以粗略视为“未通电”状态所以每次在新建工程时代码中一定要编写GPIO配置函数为芯片“通电”。代码运行时一般由芯片CPU处理。在使用stm 32前注意观察引脚丝印新建工程前要记得将gnd引脚接地3.3伏引脚接正 芯片才能正常运行硬件搭建硬件电路图如图。我选择将按键连接在PB1和PB11另一端接地LED接在PA1和PA2另一端接面包板正极。硬件连接图在连接芯片和STLINK时要注意接口是否一一对应如果连接有错误的话keil5是无法新建工程的。STM32的接口是SWCLK、SWDIO、GND、3.3V、5.5V。注意观察STLINK和STM32上的丝印准确连接LED的两种接法在这里LED可以有两种接入方法。1.低电平点亮灌电流这种方法是将LED连接在面包板3.3V电源和芯片引脚。当初始设置为低电平函数ResetBits时引脚初始输出低电平此时LED正极连接在面包板正极存在压差所以当运行代码时LED初始是点亮的状态。当初始设置为高电平函数SetBits时引脚初始输出高电平此时LED正极连接在面包板正极LED两边电压都是3.3V没有压差所以当运行代码时LED初始是熄灭的状态。低电平连接2.高电平点亮拉电流这种方法是将LED连接在面包板负极和芯片引脚。当初始设置为低电平函数ResetBits时引脚初始输出低电平此时LED正极连接在面包板负极LED两极都为0V没有压差所以当运行代码时LED初始是熄灭的状态。当初始设置为高电平函数SetBits时引脚初始输出高电平此时LED正极连接在面包板负极存在压差所以当运行代码时LED初始是点亮的状态。在搭建硬件电路时如果需要确定LED的初始状态一定要注意元件的连接方式和高低电平函数的选定。高电平连接代码解析完整代码简单注释#include stm32f10x.h//表示不在电脑自带库 表示在电脑自带库 // 宏定义 将LED_BLUE_PIN替换成GPIO_Pin_1 并不是真的定义了函数 #define LED_BLUE_PIN GPIO_Pin_1 #define LED_GREEN_PIN GPIO_Pin_2 #define KEY1_PIN GPIO_Pin_1 #define KEY2_PIN GPIO_Pin_11 // 全局标志位 volatile防止出错 使用场景中断服务函数 硬件外设寄存器 多任务系统 volatile uint8_t g_key1_pressed 0; //uint8_t嵌入式里专门使用的类型 代表无符号8位整数 g_前缀 代表全局变量global) volatile uint8_t g_key2_pressed 0; // 函数声明 int---有返回值的函数 void无返回值的函数 void GPIO_Configuration(void);//void代表无需参数 无需返回值 在嵌入式中只使用void void EXTI_Configuration(void); void NVIC_Configuration(void); void Delay_ms(uint32_t); int main(void) { //函数调用 GPIO_Configuration(); EXTI_Configuration(); NVIC_Configuration(); Delay_ms(20); // 初始灭灯 ResetBits 低电平函数输出低电平0v高电平函数SetBits //调用低电平函数 传入参数 控制特定硬件 GPIO_ResetBits(GPIOA, LED_BLUE_PIN); GPIO_ResetBits(GPIOA, LED_GREEN_PIN); while (1) { // --- 处理按键1中断标志 --- if (g_key1_pressed) { Delay_ms(20); // 消抖 if (GPIO_ReadInputDataBit(GPIOB, KEY1_PIN) Bit_RESET) // 确认按下 Bit_RESET在标准库里为低电平 { // 翻转蓝色LED if (GPIO_ReadOutputDataBit(GPIOA, LED_BLUE_PIN)) GPIO_ResetBits(GPIOA, LED_BLUE_PIN); else GPIO_SetBits(GPIOA, LED_BLUE_PIN); // 确认按键松开防止连触发 while (GPIO_ReadInputDataBit(GPIOB, KEY1_PIN) Bit_RESET); } g_key1_pressed 0; // 处理完清除标志 } // --- 处理按键2中断标志 --- if (g_key2_pressed) { Delay_ms(20); // 消抖 if (GPIO_ReadInputDataBit(GPIOB, KEY2_PIN) Bit_RESET) // 确认按下 { // 翻转绿色LED if (GPIO_ReadOutputDataBit(GPIOA, LED_GREEN_PIN)) GPIO_ResetBits(GPIOA, LED_GREEN_PIN); else GPIO_SetBits(GPIOA, LED_GREEN_PIN); while (GPIO_ReadInputDataBit(GPIOB, KEY2_PIN) Bit_RESET); } g_key2_pressed 0; // 清除标志 } } } // ---- GPIO配置 ---- void GPIO_Configuration(void) { //配置函数必写抬头73-75 GPIO_InitTypeDef GPIO_InitStructure;//空白任务单 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);//打开电闸 // LED推挽输出 GPIO_InitStructure.GPIO_Pin LED_BLUE_PIN | LED_GREEN_PIN;//定义“任务对象” GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP;//定义方法推挽输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;//定义响应速度 GPIO_Init(GPIOA, GPIO_InitStructure);//定义对应的GPIO口 // 按键上拉输入 GPIO_InitStructure.GPIO_Pin KEY1_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;//定义方法上拉输入 GPIO_Init(GPIOB, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin KEY2_PIN; GPIO_Init(GPIOB, GPIO_InitStructure); } // ---- EXTI 配置 ---- void EXTI_Configuration(void) { EXTI_InitTypeDef EXTI_InitStructure; // 将PB1映射到EXTI1PB11映射到EXTI11 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1); GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11); // 配置EXTI Line 1 (按键1) EXTI_InitStructure.EXTI_Line EXTI_Line1; EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Falling; // 下降沿触发按下 EXTI_InitStructure.EXTI_LineCmd ENABLE; EXTI_Init(EXTI_InitStructure); // 配置EXTI Line 11 (按键2) EXTI_InitStructure.EXTI_Line EXTI_Line11; EXTI_Init(EXTI_InitStructure); } // ---- NVIC 配置 ---- void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; // 优先级分组抢占2位响应2位 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 按键1中断EXTI1 NVIC_InitStructure.NVIC_IRQChannel EXTI1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); // 按键2中断EXTI15_10因为EXTI11属于这个组 NVIC_InitStructure.NVIC_IRQChannel EXTI15_10_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 2; NVIC_Init(NVIC_InitStructure); } // ---- 中断服务函数 ---- void EXTI1_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line1) ! RESET) { g_key1_pressed 1; // 仅置标志不做延时 EXTI_ClearITPendingBit(EXTI_Line1); // 清除中断标志 } } void EXTI15_10_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line11) ! RESET) { g_key2_pressed 1; // 仅置标志 EXTI_ClearITPendingBit(EXTI_Line11); } } void Delay_ms(uint32_t ms) { uint32_t i,j; for(i 0; i ms; i) for(j 0; j 8000; j); }中断函数注释EXTI一般而言代码由芯片的CPU去处理。而中断函数的作用是让其他元件代替CPU进行监测从而节约CPU的算力。所以显然中断函数的使用场景是当CPU的算力需要被放在其他更优先级的任务时。中断函数的运作流程是对信号进行监测一旦接收到有效信号就向CPU发送一个中断请求让CPU响应信号。因此中断函数需要设定对象和发出中断请求的具体信号。在本例中中断为中断配置函数、中断服务函数、中断信号优先级配置函数NVIC。中断配置中断配置函数是作用于硬件部分的函数。将所需硬件设定为中断对象设定中断请求信号。// ---- EXTI 配置 ---- void EXTI_Configuration(void) { //空白任务单 EXTI_InitTypeDef EXTI_InitStructure; // 将GPIOB_PB1设定为EXTI1的对象GPIOB_PB11设定为EXTI11的对象 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1); GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11); //将按键1、2下降设定为具体信号 // 配置EXTI Line 1 (按键1) EXTI_InitStructure.EXTI_Line EXTI_Line1; EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Falling; // 下降沿触发按下 EXTI_InitStructure.EXTI_LineCmd ENABLE; EXTI_Init(EXTI_InitStructure); // 配置EXTI Line 11 (按键2) EXTI_InitStructure.EXTI_Line EXTI_Line11; EXTI_Init(EXTI_InitStructure); }中断服务中断服务是作用于CPU处理逻辑的函数。// ---- 中断服务函数 ---- void EXTI1_IRQHandler(void) { //当CPU接收到中断请求时标志并到main运行g_key1_pressed 1 if (EXTI_GetITStatus(EXTI_Line1) ! RESET) { g_key1_pressed 1; // 仅置标志不做延时 EXTI_ClearITPendingBit(EXTI_Line1); // 清除中断标志 } } void EXTI15_10_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line11) ! RESET) { g_key2_pressed 1; // 仅置标志 EXTI_ClearITPendingBit(EXTI_Line11); } }请注意在EXTI11的中断服务函数时函数名为EXTI15_10_IRQHandler而不是EXTI11_IRQHandler。这是由于ST的硬件设计将中断线10到15打包成一个共同的中断入口。所以处理按键2Line11时必须在进入 EXTI15_10_IRQHandler 后先用 if (EXTI_GetITStatus(EXTI_Line11) ! RESET) 确认到底是 Line11 触发的信号再执行置位动作。中断优先级配置中断优先级配置函数是用于设定CPU处理多个中断信号时的先后次序。// ---- NVIC 配置设定CPU响应中断的优先级次序 ---- void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; // 1. 设置分组规则。每组中断信号有4位寄存器这里配置为“抢占优先级”占2位“响应优先级”占2位。 // 这意味着我们填写的优先级数字只能在 0~3 之间。数字越小级别越高。 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // ---------- 配置按键1 中断优先级 ---------- // 选择中断通道EXTI1 NVIC_InitStructure.NVIC_IRQChannel EXTI1_IRQn; // 抢占优先级设为1。如果和其他高优先级的任务冲突数字越小越优先处理。 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; // 响应优先级设为1。当多个同等级中断同时发生时响应数字越小排队排得越靠前。 NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; // 使能中断 NVIC_Init(NVIC_InitStructure); // 提交配置 // ---------- 配置按键2 中断优先级 ---------- // 注意因为EXTI11硬件上属于EXTI15_10这个共享中断组所以通道必须选 EXTI15_10_IRQn NVIC_InitStructure.NVIC_IRQChannel EXTI15_10_IRQn; // 抢占优先级同为1。由于和按键1同级意味着它们不能互相打断对方。 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; // 响应优先级设为2。如果两个按键同时被按按键1响应优先级1会比按键2响应优先级2被CPU先处理。 NVIC_InitStructure.NVIC_IRQChannelSubPriority 2; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); }

相关新闻

Windows免安装版MySQL8详细安装配置教程(ZIP 压缩包版)

Windows免安装版MySQL8详细安装配置教程(ZIP 压缩包版)

1 前言很多小伙伴在安装MySQL时,习惯去找MSI安装包一路“下一步”,但这种方式会在系统里写入大量注册表信息,卸载不干净时还容易留下各种残留,导致重装失败。其实MySQL官方一直提供ZIP压缩包版(也叫noinstall ZIP Arch…

2026/7/19 18:27:07 阅读更多 →
atom-in-orbit调试技巧:在浏览器中调试Atom的完整方法

atom-in-orbit调试技巧:在浏览器中调试Atom的完整方法

atom-in-orbit调试技巧:在浏览器中调试Atom的完整方法 【免费下载链接】atom-in-orbit Putting Atom in the browser 项目地址: https://gitcode.com/gh_mirrors/at/atom-in-orbit 想要在浏览器中运行和调试Atom编辑器吗?atom-in-orbit项目为您提…

2026/7/19 18:27:07 阅读更多 →
官宣|AT Work 正式开源,中小团队免费部署的一站式云研发工作台

官宣|AT Work 正式开源,中小团队免费部署的一站式云研发工作台

中小研发团队的日常,大概率是这样的: 需求写在 Jira 或飞书多维表里,代码在 GitLab,文档散落在语雀和腾讯文档,文件靠微信传来传去,沟通靠企业微信或钉钉——每天在 5 款以上工具之间来回切换,…

2026/7/19 18:27:07 阅读更多 →

最新新闻

互联网大厂常见Java面试题及答案汇总(2026持续更新)

互联网大厂常见Java面试题及答案汇总(2026持续更新)

金九银十即将来袭,又是一个跳槽的好季节,准备跳槽的同学都摩拳擦掌准备大面好几场,今天为大家准备了互联网面试必备的 1 到 5 年 Java 面试者都需要掌握的面试题,分别 JVM,并发编程,MySQL,Tomca…

2026/7/20 0:15:40 阅读更多 →
ngx_output_chain_get_buf

ngx_output_chain_get_buf

1 定义 ngx_output_chain_get_buf 函数 定义在 src/core/ngx_output_chain.cstatic ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize) {size_t size;ngx_buf_t *b, *in;ngx_uint_t recycled;in ctx->in->buf;size ctx->buf…

2026/7/20 0:13:39 阅读更多 →
python数据可视化技巧的100个练习 -- 31. 类别数据的点图

python数据可视化技巧的100个练习 -- 31. 类别数据的点图

重要性★★★☆☆ 难度★★☆☆☆ 你是一家零售公司的数据分析师。你的经理要求你可视化最近产品发布的客户满意度评级分布。评级是分类的,范围从“非常不满意”到“非常满意”。创建一个点图以显示每个评级类别的频率。使用 Python 进行数据处理和可视化。在代码中生成输入…

2026/7/20 0:12:39 阅读更多 →
智能体走进物理世界,千里科技携舱驾协同成果亮相WAIC 2026

智能体走进物理世界,千里科技携舱驾协同成果亮相WAIC 2026

在2026世界人工智能大会(WAIC 2026)举办期间,千里科技董事长、阶跃星辰董事长印奇作为特邀嘉宾出席大会开幕式并在大会主论坛(上午场)发表主题演讲《当智能体进入物理世界》。在印奇看来,"智能体"…

2026/7/20 0:12:39 阅读更多 →
商汤大装置发布“技术-生态-商业”闭环布局,共启“国产AI基础设施规模化商用元年”

商汤大装置发布“技术-生态-商业”闭环布局,共启“国产AI基础设施规模化商用元年”

7月18日,在WAIC 2026商汤科技 “基座大模型架构创新与生态合作论坛”上,商汤科技联合创始人、大装置事业群总裁杨帆发表《智变共生——加速AI基础设施持续升级》主题演讲,系统呈现了商汤大装置国产AI基础设施“技术-生态-商业”闭环布局&…

2026/7/20 0:12:39 阅读更多 →
2026年具身智能领域代表性机器人产品观察:普渡一脑多形底座与实景落地解析

2026年具身智能领域代表性机器人产品观察:普渡一脑多形底座与实景落地解析

前言2026年被行业视为具身智能从"实验室炫技"走向"规模化量产"的关键拐点。据弗若斯特沙利文《全球商用服务机器人市场研究报告》,普渡科技以23%市占率位居全球商用服务机器人第一,业务覆盖85+个国家和地区,累…

2026/7/20 0:11:39 阅读更多 →

日新闻

2026 WAIC:努比亚二代“豆包手机”NaviX Ultra亮相,智能体验全面升级!

2026 WAIC:努比亚二代“豆包手机”NaviX Ultra亮相,智能体验全面升级!

7月18日智东西消息,在2026 WAIC期间,努比亚联合字节豆包打造的二代“豆包手机”努比亚NaviX Ultra首次亮相,相比一代有诸多升级。智能体手机理念中兴通讯终端事业部总裁、努比亚总裁倪飞表示,智能体手机要从人操作手机变为手机帮人…

2026/7/20 0:00:34 阅读更多 →
努比亚NaviX Ultra亮相WAIC,智能体手机能否让用户生活更简单?

努比亚NaviX Ultra亮相WAIC,智能体手机能否让用户生活更简单?

努比亚NaviX Ultra:外观与功能双升级在2026 WAIC期间,首次亮相的努比亚NaviX Ultra吸引了众多目光。它是努比亚联合字节豆包打造的二代“豆包手机”,与一代努比亚M153相比,外观设计变化较大。其机身背部搭载横向排布的大尺寸影像模…

2026/7/20 0:00:34 阅读更多 →
C# 将逗号分割的字符串转换为long,并添加到List<long>

C# 将逗号分割的字符串转换为long,并添加到List<long>

目录 方法1:使用Split和Convert.ToInt64 方法2:使用LINQ的Select和ToList 方法3:使用TryParse进行异常安全转换(推荐) 如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天…

2026/7/20 0:00:34 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/19 0:00:40 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:00:40 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:00:40 阅读更多 →

月新闻