找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 313|回复: 3
收起左侧

stm32求助:PWM功能实现流水灯,怎样合适修改参数以实现较好的流水灯频率?

[复制链接]
ID:1162684 发表于 2025-11-7 21:57 | 显示全部楼层 |阅读模式
各位帖友大家好,本人是一枚刚学stm32的萌新。一直修改PWM的有关参数,就是无法达到想要的呼吸灯效果。
想要的呼吸灯效果:LED6和LED7交接闪烁。LED6从暗到亮时,LED7从亮到暗;紧接着LED6从亮到暗,LED7从暗到亮。
目前存在的问题:1、两盏灯之间交接得太慢:LED6从暗到亮,要过很久LED7才会从亮到暗。2、单个灯的呼吸效果不明显,一下子就从暗到亮。

以下是代码,不胜感激!
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"    // 包含led.h,使用LED_ON_OFF函数
#include "rtc.h"
#include "smg.h"
#include "timer.h"
#include "beep.h"
#include "exti.h"   // 包含exti.h,使用其中定义的类型
#include "key.h"
#include "test.h"
#include "adc.h"
#include "string.h"

/************************************************
STM32F103RC Fall Detection System
Functions: 1.Normal state: digital tube displays RTC time, LED6 and LED7 breathing light
            2.Fall detected (KEY2 pressed): buzzer sounds, LED0-LED7 flowing light
            3.KEY0 confirms safety, sends "Safe", stops buzzer;
              KEY1 confirms unsafe, sends "Unsafe", buzzer beeps every 3 seconds
            4.After receiving "SOS received" via serial port, LED0-LED7 all light up and buzzer sounds
************************************************/

// 全局变量定义 - 现在使用exti.h中定义的SystemState类型
SystemState system_state = STATE_NORMAL;  // System state
u8 LED_Shift = 0xFF;      // LED display control
u8 smg_wei = 0;           // Digital tube bit selection
u8 num = 0;               // Digital tube display value
u16 breath_count = 0;     // Breathing light counter
u8 breath_dir = 0;        // Breathing light direction (0-brighten, 1-dim)
u16 flow_count = 0;       // Flowing light counter
u8 flow_index = 0;        // Flowing light index
u16 beep_count = 0;       // Buzzer counter
u8 all_led_on = 0;        // All LEDs on flag
u8 all_led_timer = 0;     // All LEDs on timer
u8 sos_beep_flag = 0;     // SOS buzzer flag
u8 sos_beep_timer = 0;    // SOS buzzer timer
u16 unsafe_timeout = 0;   // Unsafe state timeout counter
u16 safe_timeout = 0;     // Safe state timeout counter
u16 fall_timeout = 0;     // Fall detected state timeout counter
u16 brightness = 0;    // 亮度值 0-1000

        
// Digital tube segment codes
// 0,1,2,3,4,5,6,7,8,9,dot,all off
u8 smg_num[] = {0xfc, 0x60, 0xda, 0xf2, 0x66, 0xb6, 0xbe, 0xe0, 0xfe, 0xf6, 0x01, 0x00};

// 超时时间定义 (单位: 50ms)
#define FALL_STATE_TIMEOUT   60   // 摔倒状态30秒超时 (50ms * 60 = 3s)
#define SAFE_STATE_TIMEOUT   40  // 安全状态20秒超时 (50ms * 40 = 2s)
#define UNSAFE_STATE_TIMEOUT 120 // 不安全状态60秒超时 (50ms * 120 = 6s)
#define LED6_PWM_VAL  TIM3->CCR1  // PC6
#define LED7_PWM_VAL  TIM3->CCR2  // PC7

// Function declarations
void Display_Time(void);
void Breath_LED(void);
void Flow_LED(void);
void USART_ProcessCommand(u8* buf, u16 len);
void Set_RTC_To_Beijing_Time(void);
void Reset_To_Normal_State(void);
void TIM3_PWM_Init(u16 arr, u16 psc);

// 主函数
int main(void) {
    // 系统初始化
    Stm32_Clock_Init(9);    // 系统时钟设置
    delay_init(72);         // 延时初始化
    uart_init(72, 115200);  // 串口初始化
    LED_Init();             // LED初始化
    BEEP_Init();            // 蜂鸣器初始化
    LED_SMG_Init();         // 数码管初始化
    EXTIX_Init();           // 外部中断初始化(在exti.c中定义)
    TIM3_PWM_Init(999, 71);
        
    // RTC初始化
    while(RTC_Init()) {
        printf("RTC Init Failed!\r\n");
        delay_ms(800);
        printf("RTC Retry Init...\r\n");
    }

    // 设置RTC为北京时间
    Set_RTC_To_Beijing_Time();

    // 初始化定时器
    TIM3_Init(19, 7199);    // 定时器3初始化,2ms中断一次
    TIM2_Init(499, 7199);   // 定时器2初始化,50ms中断一次

    printf("Fall Detection System Init Complete!\r\n");
    printf("Normal: Digital tube shows time, LED6/LED7 breathing\r\n");
    printf("Press KEY2 to simulate fall, KEY0 for safe, KEY1 for unsafe\r\n");
    printf("Current RTC Time: %d-%d-%d %02d:%02d:%02d\r\n",
           calendar.w_year, calendar.w_month, calendar.w_date,
           calendar.hour, calendar.min, calendar.sec);
    printf("Timeout settings: Fall(30s), Safe(20s), Unsafe(60s)\r\n");

    // 初始状态:所有LED灭
    LED_ON_OFF(0xFF);

    // 主循环
    while(1) {
        // 处理串口接收
        if(USART_RX_STA & 0x8000) {
            u16 len = USART_RX_STA & 0x3FFF;
            USART_RX_BUF[len] = '\0';

            USART_ProcessCommand(USART_RX_BUF, len);

            USART_RX_STA = 0;
        }

        // 处理LED全亮状态(5秒后自动恢复)
        if(all_led_on) {
            all_led_timer++;
            if(all_led_timer > 2500) { // 5秒后恢复
                all_led_on = 0;
                all_led_timer = 0;
                sos_beep_flag = 0; // 停止SOS蜂鸣器
                if(system_state == STATE_NORMAL) {
                    LED_ON_OFF(0xFF); // 恢复为全灭
                }
            }
        }

                //处理呼吸灯
                if(system_state == STATE_NORMAL || system_state == STATE_CONFIRMED_SAFE){
                if(breath_dir == 0) {            
            brightness += 3;
            if(brightness >= 999) {                 
                breath_dir = 1;            // 改变方向
                printf("Switch to direction 1\r\n");
            }
                }
                else {
            brightness -= 3;
            if(brightness <= 0) {               
                breath_dir = 0;            // 改变方向
                printf("Switch to direction 0\r\n");
            }
        }

        LED6_PWM_VAL = brightness;        
        LED7_PWM_VAL = 999 - brightness;
               
        delay_ms(10);
    }
}
}

// 重置到正常状态
void Reset_To_Normal_State(void) {
    system_state = STATE_NORMAL;
    BEEP = 1;  // 停止蜂鸣器
    LED_ON_OFF(0xFF); // 恢复LED状态
    fall_timeout = 0;
    safe_timeout = 0;
    unsafe_timeout = 0;
        beep_count = 0;
        sos_beep_flag = 0;
        sos_beep_timer = 0;
        brightness = 0;
        breath_dir = 0;
        LED6_PWM_VAL = 0;
        LED7_PWM_VAL = 0;
    printf("Timeout: Returning to normal state\r\n");
}

// 设置RTC为北京时间
void Set_RTC_To_Beijing_Time(void) {
    // 这里需要根据实际时间设置,示例设置为2024年1月1日 12:00:00
    // 在实际使用中,可以通过串口命令来设置准确的时间
    RTC_Set(2024, 1, 1, 12, 0, 0);
    printf("RTC set to Beijing time: 2024-01-01 12:00:00\r\n");
    printf("Use serial command 'SETTIME:YYYY-MM-DD HH:MM:SS' to set exact time\r\n");
}

// TIM2中断处理函数 - 用于处理蜂鸣器周期性响铃和超时恢复
void TIM2_IRQHandler(void) {
    if(TIM2->SR & 0X0001) {  // 溢出中断
        // 状态超时处理
        switch(system_state) {
            case STATE_FALL_DETECTED:
                // 摔倒状态30秒后自动恢复
                fall_timeout++;
                if(fall_timeout >= FALL_STATE_TIMEOUT) {
                    Reset_To_Normal_State();
                    printf("Fall state timeout (30s), auto recovery\r\n");
                }
                break;

            case STATE_CONFIRMED_SAFE:
                // 安全状态20秒后自动恢复
                safe_timeout++;
                if(safe_timeout >= SAFE_STATE_TIMEOUT) {
                    Reset_To_Normal_State();
                    printf("Safe state timeout (20s), auto recovery\r\n");
                }
                break;

            case STATE_CONFIRMED_UNSAFE:
                //不安全状态60秒后自动恢复
                unsafe_timeout++;
                if(unsafe_timeout >= UNSAFE_STATE_TIMEOUT) {
                    Reset_To_Normal_State();
                    printf("Unsafe state timeout (60s), auto recovery\r\n");
                                        break;
                }
                                
                // 不安全状态下,蜂鸣器每1秒响200ms
                                
                                        beep_count++;
                                        BEEP = (beep_count % 20 >= 4);  // 前4次中断响(200ms),后16次中断静音(800ms)
                                        break;               
            default:
                // 正常状态,不做超时处理
                break;
        }

        // SOS蜂鸣器处理 (快速响鸣)
        if(sos_beep_flag) {
            sos_beep_timer++;
            if(sos_beep_timer % 2 == 0) { // 每1秒切换一次
                BEEP = !BEEP; // 蜂鸣器快速切换
            }
        }

        // 更新RTC时间
        RTC_Get();
    }
    TIM2->SR &= ~(1 << 0);  // 清除中断标志位
}

// TIM3中断处理函数 - 用于处理LED显示、数码管刷新等
void TIM3_IRQHandler(void) {
    if(TIM3->SR & 0X0001) {  // 溢出中断
        // 数码管显示(所有状态都显示时间)
        Display_Time();

        // 根据系统状态执行不同的LED操作
        switch(system_state) {
            case STATE_NORMAL:
                // 正常状态:LED6和LED7呼吸灯交替亮暗
                //Breath_LED();
                break;

            case STATE_FALL_DETECTED:
                // 检测到摔倒:LED0-LED7流水灯闪烁,蜂鸣器持续响
                Flow_LED();
                BEEP = 0;  // 蜂鸣器持续响
                break;

            case STATE_CONFIRMED_SAFE:
                // 确认安全:LED6和LED7呼吸灯交替亮暗
                //Breath_LED();
                BEEP = 1;  // 停止蜂鸣器
                break;

            case STATE_CONFIRMED_UNSAFE:
                // 确认不安全:LED0-LED7流水灯闪烁
                Flow_LED();
                // 蜂鸣器由TIM2控制每3秒响一次
                break;
        }

        // 所有LED全亮标志处理
        if(all_led_on) {
            LED_ON_OFF(0x00);  // 所有LED全亮
        }
    }
    TIM3->SR &= ~(1 << 0);  // 清除中断标志位
}

// 数码管显示时间
void Display_Time(void) {
    switch(smg_wei) {
        case 0: num = smg_num[calendar.hour / 10]; break;  // Hour tens
        case 1: num = smg_num[calendar.hour % 10]; break;  // Hour units
        case 2: num = 0x01; break;  // Colon (using dot from segment codes)
        case 3: num = smg_num[calendar.min / 10]; break;   // Minute tens
        case 4: num = smg_num[calendar.min % 10]; break;   // Minute units
        case 5: num = 0x01; break;  // Colon
        case 6: num = smg_num[calendar.sec / 10]; break;   // Second tens
        case 7: num = smg_num[calendar.sec % 10]; break;   // Second units
    }

    LED_Write_Data(num, smg_wei);  // Write data
    LED_Refresh();  // Refresh display
    smg_wei = (smg_wei + 1) % 8;  // Cycle through bit selection
}


// 流水灯效果(LED0-LED7依次闪烁)
void Flow_LED(void) {
    flow_count++;
    if(flow_count >= 20) {  // Adjust flowing speed
        flow_count = 0;

        // Use bit operation to light current LED
        LED_Shift = ~(1 << flow_index);  // Light current LED
        LED_ON_OFF(LED_Shift);

        flow_index = (flow_index + 1) % 8;  // Cycle through LEDs
    }
}

// 串口命令处理函数
void USART_ProcessCommand(u8* buf, u16 len) {
    // Compare received command with "SOS received"
    const char* cmd = "SOS received";

    if(len == strlen(cmd)) {
        if(strncmp((char*)buf, cmd, len) == 0) {
            // LED0 to LED7 all on and buzzer sounds
            all_led_on = 1;
            all_led_timer = 0;
            sos_beep_flag = 1; // 启动SOS蜂鸣器
            sos_beep_timer = 0;
            // Output confirmation message to serial
            printf("All LEDs ON and Buzzer activated\r\n");
        }
    }

    // 处理设置时间命令
    if(len >= 7 && strncmp((char*)buf, "SETTIME:", 8) == 0) {
        // 命令格式: SETTIME:2024-01-01 12:00:00
        int year, month, day, hour, min, sec;
        if(sscanf((char*)buf + 8, "%d-%d-%d %d:%d:%d",
                  &year, &month, &day, &hour, &min, &sec) == 6) {
            RTC_Set(year, month, day, hour, min, sec);
            printf("RTC time set to: %d-%02d-%02d %02d:%02d:%02d\r\n",
                   year, month, day, hour, min, sec);
        } else {
            printf("Invalid time format. Use: SETTIME:YYYY-MM-DD HH:MM:SS\r\n");
        }
    }

    // 处理重置命令
    if(len == 6 && strncmp((char*)buf, "RESET", 5) == 0) {
        Reset_To_Normal_State();
        printf("System manually reset to normal state\r\n");
    }
}

void TIM3_PWM_Init(u16 arr, u16 psc)
{  
    // 1. 开启时钟
    RCC->APB1ENR |= 1 << 1;    // TIM3时钟使能
    RCC->APB2ENR |= 1 << 4;    // GPIOC时钟使能
    RCC->APB2ENR |= 1 << 0;    // AFIO时钟使能

    // 2. 配置GPIO为复用推挽输出
    // PC6 - TIM3_CH1 (完全重映射)
    GPIOC->CRL &= 0xF0FFFFFF;  // 清除PC6配置位
    GPIOC->CRL |= 0x0B000000;  // 50MHz, 复用推挽输出

    // PC7 - TIM3_CH2 (完全重映射)
    GPIOC->CRL &= 0x0FFFFFFF;  // 清除PC7配置位
    GPIOC->CRL |= 0xB0000000;  // 50MHz, 复用推挽输出

    // 3. 完全重映射:TIM3_CH1->PC6, TIM3_CH2->PC7
    AFIO->MAPR &= 0xFFFFF3FF;  // 清除MAPR的[11:10]
    AFIO->MAPR |= 3 << 10;     // 完全重映射 (11)

    // 4. 配置TIM3时基
    TIM3->ARR = arr;           // 自动重装载值
    TIM3->PSC = psc;           // 预分频器

    // 5. 配置通道1 (PC6)
    TIM3->CCMR1 |= 6 << 4;     // OC1M=110, PWM模式1
    TIM3->CCMR1 |= 1 << 3;     // OC1PE=1, 预装载使能
    TIM3->CCER |= 1 << 0;      // CC1E=1, 输出使能
    TIM3->CCR1 = 500;          // 初始占空比50%

    // 6. 配置通道2 (PC7)
    TIM3->CCMR1 |= 6 << 12;    // OC2M=110, PWM模式1  
    TIM3->CCMR1 |= 1 << 11;    // OC2PE=1, 预装载使能
    TIM3->CCER |= 1 << 4;      // CC2E=1, 输出使能
    TIM3->CCR2 = 500;          // 初始占空比50%

    // 7. 使能定时器
    TIM3->CR1 |= 1 << 7;       // ARPE=1, 自动重装载预装载使能
    TIM3->CR1 |= 1 << 0;       // CEN=1, 使能计数器

    printf("TIM3 PWM Initialized: PC6(TIM3_CH1), PC7(TIM3_CH2)\r\n");
}


回复

使用道具 举报

ID:685462 发表于 2025-11-7 23:05 | 显示全部楼层
感觉思路可以使用定时器实现呼吸,从暗到亮,从亮到暗。比如呼吸时间从暗-亮-暗总时间4s,明暗级别50级,亮度每一级时间us为4x1000/(50+50)=40us。至于哪个灯运行,按你的逻辑编辑程序就好了
回复

使用道具 举报

ID:1155037 发表于 2025-11-8 11:22 | 显示全部楼层
    TIM3_PWM_Init(999, 71);             // RTC初始化     while(RTC_Init()) {         printf("RTC Init Failed!\r\n");         delay_ms(800);         printf("RTC Retry Init...\r\n");     }      // 设置RTC为北京时间     Set_RTC_To_Beijing_Time();      // 初始化定时器     TIM3_Init(19, 7199);    // 定时器3初始化,2ms中断一次     TIM2_Init(499, 7199);   // 定时器2初始化,50ms中断一次
回复

使用道具 举报

ID:1162684 发表于 2025-11-11 16:36 | 显示全部楼层
lose2836 发表于 2025-11-7 23:05
感觉思路可以使用定时器实现呼吸,从暗到亮,从亮到暗。比如呼吸时间从暗-亮-暗总时间4s,明暗级别50级,亮 ...

感谢!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表