本教程针对stm32F407开发板,标准库开发。
首先查看改开发板的原理图,找到LED部分:
分析原理图:
-
电源是 +3.3V,通过限流电阻(R7 / R9)再接到 LED 的阳极(正极)。
-
LED 的阴极(负极)接到信号端 LED0 / LED1
所以:这两个 LED 是低电平触发(低电平点亮,高电平熄灭)
查看原理图分析LED灯对应的引脚
如图所示:LED0-->PF10,LED1-->PF9
查询8、STM32F4xx中文参考手册,CPU 核心时钟(HCLK)频率为168MHz
基本信息已经知道,现在打开Keil uVision5,新建工程。
项目框架如图所示
首先写SYSTEM下的延时部分(DELAY.c和DELAY.h)
DELAY.c
#include "stm32f4xx.h" // Device header
#include "DELAY.h"
void DELAY_ms(uint32_t xms)
{
while(xms--)
{
SysTick->CTRL = 0;//关闭 SysTick
SysTick->LOAD = SystemCoreClock /1000; //设定重装值:1ms 需要的时钟 tick 数
SysTick->VAL = 0;//清空当前计数,同时清除 COUNTFLAG
SysTick->CTRL = 1<<0 | 1<<2;//使能 SysTick(EN=1),时钟源选 CPU 时钟(CLKSOURCE=1),不使能中断(TICKINT=0)
while ((SysTick->CTRL &(1<<16))==0);//轮询 COUNTFLAG,等它从 0 变 1(计到 0)即 1 ms
SysTick->CTRL = 0;//关 SysTick,结束本次 1 ms
}
}
void DELAY_us(uint32_t xus)
{
while(xus--)
{
SysTick->CTRL = 0;
SysTick->LOAD = SystemCoreClock /1000/1000;
SysTick->VAL = 0;
SysTick->CTRL = 1<<0 | 1<<2;
while ((SysTick->CTRL &(1<<16))==0);
SysTick->CTRL = 0;
}
}
DELAY.h
#ifndef __DELAY__H_
#define __DELAY__H_
#include "stm32f4xx.h" // Device header
void DELAY_ms(uint32_t xms);
void DELAY_us(uint32_t xus);
#endif
LED.c
#include "stm32f4xx.h" // Device header
//PF9 LED1
//PF10 LED0
void LED_init(void)
{
//时钟使能GPIOF的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
//设置相应脚位功能
GPIO_InitTypeDef GPIO_INSTRUCT;
GPIO_INSTRUCT.GPIO_Mode = GPIO_Mode_OUT;
GPIO_INSTRUCT.GPIO_OType = GPIO_OType_PP;
GPIO_INSTRUCT.GPIO_Pin = GPIO_Pin_9 |GPIO_Pin_10;
GPIO_INSTRUCT.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_INSTRUCT.GPIO_Speed = GPIO_Speed_100MHz;
//使配置功能脚位生效
GPIO_Init(GPIOF,&GPIO_INSTRUCT);
//默认高电平,关闭LED灯
GPIO_SetBits(GPIOF,GPIO_Pin_9);
GPIO_SetBits(GPIOF,GPIO_Pin_10);
}
void LED0_on(void)
{
//分析原理图,LED灯低电平点亮
GPIO_ResetBits(GPIOF,GPIO_Pin_10);
}
void LED0_off(void)
{
//高电平熄灭
GPIO_SetBits(GPIOF,GPIO_Pin_10);
}
void LED1_on(void)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_9);
}
void LED1_off(void)
{
GPIO_SetBits(GPIOF,GPIO_Pin_9);
}
LED.h
#ifndef __LED__H_
#define __LED__H_
void LED_init(void);
void LED0_on(void);
void LED0_off(void);
void LED1_on(void);
void LED1_off(void);
#endif
main.c
#include "stm32f4xx.h" // Device header
#include "LED.h"
#include "DELAY.h" // 延时函数
int main(void)
{
LED_init(); // 初始化 LED
while(1)
{
//LED灯闪烁
LED0_on();
DELAY_ms(500); // 延时500ms
LED0_off();
DELAY_ms(500);
}
}
运行效果:
LED0循环闪烁,每次:亮0.5s——>灭0.5s——>亮0.5s.......一直循环。
当想实现流水灯效果时,只需要修改main.c
#include "stm32f4xx.h" // Device header
#include "LED.h"
#include "DELAY.h"
int main(void)
{
LED_init(); // 初始化 LED
while(1)
{
LED0_on(); // 点亮 LED0
DELAY_ms(300); // 延时 300ms
LED0_off(); // 熄灭 LED0
LED1_on(); // 点亮 LED1
DELAY_ms(300);
LED1_off();
}
}
运行效果:
LED0与LED1呈现流水灯效果。
由于我的开发板只有两个LED灯,所以流水灯效果不是很明显,可以根据自己开发板的LED灯数增加,LED灯数越多,效果越明显。
现在可以升级程序,添加按键控制功能,使用按键控制LED灯的状态
依旧查看按键的原理图及对应的引脚
由按键的原理图可以看出,WK_UP按键对应PA0,按下后为高电平,松开为低电平。KEY_0按键对应PE4,按下后为低电平,松开为高电平。
现在知道按键的相关信息,开始编写按键代码
继续在HARDWARE文件夹下添加KEY.c与KEY.h,使其与LED.c与LED.h在同一路径。
KEY.c
#include "stm32f4xx.h" // Device header
#include "KEY.h"
#include "LED.h"
// WK_UP : PA0, 按下=高电平
// KEY0 : PE4, 按下=低电平
void KEY_init(void)
{
// 使能 GPIOA 和 GPIOE 时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
GPIO_InitTypeDef GPIO_INSTRUCT;
GPIO_INSTRUCT.GPIO_Mode = GPIO_Mode_IN; // 输入模式
GPIO_INSTRUCT.GPIO_OType = GPIO_OType_PP; // 推挽(对输入脚其实没影响)
GPIO_INSTRUCT.GPIO_PuPd = GPIO_PuPd_NOPULL; // 浮空
GPIO_INSTRUCT.GPIO_Speed = GPIO_Speed_100MHz;
// PA0 (WK_UP)
GPIO_INSTRUCT.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_INSTRUCT);
// PE4 (KEY0)
GPIO_INSTRUCT.GPIO_Pin = GPIO_Pin_4;
GPIO_Init(GPIOE, &GPIO_INSTRUCT);
}
// 按键检测并控制 LED
void KEY_ScanAndControl(void)
{
// WK_UP 按下=高电平
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1)
{
LED0_on();
}
else
{
LED0_off();
}
// KEY0 按下=低电平
if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == 0)
{
LED1_on();
}
else
{
LED1_off();
}
}
KEY.h
#ifndef __KEY__H_
#define __KEY__H_
#include "stm32f4xx.h" // Device header
void KEY_init(void);
void KEY_ScanAndControl(void);
#endif
main.c
#include "stm32f4xx.h"
#include "LED.h"
#include "KEY.h"
#include "DELAY.h"
int main(void)
{
LED_init(); // 初始化 LED
KEY_init(); // 初始化按键
while(1)
{
KEY_ScanAndControl(); // 检测按键并控制 LED
DELAY_ms(10); // 软件消抖(10ms)
}
}
对于按键控制LED灯时,最好加入延时函数,进行消抖处理。负责有可能出现按一次按键,实际按了很多次。
运行效果:
-
按下 WK_UP (PA0 高电平) → LED0 点亮,松开熄灭
-
按下 KEY0 (PE4 低电平) → LED1 点亮,松开熄灭
如果想实现按键控制 LED 的“翻转开关”效果,按一下松开开关:打开LED,再按一下松开开关:关闭LED。那么就要实现状态翻转 + 消抖。
KEY.c
#include "stm32f4xx.h"
#include "KEY.h"
#include "LED.h"
#include "DELAY.h" // 用于按键消抖延时
// WK_UP : PA0, 按下=高电平
// KEY0 : PE4, 按下=低电平
void KEY_init(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
GPIO_InitTypeDef GPIO_INSTRUCT;
GPIO_INSTRUCT.GPIO_Mode = GPIO_Mode_IN;
GPIO_INSTRUCT.GPIO_OType = GPIO_OType_PP;
GPIO_INSTRUCT.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_INSTRUCT.GPIO_Speed = GPIO_Speed_100MHz;
// PA0 (WK_UP)
GPIO_INSTRUCT.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_INSTRUCT);
// PE4 (KEY0)
GPIO_INSTRUCT.GPIO_Pin = GPIO_Pin_4;
GPIO_Init(GPIOE, &GPIO_INSTRUCT);
}
// 按键扫描(带翻转功能)
// 返回值:0=无操作, 1=WK_UP触发, 2=KEY0触发
uint8_t KEY_Scan(void)
{
// WK_UP: 按下=高电平
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1)
{
Delay_ms(20); // 消抖
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1)
{
while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1); // 等待松手
return 1;
}
}
// KEY0: 按下=低电平
if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == 0)
{
Delay_ms(20); // 消抖
if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == 0)
{
while(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == 0); // 等待松手
return 2;
}
}
return 0;
}
KEY.h
#ifndef __KEY__H_
#define __KEY__H_
#include "stm32f4xx.h" // Device header
void KEY_init(void);
uint8_t KEY_Scan(void);
#endif
main.c
#include "stm32f4xx.h"
#include "LED.h"
#include "KEY.h"
#include "DELAY.h"
int main(void)
{
LED_init();
KEY_init();
uint8_t led0_state = 0; // 记录 LED0 状态
uint8_t led1_state = 0; // 记录 LED1 状态
while(1)
{
uint8_t key = KEY_Scan();
if (key == 1) // WK_UP
{
led0_state = !led0_state; // 翻转
if (led0_state) LED0_on();
else LED0_off();
}
else if (key == 2) // KEY0
{
led1_state = !led1_state; // 翻转
if (led1_state) LED1_on();
else LED1_off();
}
}
}
运行效果:
-
按一次 WK_UP (PA0) → LED0 状态翻转(亮 ↔ 灭)
-
按一次 KEY0 (PE4) → LED1 状态翻转
-
每次按下都要松开后才能再次识别(消抖+等待松手机制)
本文章将STM32入门点亮LED灯延申实现流水灯,再延申实现按键控制LED灯,由浅入深。