STM32 04 按键控制LED
一、学习背景与进阶目标
在之前的学习中,我们已经掌握了如何点亮 LED 和驱动蜂鸣器,但代码都集中在 main.c 中。随着功能增加,主函数会变得臃肿难读。本实验的目标是实现“按键控制 LED”,更重要的是学习代码的模块化封装。我们将把 LED 初始化和按键检测的逻辑分别封装到独立的 .c 和 .h 文件中,让 main.c 只负责核心逻辑调度,这是嵌入式开发中非常重要的工程化思维。
二、硬件连接说明
LED 模块:通常连接在 GPIO 输出引脚(如 PB1、PB2),低电平点亮;
按键模块:连接在 GPIO 输入引脚(如 PB3、PB4),按下时接地(低电平);
原理简述:通过读取按键引脚的电平变化,判断用户操作,进而翻转对应 LED 的状态。
三、代码块
main主函数
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "LED.h"
#include "Key.h"
int main()
{
uint8_t number;
LED__init();
Key__init();
while(1)
{
number=Key_GetNum();
if(number==1)
{
LED1_turn();
}
if(number==2)
{
LED2_turn();
}
}
}
LED.c
#include "stm32f10x.h" // Device header
void LED__init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct1;
GPIO_InitStruct1.GPIO_Mode=GPIO_Mode_Out_PP ;
GPIO_InitStruct1.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStruct1.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct1);
GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);
}
void LED1_on(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
void LED1_off(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
void LED2_on(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
void LED2_off(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_2);
}
void LED1_turn(void)
{ if (GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1)==0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
else
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
}
void LED2_turn(void)
{ if (GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_2)==0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_2);
}
else
{
GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
}
Key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
void Key__init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct2;
GPIO_InitStruct2.GPIO_Mode=GPIO_Mode_IPU ;
GPIO_InitStruct2.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_11;
GPIO_InitStruct2.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct2);
GPIO_SetBits(GPIOB,GPIO_Pin_1|GPIO_Pin_11);
}
uint8_t Key_GetNum(void)
{
uint16_t KeyNum=0;
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0);
Delay_ms(20);
KeyNum=1;
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0);
Delay_ms(20);
KeyNum=2;
}
return KeyNum;
}
四、个人学习心得与踩坑记录
作为一个刚入门 STM32 的初学者,这次尝试模块化编程让我既兴奋又有点“头大”。虽然代码跑通了,但在编写过程中也遇到了一些小插曲,这里如实记录下来,希望能给同样在摸索的朋友一点参考:
关于文件包含的困惑:刚开始我把 LED.c 和 Key.c 建好了,但在 main.c 里编译总是报错“未定义”。后来查资料才知道,光有 .c 文件不行,还得写对应的 .h 头文件来声明函数,并且在工程目录里要把这些文件都添加进去。这一步卡了我好久,但也让我明白了“声明”和“定义”分离的重要性。
对“封装”的浅显理解:以前我觉得把代码写在一个文件里挺方便的,现在试着拆分后,发现 main 函数确实清爽了很多。虽然目前只是简单的函数搬运,但我隐约感觉到,如果以后项目变大了,这种“各司其职”的写法肯定能救命。
按键消抖的疑惑:目前的 Key_GetNum() 函数里我加了一点延时来做消抖,但我不确定这是否是最高效的方法。如果有前辈看到这篇笔记,欢迎在评论区指点一下有没有更优雅的消抖思路,我会非常感激!
五、存在的不足与改进方向
目前的程序虽然实现了“按键控制 LED”,但我深知这只是最基础的入门水平,代码里还有很多不成熟的地方:
阻塞式延时的局限:现在的 Delay_ms 是死等,这意味着在等待灯闪烁或者按键消抖的时候,CPU 是什么都做不了的。我知道这在复杂项目里是大忌,但目前我还不太会用定时器中断来替代它,这是我接下来的重点攻克目标。
代码的扩展性:现在只有两个按键和两个 LED,如果以后要控制 10 个灯怎么办?难道要写 10 个 if 吗?我听说可以用数组或者结构体来优化,但这超出了我目前的知识范围,只能先挖个坑,等学会了再来填。
缺乏异常处理:如果按键坏了或者接触不良,程序目前没有保护措施。
更多推荐


所有评论(0)