用C语言写LED灯嵌入式系统案例|STM32 LED控制与按键输入系统

我来为您提供一个基于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系统实现了以下功能:

  1. LED控制模式

    • 模式0:关闭所有LED

    • 模式1:LED常亮

    • 模式2:双LED交替慢闪(500ms)

    • 模式3:双LED交替快闪(200ms)

    • 模式4:呼吸灯效果

  2. 按键控制

    • PA0作为按键输入

    • 支持按键消抖

    • 按键按下切换LED模式

  3. 系统特性

    • 使用SysTick定时器实现精确延时

    • 支持PWM呼吸灯效果

    • 错误处理机制

6. 硬件连接

  • LED1 → PA5

  • LED2 → PA6

  • 按键 → PA0(按下为低电平)

  • 使用8MHz外部晶振

7. 编译和烧录

复制代码
# 编译项目
make

# 清理编译文件
make clean

# 烧录到STM32
make flash

这个案例展示了嵌入式系统中LED控制的基本实现,包括GPIO配置、中断处理、定时器使用和按键扫描等常用技术。您可以根据实际需求进行修改和扩展。

相关推荐
电子工程师成长日记-C512 小时前
51单片机电子打铃系统
单片机·嵌入式硬件·51单片机
励志的小陈2 小时前
数据结构--队列(C语言实现)
c语言·开发语言·数据结构
iCxhust3 小时前
Keil µVision 调试指南---UART#1 模拟/调试窗口 完全使用教程
stm32·单片机·嵌入式硬件
iCxhust3 小时前
51单片机引脚 ALE EA PSEN的用途
单片机·嵌入式硬件·51单片机
碎像3 小时前
51单片机创建项目
单片机·嵌入式硬件·51单片机
木白CPP3 小时前
MCU 进程内存布局详解(.text, .rodata, .data, .bss, 堆, 栈)
单片机·嵌入式硬件
風清掦3 小时前
【江科大STM32学习笔记-10】I2C通信协议 - 10.2 硬件 I2C 读写MPU6050
笔记·stm32·单片机·嵌入式硬件·学习
ALINX技术博客4 小时前
【黑金云课堂】FPGA技术教程Vitis开发:RTC中断讲解
单片机·嵌入式硬件·fpga开发
进击的小头4 小时前
第10篇:嵌入式芯片中断系统详解:NVIC与硬实时性优化设计
单片机·嵌入式硬件