第36章 TIM-全彩呼吸灯

第三十六章 TIM-全彩呼吸灯

1. 硬件设计

与上一章一样

2. 软件设计

2.1 编程目标

  1. 在单色呼吸灯的基础上,增加PWM输出通道,三个通道分别控制红绿蓝颜色;

  2. 编写中断服务函数,增加对拟合波形幅值的控制;

  3. 计算获取新的PWM数据表;

2.2 代码分析

  • 硬件相关宏定义
// 电压幅值等级数
#define AMPLITUDE_CLASS 256

// 控制输出波形的频率
extern __IO uint16_t period_class ;

// PWM表中的点数
extern uint16_t POINT_NUM;

// TIM3宏定义
#define BRE_TIMx TIM3
//中断相关
#define BRE_TIMx_IRQn       TIM3_IRQn             
#define BRE_TIMx_IRQHandler TIM3_IRQHandler
//时钟
#define BRE_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define BRE_TIM_CLK           RCC_APB1Periph_TIM3
#define BRE_TIM_GPIO_CLK     (RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO)
// 红灯的引脚需要重映射
#define BRE_GPIO_REMAP_FUN() GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);                 
// 红灯
#define BRE_RED_TIM_LED_PORT          GPIOB
#define BRE_RED_TIM_LED_PIN           GPIO_Pin_5
#define BRE_RED_TIM_OCxInit           TIM_OC2Init          // 通道初始化函数
#define BRE_RED_TIM_OCxPreloadConfig  TIM_OC2PreloadConfig // 通道重载配置函数
//通道比较寄存器,以TIMx->CCRx方式可访问该寄存器,设置新的比较值,控制占空比
//以宏封装后,使用这种形式:BRE_TIMx->BRE_RED_CCRx ,可访问该通道的比较寄存器
#define BRE_RED_CCRx                   CCR2        

// 绿灯
#define BRE_GREEN_TIM_LED_PORT         GPIOB
#define BRE_GREEN_TIM_LED_PIN          GPIO_Pin_0
#define BRE_GREEN_TIM_OCxInit          TIM_OC3Init          // 通道初始化函数
#define BRE_GREEN_TIM_OCxPreloadConfig TIM_OC3PreloadConfig // 通道重载配置函数
#define BRE_GREEN_CCRx                 CCR3

// 蓝灯
#define BRE_BLUE_TIM_LED_PORT          GPIOB
#define BRE_BLUE_TIM_LED_PIN           GPIO_Pin_1
#define BRE_BLUE_TIM_OCxInit           TIM_OC4Init           // 通道初始化函数
#define BRE_BLUE_TIM_OCxPreloadConfig  TIM_OC4PreloadConfig  // 通道重载配置函数
#define BRE_BLUE_CCRx                  CCR4
  • 初始化GPIO
// GPIO 初始化
static void TIMx_GPIO_Config(void) 
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(BRE_TIM_GPIO_CLK, ENABLE); // 定时器GPIO时钟使能 
  BRE_GPIO_REMAP_FUN(); // 引脚映射设置
  // 配置LED灯用到的引脚
  //红
  GPIO_InitStructure.GPIO_Pin =  BRE_RED_TIM_LED_PIN ;    
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(BRE_RED_TIM_LED_PORT, &GPIO_InitStructure);
  //绿
  GPIO_InitStructure.GPIO_Pin =  BRE_GREEN_TIM_LED_PIN ;
  GPIO_Init(BRE_GREEN_TIM_LED_PORT, &GPIO_InitStructure);
  //蓝
  GPIO_InitStructure.GPIO_Pin =  BRE_BLUE_TIM_LED_PIN ;
  GPIO_Init(BRE_BLUE_TIM_LED_PORT, &GPIO_InitStructure);
}

本实验对GPIO的初始化也相对单色呼吸灯实验作了修改,同时初始化三个通道。

  • 定义PWM表
/********全彩呼吸灯实验的指数曲线表**********/
/* LED亮度等级 PWM表,指数曲线 ,
此表使用工程目录下的python脚本index_wave.py生成*/
const uint16_t indexWave[] = {
    1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
    2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5,
    5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10,
    11, 12, 12, 13, 14, 15, 17, 18,
    19, 20, 22, 23, 25, 27, 29, 31,
    33, 36, 38, 41, 44, 47, 51, 54,
    58, 63, 67, 72, 77, 83, 89, 95,
    102, 110, 117, 126, 135, 145, 156,
    167, 179, 192, 206, 221, 237, 254,
    272, 292, 313, 336, 361, 387, 415,
    445, 477, 512, 512, 477, 445, 415,
    387, 361, 336, 313, 292, 272, 254,
    237, 221, 206, 192, 179, 167, 156,
    145, 135, 126, 117, 110, 102, 95,
    89, 83, 77, 72, 67, 63, 58, 54, 51,
    47, 44, 41, 38, 36, 33, 31, 29, 27,
    25, 23, 22, 20, 19, 18, 17, 15, 14,
    13, 12, 12, 11, 10, 9, 9, 8, 8, 7, 7,
    6, 6, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3,
    3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1
};

/*********SPWM实验的正弦曲线表*************/
/* SPWM表,正弦曲线,此表使用工程目录下的python脚本sin_wave.py生成*/
const uint16_t indexWave[] = {
    0, 9, 18, 27, 36, 45, 54, 63, 72, 81, 89, 98,
    107, 116, 125, 133, 142, 151, 159, 168, 176,
    184, 193, 201, 209, 218, 226, 234, 242, 249,
    257, 265, 273, 280, 288, 295, 302, 310, 317,
    324, 331, 337, 344, 351, 357, 364, 370, 376,
    382, 388, 394, 399, 405, 410, 416, 421, 426,
    431, 436, 440, 445, 449, 454, 458, 462, 465,
    469, 473, 476, 479, 482, 485, 488, 491, 493,
    496, 498, 500, 502, 503, 505, 506, 508, 509,
    510, 510, 511, 512, 512, 512, 512, 512, 512,
    511, 510, 510, 509, 508, 506, 505, 503, 502,
    500, 498, 496, 493, 491, 488, 485, 482, 479,
    476, 473, 469, 465, 462, 458, 454, 449, 445,
    440, 436, 431, 426, 421, 416, 410, 405, 399,
    394, 388, 382, 376, 370, 364, 357, 351, 344,
    337, 331, 324,  317, 310, 302, 295, 288, 280,
    273, 265, 257, 249, 242, 234, 226, 218, 209,
    201, 193, 184, 176, 168, 159, 151, 142, 133,
    125, 116, 107, 98, 89, 81, 72, 63, 54, 45, 36,
    27, 18, 9, 0
};

//计算PWM表有多少个元素
uint16_t POINT_NUM = sizeof(indexWave)/sizeof(indexWave[0]);

代码中列出的PWM表内元素的最大值均为512,元素个数均为128, 把两个表绘制成成曲线如图:呼吸曲线和正弦曲线

  • NVIC配置
// 配置NVIC中断优先级
static void NVIC_Config_PWM(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 设置中断优先级组1

  // 配置TIM3_IRQ中断为中断源
  NVIC_InitStructure.NVIC_IRQChannel = BRE_TIMx_IRQn; // 选择中断源
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级0
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; // 子优先级2
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
  NVIC_Init(&NVIC_InitStructure); // 初始化
}
  • 定时器PWM配置
static void TIMx_Mode_Config(void)
{
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  TIM_OCInitTypeDef  TIM_OCInitStructure;                                                                                

  // 设置TIMCLK 时钟
  BRE_TIM_APBxClock_FUN(BRE_TIM_CLK, ENABLE);

  TIM_TimeBaseStructure.TIM_Period = (512-1);                // 当定时器从0计数到 TIM_Period+1 为一个定时周期
  TIM_TimeBaseStructure.TIM_Prescaler = (10-1);              // 设置预分频
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;   // 设置时钟分频系数:不分频(这里用不到)
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;// 向上计数模式
  TIM_TimeBaseInit(BRE_TIMx, &TIM_TimeBaseStructure); // 初始化定时器

  // PWM模式配置
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;            // 配置为PWM模式1
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;// 使能输出
  TIM_OCInitStructure.TIM_Pulse = 0;                           // 设置初始PWM脉冲宽度为0    
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;     // 当定时器计数值小于CCR_Val时为低电平

  // RGB通道
  // 红色
  BRE_RED_TIM_OCxInit(BRE_TIMx, &TIM_OCInitStructure);    
  BRE_RED_TIM_OCxPreloadConfig(BRE_TIMx, TIM_OCPreload_Enable); // 使能预装载    
  // 绿色
  BRE_GREEN_TIM_OCxInit(BRE_TIMx, &TIM_OCInitStructure);    
  BRE_GREEN_TIM_OCxPreloadConfig(BRE_TIMx, TIM_OCPreload_Enable); // 使能预装载    
  // 蓝色
  BRE_BLUE_TIM_OCxInit(BRE_TIMx, &TIM_OCInitStructure);    
  BRE_BLUE_TIM_OCxPreloadConfig(BRE_TIMx, TIM_OCPreload_Enable); // 使能预装载    

  TIM_ARRPreloadConfig(BRE_TIMx, ENABLE); // 使能TIM重载寄存器ARR
  TIM_Cmd(BRE_TIMx, ENABLE); // 使能定时器    
  TIM_ITConfig(BRE_TIMx, TIM_IT_Update, ENABLE); // 使能update中断
  NVIC_Config_PWM(); // 配置中断优先级        
}

本配置中同时使能三个PWM通道,而定时器初始化中的TIM_Period成员被配置为(512-1),即PWM表元素的极大值,TIM_Prescaler被配置为(10-1), 相对于单色呼吸灯实验,提高了定时器的时钟频率。

3. 小结

本章与上面一章单色呼吸灯大同小异,就是多加两个通道,用来进行混色,下面我们来复盘一下:

实验步骤:

  1. 硬件连接
  • 连接LED的红、绿、蓝引脚到STM32F103的PWM输出引脚(如TIM1、TIM2等)。
  • 通过适当的电阻限流。
  1. 配置时钟
  • 配置系统时钟,确保TIM的时钟频率正确。
  1. 配置定时器(TIM)
  • 设置TIM为PWM模式。
  • 配置TIM的预分频器和自动重装载寄存器(ARR)以获得所需的PWM频率。
  • 配置通道1、2和3来控制红、绿、蓝LED。
  1. 编写代码
  • 初始化TIM。
  • 设置PWM输出。
  • 实现呼吸灯效果的逻辑,通过调整PWM占空比来实现渐变效果。

代码详解:

#include "stm32f10x.h"

// 定义周期和预分频器的值
#define PERIOD  1000
#define PRESCALER  72

// 初始化TIM
void TIM_Config(void) {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_OCInitTypeDef TIM_OCInitStruct;

    TIM_TimeBaseInitStruct.TIM_Prescaler = PRESCALER - 1;
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStruct.TIM_Period = PERIOD - 1;
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct);

    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStruct.TIM_Pulse = PERIOD / 2; // 初始占空比50%
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;

    TIM_OC1Init(TIM1, &TIM_OCInitStruct);
    TIM_OC2Init(TIM1, &TIM_OCInitStruct);
    TIM_OC3Init(TIM1, &TIM_OCInitStruct);

    TIM_CtrlPWMOutputs(TIM1, ENABLE);
    TIM_Cmd(TIM1, ENABLE);
}

// 设置PWM占空比
void SetPWM_Pulse(uint32_t channel, uint32_t pulse) {
    if (channel == TIM_Channel_1) TIM1->CCR1 = pulse;
    if (channel == TIM_Channel_2) TIM1->CCR2 = pulse;
    if (channel == TIM_Channel_3) TIM1->CCR3 = pulse;
}

// 呼吸灯效果
void BreathingLight(void) {
    uint32_t pulse;
    while (1) {
        for (pulse = 0; pulse < PERIOD; pulse++) {
            SetPWM_Pulse(TIM_Channel_1, pulse);
            SetPWM_Pulse(TIM_Channel_2, PERIOD - pulse);
            SetPWM_Pulse(TIM_Channel_3, (pulse + PERIOD / 2) % PERIOD); // 简单变化的蓝色
            Delay(10); // 延时函数,根据需要调整
        }
    }
}

// 简单延时函数
void Delay(uint32_t time) {
    for (volatile uint32_t i = 0; i < time * 1000; i++);
}

int main(void) {
    SystemInit();
    TIM_Config();
    BreathingLight();
    while (1);
}

代码说明:

  • TIM_Config: 配置定时器TIM1为PWM模式,设置预分频器和周期。TIM_OCInitStruct 用于配置PWM的占空比和极性。
  • SetPWM_Pulse: 调节指定通道的PWM占空比。
  • BreathingLight: 实现呼吸灯效果,通过调整PWM占空比来使LED颜色变化。
  • Delay: 一个简单的延时函数,用于调整亮度变化的速度。

2024.9.18 第一次修订,后期不再维护

posted @ 2024-09-18 09:32  hazy1k  阅读(5)  评论(0编辑  收藏  举报