一、变量定义部分(理解程序的 "记忆")

c

运行

/* USER CODE BEGIN PV */
static uint8_t last_button_state = 1; // 初始为高电平(未按下)
static uint8_t device_mode = 0; // 设备模式:0=LD1, 1=LD3, 2=蜂鸣器, 3=全部关闭
static uint8_t device_state[3] = {0, 0, 0}; // 设备状态:0=关, 1=开
/* USER CODE END PV */

这部分定义了程序运行中需要 "记住" 的变量,就像人的短期记忆:

  • last_button_state:记录上一次按键的状态(1 = 未按下,0 = 按下),用于检测按键的 "变化"(从按下到松开或反之)。初始值为 1,因为按键未按下时默认是高电平(硬件上拉)。
  • device_mode:记录当前操作的设备模式(0-3 分别对应不同设备),类似遥控器的 "模式切换"。
  • device_state[3]:数组,分别记录 3 个设备的开关状态(索引 0=LD1,1=LD3,2 = 蜂鸣器),0 表示关,1 表示开。

二、函数声明(提前 "告知" 程序要用到的功能)

c

运行

/* USER CODE BEGIN PFP */
void beep_short(void);
/* USER CODE END PFP */

这行代码是函数声明,告诉编译器:后面会定义一个叫beep_short的函数,无参数、无返回值。作用是提前 "报备",避免编译器在遇到函数调用时不认识该函数。

三、初始化代码(程序启动时的 "准备工作")

c

运行

/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET);  // LD1关闭
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // LD3关闭
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET); // 蜂鸣器关闭// 开机提示音
beep_short();
HAL_Delay(200);
beep_short();
/* USER CODE END 2 */

这部分是程序启动后首先执行的初始化操作:

  1. 关闭所有外设:通过HAL_GPIO_WritePin函数将 3 个设备的引脚设为低电平(GPIO_PIN_RESET),确保程序启动时所有设备都是关闭状态。
    • LD1 接在 GPIOC 的 Pin4
    • LD3 接在 GPIOC 的 Pin13
    • 蜂鸣器接在 GPIOA 的 Pin15
  2. 开机提示音:调用beep_short函数让蜂鸣器短响两次(间隔 200ms),提示程序已正常启动。

四、主循环逻辑(程序的 "核心动作",反复执行)

这部分在while(1)循环中,是程序运行时一直在重复做的事情,就像人反复 "观察→判断→行动" 的过程。

1. 读取当前按键状态

c

运行

uint8_t current_button_state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9);

  • 通过HAL_GPIO_ReadPin函数读取按键引脚(GPIOC 的 Pin9)的当前状态(1 = 未按下,0 = 按下),并存在current_button_state变量中。
2. 检测按键 "按下" 动作(下降沿检测)

c

运行

if (last_button_state == 1 && current_button_state == 0) {// 按键从"未按下"变为"按下"(下降沿)HAL_Delay(20); // 防抖延时current_button_state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9);if (current_button_state == 0) { // 确认按键确实按下// 按键处理逻辑...}
}

这是按键检测的核心逻辑,作用是准确识别一次有效的按键按下

  • 条件last_button_state == 1 && current_button_state == 0:判断按键是否从 "未按下"(1)变为 "按下"(0),这种变化叫 "下降沿"。
  • HAL_Delay(20):延时 20ms,是为了 "消抖"—— 按键机械结构会导致按下瞬间有微小抖动(状态快速变化),延时后再读一次状态,避免误判。
  • 再次判断current_button_state == 0:确认按键确实处于按下状态,排除抖动干扰。
3. 按键按下后的处理

当确认按键按下后,执行以下操作:

(1)按键提示音

c

运行

beep_short(); // 调用蜂鸣器短响函数,反馈按键已被按下
(2)切换设备模式

c

运行

device_mode = (device_mode + 1) % 4;

  • device_mode从 0 开始,每次按键加 1,通过%4(取余 4)实现循环:0→1→2→3→0→...,对应 4 种模式的切换。
(3)根据模式控制外设(核心逻辑)

通过switch-case语句,根据当前device_mode执行不同操作:

c

运行

switch(device_mode) {case 0: // 控制LD1device_state[0] = !device_state[0]; // 取反:0→1(开),1→0(关)// 控制LD1引脚状态(开=高电平,关=低电平)HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, device_state[0] ? GPIO_PIN_SET : GPIO_PIN_RESET);// 关闭其他设备,确保每次只控制一个设备HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);device_state[1] = 0; // 同步更新其他设备的状态记录device_state[2] = 0;break;// case 1(控制LD3)、case 2(控制蜂鸣器)逻辑与case 0类似,只是操作的引脚不同case 3: // 全部关闭模式// 关闭所有设备引脚HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);// 同步更新所有设备状态为0(关)device_state[0] = 0;device_state[1] = 0;device_state[2] = 0;break;
}

逻辑要点:

  • 每个模式只控制一个设备,同时关闭其他设备(避免多个设备同时工作)。
  • device_state数组记录设备状态,方便下次切换时判断当前状态(开 / 关)。
  • 通过三元运算符device_state[0] ? GPIO_PIN_SET : GPIO_PIN_RESET快速设置引脚状态(开 = 高电平,关 = 低电平)。
4. 等待按键释放并更新状态

c

运行

// 等待按键释放
while (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9) == 0) {// 空循环,直到按键松开(状态变为1)
}
HAL_Delay(20); // 释放时的防抖延时

  • 这部分确保一次按键只触发一次模式切换,避免按键长按导致多次切换。
  • 按键松开后再延时 20ms,进一步消除释放时的机械抖动。
5. 更新按键状态记录

c

运行

last_button_state = current_button_state;

  • 将当前按键状态保存到last_button_state,为下一次循环的 "状态变化检测" 做准备。

五、蜂鸣器短响函数(封装重复功能)

c

运行

/* USER CODE BEGIN 4 */
void beep_short(void)
{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET); // 蜂鸣器开启(高电平)HAL_Delay(100); // 持续100msHAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET); // 蜂鸣器关闭(低电平)
}
/* USER CODE END 4 */

  • 这是一个封装好的函数,实现蜂鸣器短响 100ms 的功能。
  • 好处:避免重复写相同代码,需要蜂鸣器提示时直接调用beep_short()即可,让程序更简洁。

总结:程序的整体流程

  1. 初始化:关闭所有设备,蜂鸣器响两次提示启动。
  2. 循环检测:反复读取按键状态,判断是否有有效按下。
  3. 按键处理:确认按键按下后,切换模式→控制对应设备(开 / 关)→关闭其他设备→等待按键释放。
  4. 状态记录:通过变量记录按键状态和设备状态,实现 "记忆" 功能。

关键思路提炼(自己写时可复用)

  1. 按键检测:通过 "上一次状态 + 当前状态" 判断按键变化(下降沿),加延时消抖。
  2. 状态管理:用变量 / 数组记录设备状态,方便切换时判断当前状态。
  3. 模式切换:用switch-case或数组索引实现多模式循环。
  4. 功能封装:重复使用的功能(如蜂鸣器提示)写成函数,简化代码。

理解这些逻辑后,你可以尝试修改设备引脚、增加更多模式(如控制 4 个设备),或者改用中断方式检测按键,逐步掌握 STM32 的 GPIO 控制编程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/921627.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/921627.shtml
英文地址,请注明出处:http://en.pswp.cn/news/921627.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前沿重器[74] | 淘宝RecGPT:大模型推荐框架,打破信息茧房

前沿重器栏目主要给大家分享各种大厂、顶会的论文和分享,从中抽取关键精华的部分和大家分享,和大家一起把握前沿技术。具体介绍:仓颉专项:飞机大炮我都会,利器心法我还有。(算起来,专项启动已经…

jenkins加docker 部署项目

jenkins加docker 部署springboot项目 1项目结构Dockerfile 内容 FROM openjdk:8-jdk-alpine ARG JAR_FILEtarget/*.jar COPY ${JAR_FILE} app.jar ENTRYPOINT ["java","-jar","/app.jar","--server.port9090"]在A服务器上启动jenkins …

提示词工程(Prompt Engineering)的崛起——为什么“会写Prompt”成了新技能?

🎀【开场 猫猫狐狐的对话】🐾猫猫扒着屏幕:“喵?咱写的这句 Prompt 怎么又跑偏啦?明明只是想让它帮忙写一段 Python 代码,它偏要给咱写论文摘要……” 🦊狐狐眯着眼,声音带点冷意&a…

供应链管理系统入门知识:是什么,功能模块,怎么定制开发?

如果你是刚接触企业运营的新手,听到 “供应链管理系统” 可能会觉得有点复杂。其实,它就像一个 “智能管家”,帮企业把从买材料到卖产品的一系列流程管得明明白白。今天就用大白话给你讲清楚这个系统到底是什么,以及它能帮上什么忙…

kotlin - 平板分屏,左右拖动,2个Activity计算宽度,使用ActivityOptions、Rect(三)

kotlin - 平板分屏,左右拖动,2个Activity计算宽度,使用ActivityOptions、Rect使用平板,api33才支持,可以左右拖动,分屏第一个页面 , 思考:分屏后,对整个app的影响&#x…

v0.29.3 敏感词性能优化之繁简体转换 opencc4j 优化

敏感词性能调优系列 v0.29.0 敏感词性能优化提升 14 倍全过程 v0.29.1 敏感词性能优化之内部类迭代器内部类 v0.29.2 敏感词性能优化之基本类型拆箱、装箱的进一步优化的尝试 v0.29.3 敏感词性能优化之繁简体转换 opencc4j 优化 背景 opencc4j opencc4j 中,因…

Spark SQL解析查询parquet格式Hive表获取分区字段和查询条件

首先说一下,这里解决的问题应用场景: sparksql处理Hive表数据时,判断加载的是否是分区表,以及分区表的字段有哪些?再进一步限制查询分区表必须指定分区? 这里涉及到两种情况:select SQL查询和…

谷歌发布文本嵌入模型EmbeddingGemma(附部署方式)

EmbeddingGemma是谷歌于2025年9月开源的开放式文本嵌入模型,专为端侧设备设计,具备以下核心优势: 性能优势 在MTEB基准测试中,EmbeddingGemma在500M以下参数规模的多语言文本嵌入模型中表现最佳,性能接近参数翻倍的顶…

CPU调度——调度的目标

2.2.2 调度的目标 当系统中“想运行”的实体多于 CPU 的数量时,调度就不可避免地要在“效率”与“公平”之间做取舍。直观地说,一类目标希望把硬件压榨到更高的利用率,让单位时间内做更多的工作;另一类目标则关心个体体验&#x…

C++ 8

封装一个学生的类&#xff0c;定义一个学生这样类的vector容器, 里面存放学生对象&#xff08;至少3个&#xff09;再把该容器中的对象&#xff0c;保存到文件中。再把这些学生从文件中读取出来&#xff0c;放入另一个容器中并且遍历输出该容器里的学生。#include <iostream…

短视频矩阵系统源码开发搭建技术指南--支持OEM

短视频矩阵系统架构设计短视频矩阵系统通常采用分布式架构&#xff0c;包含内容管理、用户管理、推荐算法、存储分发等模块。主流技术栈包括微服务框架&#xff08;Spring Cloud/Dubbo&#xff09;、消息队列&#xff08;Kafka/RabbitMQ&#xff09;、数据库&#xff08;MySQL/…

不连续页分配器补充

vmalloc流程 1. 背景&#xff1a;vmalloc() 要解决的问题 kmalloc() 要求 虚拟地址连续&#xff0c;物理页也连续。大块内存分配可能失败。vmalloc() 只保证 虚拟地址连续&#xff0c;物理内存可以由很多不连续的页拼接。 实现的关键就是&#xff1a; 在 vmalloc 区域 找一块空…

bug | 事务粒度不能太大,含demo

刷到一个说法&#xff0c;建议不要使用transaction注解。这个说法不太准确&#xff0c;注解可以用&#xff0c;但标注的事务粒度不能太大&#xff0c;这样可能会引起数据库阻塞问题。以下介绍注解事务和编程式事务的两种用法。 关键字&#xff1a;声明式事务&#xff0c;编程式…

别再看人形机器人了!真正干活的机器人还有这些!

每次提起“机器人”&#xff0c;你脑海中是不是立刻浮现出双足行走、拟人微笑、还能陪你聊天的那种“人形机器人”&#xff1f;但真相是&#xff1a;人形机器人并非更实用&#xff0c;只是满足了我们对“人类替代品”的幻想。事实上&#xff0c;机器人的世界远比我们想象的更丰…

垃圾回收,几种GC算法及GC机制

1.什么是垃圾回收&#xff1f;如何触发垃圾回收&#xff1f; 垃圾回收(GC)是自动管理内存的一种机制&#xff0c;它负责自动释放不再被程序引用的对象所占用的内存&#xff0c;这种机制减少内存泄漏和内存管理错误的可能性。可以通过多种方式触发&#xff1a;内存不足时&#x…

更智能的零售终端设备管理:合规、安全与高效

目录 引言&#xff1a;为什么零售连锁和自助终端需要更智能的设备管理&#xff1f; 典型应用场景 1. 便利店连锁 2. 大型超市 3. 加油站 4. 自助终端 核心功能&#xff0c;驱动高效与安全 1. 批量配置 2. 定时策略同步 3. 设备状态监控 4. Kiosk 模式&#xff0c;保…

Elasticsearch:向量搜索过滤 - 保持相关性

作者&#xff1a;来自 Elastic Carlos Delgado 仅执行向量搜索以找到与查询最相似的结果是不够的。通常需要过滤来缩小搜索结果。本文解释了在 Elasticsearch 和 Apache Lucene 中向量搜索的过滤是如何工作的。 Elasticsearch 拥有丰富的新功能&#xff0c;帮助你为自己的用例构…

Linux 性能调优之 OOM Killer 的认知与观测

写在前面 博文内容涉及到OOM Killer机制,以及利用 Cgroup/dmesg/BPF 观测 OOM Killer 事件,包括云原生环境下的 OOM Killer 机制的简单介绍 这是内存调优的最后一篇,之后会分享一些网络调优相关内容 理解不足小伙伴帮忙指正 😃,生活加油 我不再将这个世界与我所期待的,塑…

webrtc之高通滤波——HighPassFilter源码及原理分析

文章目录前言一、导读二、高通滤波过程1.HighPassFilter的创建1&#xff09;HighPassFilter的作用2&#xff09;开启条件3&#xff09;开启配置2.高通滤波整体过程1&#xff09;触发时机2&#xff09;滤波器创建3&#xff09;高通滤波过程三、算法实现1.原理1&#xff09;滤波器…

《sklearn机器学习——聚类性能指数》同质性,完整性和 V-measure

函数&#xff1a;homogeneity_score 参数&#xff1a; labels_true: array-like, shape [n_samples] 样本的真实标签。 labels_pred: array-like, shape [n_samples] 样本的预测标签。返回值&#xff1a; h: float 同质性得分&#xff0c;在0到1之间&#xff0c;值越大表示聚…