单片机:实现PWM LED灯亮度调节及Proteus仿真(完整源码)

单片机实现PWM LED灯亮度调节及Proteus仿真项目详解

作者:Katie
日期:2025-03-31


目录

  1. 项目背景与简介

  2. PWM与LED亮度调节原理
    2.1 PWM控制原理
    2.2 LED亮度调节原理

  3. 系统设计方案
    3.1 项目需求与功能描述
    3.2 系统整体架构

  4. 硬件电路设计
    4.1 单片机与LED驱动电路
    4.2 Proteus仿真环境搭建

  5. 软件实现方案
    5.1 定时器PWM配置思路
    5.2 亮度调节控制逻辑

  6. 详细代码实现
    6.1 整合代码及详细注释

  7. Proteus仿真步骤

  8. 代码解读与测试结果

  9. 项目总结与体会

  10. 扩展阅读与参考资料


1. 项目背景与简介

在现代嵌入式系统中,利用单片机实现PWM(脉宽调制)控制是调节LED灯亮度的常用方法。通过改变PWM信号的占空比,可以调整LED的平均电流,从而实现从全亮到全灭的亮度变化。为了验证设计的正确性,本项目不仅在实际硬件平台上实现了LED亮度调节程序,同时利用Proteus仿真软件构建虚拟电路进行仿真验证。


2. PWM与LED亮度调节原理

2.1 PWM控制原理

PWM是一种通过改变脉冲宽度来调制信号平均值的方法。其基本原理为:在固定周期内保持高低电平,通过改变高电平持续时间(占空比)实现对平均电压的控制。公式为:

其中,占空比越高,LED点亮时间越长,平均电流越大,亮度越高。

2.2 LED亮度调节原理

LED的亮度与其通过的平均电流成正比。通过PWM信号控制LED,可以实现以下效果:

  • 占空比为0%:LED熄灭;

  • 占空比为50%:LED亮度约为一半;

  • 占空比为100%:LED全亮。

在本项目中,我们采用固定频率(例如1KHz)的PWM信号,通过调整占空比实现亮度调节。


3. 系统设计方案

3.1 项目需求与功能描述

本项目主要功能需求:

  • 利用单片机内置定时器模块产生PWM信号;

  • 通过软件控制改变PWM信号占空比,实现LED亮度调节;

  • 通过Proteus仿真软件构建虚拟电路,实现仿真验证;

  • 利用USART调试接口输出当前PWM参数,便于调试。

3.2 系统整体架构

系统主要分为:

  • PWM生成模块:利用定时器产生固定频率PWM信号,动态更新占空比;

  • LED驱动模块:通过单片机GPIO口输出PWM信号,驱动LED灯;

  • 调试反馈模块:利用USART输出调试信息;

  • Proteus仿真平台:构建虚拟电路,包括单片机、LED、限流电阻、供电电源等,验证设计正确性。


4. 硬件电路设计

4.1 单片机与LED驱动电路

硬件部分选用常见的STM32F103系列单片机,连接LED驱动电路示意如下:

  • PWM输出引脚:选用STM32的PA0作为PWM输出口;

  • LED连接:LED通过限流电阻与PA0连接,LED的另一端接地(或根据具体电路设计接电源);

  • 供电:单片机及LED均由稳定电源供电(例如5V或3.3V)。

4.2 Proteus仿真环境搭建

在Proteus中进行仿真时:

  1. 添加STM32F103C8T6芯片模型;

  2. 在芯片引脚上接入LED及限流电阻;

  3. 配置电源模块(如5V稳压电源);

  4. 添加虚拟串口模块(如虚拟终端),连接到USART引脚(PA9/PA10,用于调试输出,可选);

  5. 配置时钟参数与仿真参数,确保与代码匹配。


5. 软件实现方案

5.1 定时器PWM配置思路

通过单片机内部定时器(如TIM2)生成PWM信号:

  • 设定预分频器、自动重装载寄存器(ARR)及比较寄存器(CCR);

  • 频率公式为:

  • 占空比计算公式:

  • 本项目中设定固定PWM频率(如1KHz),通过修改CCR值改变占空比,达到调节LED亮度的目的。

5.2 亮度调节控制逻辑

  • 定义全局变量记录当前占空比(0~100%);

  • 通过软件接口(如按键或预设程序)改变占空比;

  • 在主循环或定时任务中调用“Set_LED_Brightness”函数,更新PWM比较寄存器;

  • 利用USART输出当前占空比及调试信息,便于调试与验证。


6. 详细代码实现

下面给出基于STM32F103的示例代码,整合了系统初始化、PWM配置及LED亮度调节逻辑,代码中附有详细注释,便于理解。

6.1 整合代码及详细注释

/***********************************************************************
 * 文件名称:PWM_LED_Brightness_Control.c
 * 项目名称:单片机实现PWM LED灯亮度调节及Proteus仿真
 * 文件描述:本文件实现了利用单片机内部定时器产生PWM信号,
 *           并通过动态改变PWM占空比调节LED亮度的功能。程序中
 *           包含系统初始化、PWM配置、LED亮度调节函数及USART调试输出。
 *           同时提供了在Proteus仿真平台下进行电路仿真的指导说明。
 * 作者      :Katie
 * 日期      :2025-03-31
 *
 * 说明:
 * 1. 采用STM32F103系列单片机,通过TIM2生成PWM信号输出到PA0。
 * 2. LED通过限流电阻连接到PA0,PWM占空比控制LED亮度(0%:灭,100%:全亮)。
 * 3. USART1用于调试信息输出,便于在Proteus虚拟终端观察当前占空比参数。
 * 4. Proteus仿真部分请参照文末“Proteus仿真步骤”部分。
 ***********************************************************************/

#include "stm32f10x.h"    // STM32F10x标准外设库头文件
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

/*-----------------------------------------------
 宏定义部分:系统参数及外设配置
-----------------------------------------------*/
#define SYSTEM_CORE_CLOCK    72000000UL   // 系统核心时钟72MHz

// PWM输出配置:使用TIM2通道1,输出至PA0
#define PWM_TIM              TIM2
#define PWM_CHANNEL          TIM_OCMode_PWM1
#define PWM_GPIO_PORT        GPIOA
#define PWM_GPIO_PIN         GPIO_Pin_0

// USART调试接口(使用USART1, TX: PA9, RX: PA10)
#define DEBUG_USART          USART1
#define DEBUG_BAUDRATE       115200

// PWM基本参数
#define PWM_FREQUENCY        1000        // PWM频率1KHz(周期1ms)
#define DEFAULT_DUTY_CYCLE   50          // 默认占空比50%

/*-----------------------------------------------
 全局变量定义
-----------------------------------------------*/
volatile uint32_t pwmPeriod = 0;     // 自动重装载寄存器ARR值
volatile uint32_t pwmPulse = 0;      // 比较寄存器CCR值
volatile uint8_t currentDutyCycle = DEFAULT_DUTY_CYCLE; // 当前占空比(%)

/*-----------------------------------------------
 函数声明
-----------------------------------------------*/
void System_Init(void);
void GPIO_Init_Config(void);
void USART_Init_Config(void);
void TIM_PWM_Init(void);
void Set_LED_Brightness(uint8_t dutyCycle);
void Delay_ms(uint32_t ms);
void USART_Print(const char* fmt, ...);

/*-----------------------------------------------
 函数名称:System_Init
 函数功能:系统初始化,配置时钟、GPIO、USART和PWM定时器
-----------------------------------------------*/
void System_Init(void)
{
    SystemCoreClockUpdate();
    GPIO_Init_Config();
    USART_Init_Config();
    TIM_PWM_Init();
}

/*-----------------------------------------------
 函数名称:GPIO_Init_Config
 函数功能:初始化PWM输出和USART引脚
-----------------------------------------------*/
void GPIO_Init_Config(void)
{
    // 开启GPIOA时钟(PA0用于PWM,PA9/PA10用于USART)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    // 配置PA0为复用推挽输出(PWM输出)
    GPIO_InitStructure.GPIO_Pin = PWM_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(PWM_GPIO_PORT, &GPIO_InitStructure);
}

/*-----------------------------------------------
 函数名称:USART_Init_Config
 函数功能:初始化USART1,用于调试信息输出
-----------------------------------------------*/
void USART_Init_Config(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    // TX: PA9,复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    // RX: PA10,浮空输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = DEBUG_BAUDRATE;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(DEBUG_USART, &USART_InitStructure);
    
    USART_Cmd(DEBUG_USART, ENABLE);
}

/*-----------------------------------------------
 函数名称:TIM_PWM_Init
 函数功能:初始化TIM2生成PWM信号,用于LED亮度调节
-----------------------------------------------*/
void TIM_PWM_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    // 不采用预分频,计数器时钟 = 系统时钟
    uint32_t timerClock = SYSTEM_CORE_CLOCK;
    // 计算ARR值:f_PWM = timerClock / (ARR + 1)
    pwmPeriod = (timerClock / PWM_FREQUENCY) - 1;
    
    TIM_TimeBaseStructure.TIM_Period = pwmPeriod;
    TIM_TimeBaseStructure.TIM_Prescaler = 0;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(PWM_TIM, &TIM_TimeBaseStructure);
    
    // 配置PWM输出模式
    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCInitStructure.TIM_OCMode = PWM_CHANNEL;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    // 初始占空比为50%,计算CCR值:CCR = 50% * (ARR + 1)
    pwmPulse = (DEFAULT_DUTY_CYCLE * (pwmPeriod + 1)) / 100;
    TIM_OCInitStructure.TIM_Pulse = pwmPulse;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(PWM_TIM, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(PWM_TIM, TIM_OCPreload_Enable);
    
    TIM_ARRPreloadConfig(PWM_TIM, ENABLE);
    TIM_Cmd(PWM_TIM, ENABLE);
}

/*-----------------------------------------------
 函数名称:Set_LED_Brightness
 函数功能:设置LED亮度,通过更新PWM比较寄存器(CCR)改变占空比
 参数说明:
   dutyCycle - 目标占空比(0~100)
-----------------------------------------------*/
void Set_LED_Brightness(uint8_t dutyCycle)
{
    if(dutyCycle > 100) dutyCycle = 100;
    currentDutyCycle = dutyCycle;
    // 计算新的CCR值
    pwmPulse = (dutyCycle * (pwmPeriod + 1)) / 100;
    TIM_SetCompare1(PWM_TIM, pwmPulse);
    USART_Print("当前占空比: %d%%, ARR=%lu, CCR=%lu\r\n", dutyCycle, pwmPeriod, pwmPulse);
}

/*-----------------------------------------------
 函数名称:Delay_ms
 函数功能:简单延时函数(非精确,仅用于测试)
-----------------------------------------------*/
void Delay_ms(uint32_t ms)
{
    volatile uint32_t i, j;
    for(i = 0; i < ms; i++)
        for(j = 0; j < 7200; j++);
}

/*-----------------------------------------------
 函数名称:USART_Print
 函数功能:通过USART输出调试信息,封装printf
-----------------------------------------------*/
void USART_Print(const char* fmt, ...)
{
    char buffer[128];
    va_list args;
    va_start(args, fmt);
    vsnprintf(buffer, sizeof(buffer), fmt, args);
    va_end(args);
    
    uint16_t len = strlen(buffer);
    for(uint16_t i = 0; i < len; i++)
    {
        while(USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET);
        USART_SendData(DEBUG_USART, buffer[i]);
    }
}

/*-----------------------------------------------
 主函数:程序入口
-----------------------------------------------*/
int main(void)
{
    System_Init();
    
    USART_Print("PWM LED亮度调节程序启动...\r\n");
    
    // 在Proteus仿真中可观察到LED亮度随占空比变化
    // 这里示例依次改变占空比:0%、25%、50%、75%、100%,各状态保持2秒
    while(1)
    {
        Set_LED_Brightness(0);
        Delay_ms(2000);
        
        Set_LED_Brightness(25);
        Delay_ms(2000);
        
        Set_LED_Brightness(50);
        Delay_ms(2000);
        
        Set_LED_Brightness(75);
        Delay_ms(2000);
        
        Set_LED_Brightness(100);
        Delay_ms(2000);
    }
    
    return 0;
}

7. 代码解读与测试结果

7.1 系统初始化与PWM配置

  • System_Init
    调用GPIO、USART和TIM2初始化函数。GPIO配置中将PA0设置为复用推挽输出(PWM输出);USART初始化配置为115200波特率,用于调试信息输出。

  • TIM_PWM_Init
    根据系统时钟计算ARR值,设定PWM频率为1KHz,初始占空比50%对应的CCR值由公式计算得出。启动TIM2后PWM信号开始输出。

7.2 LED亮度调节逻辑

  • Set_LED_Brightness
    根据输入的占空比参数(0~100%),重新计算CCR值,更新PWM输出。通过USART输出调试信息,便于观察当前占空比、ARR和CCR值。

  • 主循环
    主函数中依次改变占空比,LED亮度由低至高变化,每个状态保持2秒。通过Proteus仿真可以观察到LED亮度随占空比调整而变化。

7.3 Proteus仿真结果

在Proteus仿真环境中:

  • 构建包含STM32F103、LED及限流电阻、供电模块的虚拟电路;

  • 配置仿真参数(时钟、供电电压等)与代码一致;

  • 通过虚拟终端(USART调试模块)可以实时查看程序输出信息;

  • 仿真运行过程中LED亮度随着程序中设置的占空比变化,符合预期效果。


8. Proteus仿真步骤

  1. 创建工程
    在Proteus中新建一个工程,选择STM32F103C8T6芯片模型。

  2. 添加器件

    • 添加STM32F103C8T6、LED、限流电阻、稳压电源(例如5V);

    • 添加虚拟串口终端(Virtual Terminal),连接到USART1的TX引脚(PA9)。

  3. 连接电路

    • 将STM32的PA0连接到LED的正极,LED负极接限流电阻后接地;

    • 配置LED工作电压和电阻值,确保LED正常工作;

    • 连接电源,确保单片机和器件获得稳定电源。

  4. 配置仿真参数
    设置STM32F103的系统时钟为72MHz,并确保仿真环境电压与代码匹配。

  5. 编译与加载程序
    将上述代码编译生成HEX文件,在Proteus中加载HEX文件到STM32芯片模型上。

  6. 启动仿真
    启动仿真后,通过虚拟终端观察USART输出,同时观察LED亮度变化,验证PWM调节效果。


9. 项目总结与体会

本项目通过单片机利用定时器产生PWM信号实现了LED亮度调节,同时在Proteus仿真环境中验证了设计的正确性。主要体会如下:

  • PWM调制与亮度控制
    利用定时器PWM输出调节LED亮度的方法简单高效,通过改变占空比即可实现从全暗到全亮的连续调节。

  • 软件调试的重要性
    通过USART调试输出,可以实时监控PWM参数(ARR、CCR),便于验证计算公式与实际效果。

  • Proteus仿真验证
    利用Proteus搭建虚拟电路进行仿真,有助于在实际硬件实现前排查问题、调试程序,并降低开发风险。

  • 模块化设计
    系统将硬件初始化、PWM配置、亮度调节及调试输出分为独立模块,便于后续扩展(如添加按键调节、远程控制等功能)。

总体来说,该项目为嵌入式系统中LED亮度调节提供了一个完整参考案例,对初学者了解PWM原理、定时器配置及Proteus仿真方法具有较高的指导意义。


10. 扩展阅读与参考资料

  1. 《嵌入式系统原理与实践》

  2. 《STM32微控制器实战开发》

  3. STM32F103系列数据手册与参考手册

  4. 在线技术博客(如CSDN、博客园等)中关于PWM、LED控制及Proteus仿真的相关文章

  5. Proteus仿真软件使用指南


结语

本文详细介绍了如何利用单片机实现PWM LED灯亮度调节,并在Proteus仿真中验证设计。文章从项目背景、PWM及LED亮度原理、系统设计、硬件电路设计、软件实现方案、详细代码实现与注释,到Proteus仿真步骤、代码解读及测试结果,全面展示了整个设计流程。
作者:Katie
希望本文能为你在嵌入式系统开发和仿真调试方面提供有益启发,欢迎在实践中不断探索和完善该方案!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值