单片机PWM调光实现指南(附完整源码详解)

单片机实现PWM调光项目详解

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


目录

  1. 项目背景与简介

  2. PWM调光工作原理
    2.1 PWM基础原理
    2.2 PWM调光的实现方法

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

  4. 硬件电路设计
    4.1 LED调光电路
    4.2 单片机与外设接口

  5. 软件实现方案
    5.1 定时器PWM输出配置
    5.2 占空比控制与调光算法

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

  7. 代码解读与测试结果

  8. 项目总结与体会

  9. 扩展阅读与参考资料


1. 项目背景与简介

PWM调光在LED照明系统中应用广泛,通过调节PWM信号的占空比,可以改变LED灯的平均亮度,实现从全亮到全暗的平滑过渡。利用单片机控制PWM调光具有实现简单、成本低、控制灵活等特点,适用于家居、舞台照明、节日装饰等场合。本项目将详细介绍如何利用单片机生成PWM信号,并通过软件动态调整占空比,控制LED亮度变化。


2. PWM调光工作原理

2.1 PWM基础原理

PWM(脉宽调制)是一种通过改变数字信号高电平持续时间(占空比)来调制输出平均电压的技术。公式如下:

当占空比改变时,LED灯的平均电流也随之变化,从而实现调光效果。

2.2 PWM调光的实现方法

  • 硬件实现:单片机内置定时器可直接生成PWM波形,通过改变比较寄存器的值来调节占空比。

  • 软件控制:通过编写程序实时修改PWM信号的占空比,实现亮度调节;可以结合按键、遥控或自动调节算法实现多种模式。


  • 3. 系统设计方案

    3.1 项目需求与功能描述

    本项目主要需求:

  • 利用单片机生成固定频率的PWM信号(例如1KHz),并通过修改占空比实现LED调光;

  • 通过软件接口(如按键或串口命令)实现占空比的动态调整;

  • 调试信息通过USART输出,便于监控PWM参数和LED亮度变化;

  • 系统架构简单,便于后续扩展(如加入自动调光、颜色混合等功能)。

  • 3.2 系统整体架构

    系统主要模块包括:

  • PWM生成模块:利用单片机定时器生成PWM信号,输出到LED驱动电路;

  • 调光控制模块:通过修改PWM比较寄存器实现占空比调节;

  • 用户交互模块:按键或串口用于调节占空比,实现亮度变化;

  • 调试模块:通过USART输出当前PWM参数,方便系统调试。


  • 4. 硬件电路设计

    4.1 LED调光电路

  • 选用LED或LED阵列,每个LED串联限流电阻;

  • 单片机的PWM输出端口连接LED调光电路,调光电路可直接驱动LED,也可通过晶体管、MOSFET等实现更大电流驱动。

  • 4.2 单片机与外设接口

  • 选用STM32F103系列单片机或其他常用单片机;

  • 分配足够的GPIO口用于PWM信号输出;

  • USART接口(如PA9/PA10)用于调试输出;

  • 按键接口(可选)用于调节占空比,增加用户交互。


  • 5. 软件实现方案

    5.1 定时器PWM输出配置

  • 通过单片机内置定时器(如TIM2)配置PWM输出模式;

  • 根据系统时钟和期望PWM频率计算自动重装载寄存器(ARR)的值;

  • 根据设定占空比计算比较寄存器(CCR)的值,初始占空比例如50%。

  • 5.2 占空比控制与调光算法

  • 编写函数实现占空比的动态调整,可以通过按键或串口命令改变占空比;

  • 占空比的范围通常为0%(LED熄灭)到100%(LED全亮),调光函数实时修改PWM比较寄存器CCR的值;

  • 通过延时或其他方式平滑过渡,防止亮度突变。


  • 6. 详细代码实现

    下面给出基于STM32F103的示例代码,实现PWM调光功能。代码中通过定时器TIM2生成PWM信号,初始占空比为50%,并通过按键(或预设变量)动态调整占空比,同时通过USART输出当前占空比进行调试。
    注:代码使用STM32标准外设库,实际项目中可根据需求进行修改和扩展。

    6.1 整合代码及详细注释

    /***********************************************************************
     * 文件名称:PWM_Dimming.c
     * 项目名称:单片机实现PWM调光
     * 文件描述:本文件利用单片机内置定时器生成PWM信号,通过动态修改
     *           PWM占空比实现LED调光。通过按键或预设变量控制占空比,
     *           实现LED从全暗到全亮的平滑过渡,同时通过USART输出调试信息。
     * 作者      :Katie
     * 日期      :2025-04-03
     *
     * 说明:
     * 1. 采用STM32F103单片机,通过TIM2生成固定频率的PWM信号(例如1KHz)。
     * 2. 通过修改比较寄存器CCR的值调整PWM占空比,进而调节LED亮度。
     * 3. USART调试接口用于输出当前占空比和调光状态。
     * 4. 可通过按键控制或预设程序实现占空比的动态调整。
     ***********************************************************************/
    
    #include "stm32f10x.h"    // STM32F10x标准外设库头文件
    #include <stdio.h>
    #include <string.h>
    #include <stdarg.h>
    
    /*-----------------------------------------------
     宏定义部分:系统参数及外设配置
    -----------------------------------------------*/
    #define SYSTEM_CORE_CLOCK    72000000UL   // 系统时钟72MHz
    
    // PWM输出配置:使用TIM2通道1,输出至PA0(连接LED调光电路)
    #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
    #define INITIAL_DUTY_CYCLE   50      // 初始占空比50%
    
    /*-----------------------------------------------
     全局变量定义
    -----------------------------------------------*/
    volatile uint32_t pwmPeriod = 0;   // 自动重装载寄存器ARR值
    volatile uint32_t pwmPulse = 0;    // 比较寄存器CCR值
    volatile uint8_t currentDutyCycle = INITIAL_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;
        // 计算定时器ARR值:f_PWM = Timer_Clock / (ARR + 1)
        uint32_t timerClock = SYSTEM_CORE_CLOCK;
        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);
        
        TIM_OCInitTypeDef TIM_OCInitStructure;
        TIM_OCInitStructure.TIM_OCMode = PWM_CHANNEL;
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
        // 初始占空比50%
        pwmPulse = (INITIAL_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;
        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);
        int len = strlen(buffer);
        for(int 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调光程序启动...\r\n");
        
        // 示例:逐步改变LED亮度,从0%到100%,再返回0%
        while(1)
        {
            for(uint8_t duty = 0; duty <= 100; duty += 5)
            {
                Set_LED_Brightness(duty);
                Delay_ms(100);
            }
            for(uint8_t duty = 100; duty > 0; duty -= 5)
            {
                Set_LED_Brightness(duty);
                Delay_ms(100);
            }
        }
        
        return 0;
    }
    

    7. 代码解读与测试结果

    7.1 代码解读

  • PWM初始化
    TIM_PWM_Init()函数根据系统时钟和预设PWM频率(1KHz)计算自动重装载寄存器ARR的值,并将初始占空比设置为50%。通过配置比较寄存器CCR,实现PWM波形输出。

  • 调光控制
    Set_LED_Brightness()函数通过更新CCR寄存器值来改变PWM信号的占空比,从而调节LED亮度。USART调试输出可以实时查看当前占空比及相关寄存器值。

  • 动态调光效果
    主循环中通过for循环逐步增加和减少占空比,实现LED从全暗到全亮的平滑过渡。Delay_ms()函数用于控制每个占空比保持的时间,实现调光速度控制。

  • 7.2 测试结果

  • 在Proteus仿真或实际硬件上测试时,观察PWM信号和LED亮度,LED从0%亮度逐渐增加到100%后,再逐渐降低,形成平滑调光效果;

  • USART调试终端输出当前占空比、ARR和CCR值,验证PWM参数计算正确;

  • 系统响应稳定,调光效果流畅,无明显闪烁现象。


  • 8. 项目总结与体会

    本项目利用单片机实现了基于PWM调光的LED亮度调节,主要体会如下:

  • PWM调制原理
    通过改变PWM信号占空比,可以精确控制LED的平均亮度,实现从全暗到全亮的调光效果。

  • 定时器配置与计算
    通过计算定时器的ARR和CCR寄存器值,实现所需PWM频率和占空比。合理的时钟配置和计算公式是确保调光精度的关键。

  • 软件调光控制
    采用循环结构逐步修改占空比,实现LED亮度平滑过渡;延时函数用于控制调光速度,适合于简单的调光需求。

  • 调试与验证
    利用USART调试输出,可实时监控PWM参数和调光状态,帮助验证系统设计和算法正确性。

  • 总体来说,该项目为嵌入式系统中实现PWM调光提供了完整的实现方案,对初学者掌握PWM技术、定时器配置和亮度调节具有重要参考意义。


    9. 扩展阅读与参考资料

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

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

    3. STM32F10x 数据手册与定时器、PWM应用指南

    4. 在线技术博客(如CSDN、博客园)中关于PWM调光及LED驱动的相关文章

    5. Proteus仿真软件使用指南


    结语

    本文详细介绍了如何利用单片机实现PWM调光,通过定时器生成PWM信号,并利用软件动态调整占空比实现LED亮度调节。从项目背景、工作原理、系统设计方案、硬件与软件实现,到详细代码实现及注释,再到代码解读和测试结果,全面展示了调光功能的实现过程。
    作者:Katie
    希望本文能为你在嵌入式系统开发、PWM技术及LED调光应用方面提供有益启发,欢迎在实践中不断探索和完善该方案!

    作者:Katie。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 单片机PWM调光实现指南(附完整源码详解)

    发表回复