我来为您提供一个基于STM32的LED灯嵌入式系统案例。这个案例使用标准库,包含按键控制LED闪烁。
案例:STM32 LED控制与按键输入系统
1. 项目结构
LED_Project/
├── Core/
│ ├── Src/
│ │ ├── main.c
│ │ ├── gpio.c
│ │ └── sys.c
│ └── Inc/
│ ├── gpio.h
│ └── sys.h
├── Drivers/
└── README.md
2. 主要代码文件
main.c
/**
* @file main.c
* @brief STM32 LED控制主程序
* @details 实现LED闪烁、按键控制、定时器控制功能
*/
#include "stm32f1xx.h"
#include "gpio.h"
#include "sys.h"
#include <stdint.h>
/* 全局变量定义 */
volatile uint32_t systick_counter = 0;
volatile uint8_t led_mode = 0; // 0:关闭, 1:常亮, 2:慢闪, 3:快闪, 4:呼吸灯
volatile uint8_t button_pressed = 0;
/* 函数声明 */
void SystemClock_Config(void);
void SysTick_Handler(void);
void Delay_ms(uint32_t ms);
void LED_Breathing(void);
/**
* @brief 主函数
* @param 无
* @retval int
*/
int main(void)
{
/* MCU初始化 */
HAL_Init();
SystemClock_Config();
/* 外设初始化 */
GPIO_Init();
/* 配置SysTick定时器为1ms中断 */
SysTick_Config(SystemCoreClock / 1000);
/* LED初始状态 */
LED_OFF(LED1);
LED_OFF(LED2);
while (1)
{
/* 按键扫描 */
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
{
Delay_ms(20); // 按键消抖
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
{
button_pressed = 1;
while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0); // 等待按键释放
}
}
/* 按键处理 */
if (button_pressed)
{
button_pressed = 0;
led_mode = (led_mode + 1) % 5; // 循环切换模式
}
/* 根据模式控制LED */
switch (led_mode)
{
case 0: // LED关闭
LED_OFF(LED1);
LED_OFF(LED2);
break;
case 1: // LED常亮
LED_ON(LED1);
LED_ON(LED2);
break;
case 2: // LED慢闪 (500ms)
if (systick_counter % 1000 < 500)
{
LED_ON(LED1);
LED_OFF(LED2);
}
else
{
LED_OFF(LED1);
LED_ON(LED2);
}
break;
case 3: // LED快闪 (200ms)
if (systick_counter % 400 < 200)
{
LED_ON(LED1);
LED_OFF(LED2);
}
else
{
LED_OFF(LED1);
LED_ON(LED2);
}
break;
case 4: // 呼吸灯效果
LED_Breathing();
break;
}
}
}
/**
* @brief SysTick中断服务函数
* @param 无
* @retval 无
*/
void SysTick_Handler(void)
{
systick_counter++;
}
/**
* @brief 简单延时函数
* @param ms: 延时的毫秒数
* @retval 无
*/
void Delay_ms(uint32_t ms)
{
uint32_t start_time = systick_counter;
while ((systick_counter - start_time) < ms);
}
/**
* @brief LED呼吸灯效果
* @param 无
* @retval 无
*/
void LED_Breathing(void)
{
static uint8_t pwm_value = 0;
static int8_t direction = 1;
static uint32_t last_time = 0;
/* 每10ms调整一次PWM值 */
if ((systick_counter - last_time) >= 10)
{
last_time = systick_counter;
/* 更新PWM值 */
pwm_value += direction;
/* 改变方向 */
if (pwm_value >= 100) direction = -1;
if (pwm_value <= 0) direction = 1;
/* 简单PWM实现 */
if ((systick_counter % 100) < pwm_value)
{
LED_ON(LED1);
}
else
{
LED_OFF(LED1);
}
LED2_TOGGLE(); // LED2在呼吸灯模式下闪烁
}
}
/**
* @brief 系统时钟配置
* @param 无
* @retval 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置HSE
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
gpio.h
/**
* @file gpio.h
* @brief GPIO配置头文件
*/
#ifndef __GPIO_H
#define __GPIO_H
#include "stm32f1xx.h"
/* LED引脚定义 */
#define LED1_PIN GPIO_Pin_5
#define LED1_PORT GPIOA
#define LED2_PIN GPIO_Pin_6
#define LED2_PORT GPIOA
/* 按键引脚定义 */
#define BUTTON_PIN GPIO_Pin_0
#define BUTTON_PORT GPIOA
/* LED控制宏 */
#define LED_ON(led) (led##_PORT->BRR = led##_PIN) // 输出低电平,LED亮
#define LED_OFF(led) (led##_PORT->BSRR = led##_PIN) // 输出高电平,LED灭
#define LED_TOGGLE(led) (led##_PORT->ODR ^= led##_PIN) // 翻转LED状态
/* 针对特定LED的宏 */
#define LED1_ON() LED_ON(LED1)
#define LED1_OFF() LED_OFF(LED1)
#define LED1_TOGGLE() LED_TOGGLE(LED1)
#define LED2_ON() LED_ON(LED2)
#define LED2_OFF() LED_OFF(LED2)
#define LED2_TOGGLE() LED_TOGGLE(LED2)
/* 函数声明 */
void GPIO_Init(void);
void Error_Handler(void);
#endif /* __GPIO_H */
gpio.c
/**
* @file gpio.c
* @brief GPIO初始化配置
*/
#include "gpio.h"
/**
* @brief GPIO初始化
* @param 无
* @retval 无
*/
void GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 使能GPIO时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/* 配置LED引脚 - 推挽输出 */
GPIO_InitStruct.Pin = LED1_PIN | LED2_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED1_PORT, &GPIO_InitStruct);
/* 配置按键引脚 - 输入模式,上拉 */
GPIO_InitStruct.Pin = BUTTON_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct);
/* 配置一个额外的GPIO用于调试 */
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
/**
* @brief 错误处理函数
* @param 无
* @retval 无
*/
void Error_Handler(void)
{
/* LED1和LED2同时闪烁表示错误 */
while(1)
{
LED1_TOGGLE();
LED2_TOGGLE();
for(int i = 0; i < 1000000; i++); // 简单延时
}
}
3. Makefile 示例
# 编译器定义
CC = arm-none-eabi-gcc
OBJCOPY = arm-none-eabi-objcopy
OBJDUMP = arm-none-eabi-objdump
SIZE = arm-none-eabi-size
# 编译选项
CFLAGS = -mcpu=cortex-m3 -mthumb -Wall -Og -g
CFLAGS += -DSTM32F103x6
CFLAGS += -fdata-sections -ffunction-sections
LDFLAGS = -mcpu=cortex-m3 -mthumb -specs=nano.specs
LDFLAGS += -Wl,--gc-sections -TSTM32F103C8Tx_FLASH.ld
# 包含路径
INCLUDES = -ICore/Inc -IDrivers/STM32F1xx_HAL_Driver/Inc
# 源文件
SRCS = Core/Src/main.c \
Core/Src/gpio.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c
# 目标文件
OBJS = $(SRCS:.c=.o)
# 目标名称
TARGET = led_project
# 默认目标
all: $(TARGET).elf $(TARGET).hex $(TARGET).bin
# 生成elf文件
$(TARGET).elf: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
$(SIZE) $@
# 生成hex文件
$(TARGET).hex: $(TARGET).elf
$(OBJCOPY) -O ihex $< $@
# 生成bin文件
$(TARGET).bin: $(TARGET).elf
$(OBJCOPY) -O binary $< $@
# 编译规则
%.o: %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
# 清理
clean:
rm -f $(OBJS) $(TARGET).elf $(TARGET).hex $(TARGET).bin
# 烧录到设备
flash: $(TARGET).bin
openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg \
-c "program $(TARGET).bin verify reset exit 0x08000000"
.PHONY: all clean flash
4. 链接脚本 (STM32F103C8Tx_FLASH.ld)
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 64K
}
SECTIONS
{
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
. = ALIGN(4);
} >FLASH
.text :
{
. = ALIGN(4);
*(.text)
*(.text*)
. = ALIGN(4);
} >FLASH
.rodata :
{
. = ALIGN(4);
*(.rodata)
*(.rodata*)
. = ALIGN(4);
} >FLASH
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data)
*(.data*)
. = ALIGN(4);
_edata = .;
} >RAM AT> FLASH
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss)
*(.bss*)
. = ALIGN(4);
_ebss = .;
} >RAM
}
5. 案例功能说明
这个嵌入式LED系统实现了以下功能:
-
LED控制模式:
-
模式0:关闭所有LED
-
模式1:LED常亮
-
模式2:双LED交替慢闪(500ms)
-
模式3:双LED交替快闪(200ms)
-
模式4:呼吸灯效果
-
-
按键控制:
-
PA0作为按键输入
-
支持按键消抖
-
按键按下切换LED模式
-
-
系统特性:
-
使用SysTick定时器实现精确延时
-
支持PWM呼吸灯效果
-
错误处理机制
-
6. 硬件连接
-
LED1 → PA5
-
LED2 → PA6
-
按键 → PA0(按下为低电平)
-
使用8MHz外部晶振
7. 编译和烧录
# 编译项目
make
# 清理编译文件
make clean
# 烧录到STM32
make flash
这个案例展示了嵌入式系统中LED控制的基本实现,包括GPIO配置、中断处理、定时器使用和按键扫描等常用技术。您可以根据实际需求进行修改和扩展。