ECE STM32开发 呼吸灯

一、实验目的

  • 了解呼吸灯的原理
  • 了解正弦脉宽调制(Sinusoidal Pulse Width Modulation,SPWM),学会使用通用定时器输出SPWM从而实现呼吸灯
  • 学会使用基本定时器产生中断

二、实验过程

1.使用python计算SPWM表并画出得到的正弦曲线,使灯的亮度按照正弦曲线变化。

2.调用bsp_spwm库中的TIMx_Breathing_Init函数,进行GPIO输出初始化,TIM3时基初始化、输出初始化、技术使能,嵌套向量中断控制器(NVIC)设置。

3.调用bsp_TiMbase库中的BASIC_TIM_Init函数,进行TIM6时基初始化、输出初始化、输出初始化、技术使能,嵌套向量中断控制器(NVIC)设置。

4.编写 BRE_TIMx_IRQHandler 函数,当 TIM3 发出中断请求时,该函数将 LED 灯亮度按照 SPWM 表设置。

5.编写 TIM3_IRQHandler 函数,当 TIM6 发出中断请求时,该函数将 LED 灯颜色按照红绿蓝白的顺序循环设置。

6.根据实际需要设置决定呼吸周期的各变量值,计算公式为呼吸周期=1/(时钟频率/预分频)RGB 等级周期倍数亮度等级SPWM 点数,本实验呼吸周期为1/(720000004)2561512*247≈1.8s。周期倍数为 period_class 变量值加 1,因为 period_cnt 变量需要从 0 增加到大于 period_class 时才会指向 SPWM 表中下一个元素。

7.根据呼吸周期设置决定 TIM3 发出中断周期的各变量值,计算公式为发出中断周期=呼吸周期=(时钟频率/预分频)*计数周期,本实验发出中断周期为(72000000/10000)*12960=1.8s。

三、附录

main.c

#include "stm32f10x.h"
#include "bsp_spwm.h"
#include "bsp_TiMbase.h"

__IO uint32_t rgb_color = 0x000000;
unsigned int ColorState=0;
extern uint16_t indexWave[];
extern __IO uint32_t rgb_color;
void BRE_TIMx_IRQHandler(void);
void TIM6_IRQHandler(void);

int main(void)
{			
	TIMx_Breathing_Init();
	BASIC_TIM_Init();
	while(1);
}

void BRE_TIMx_IRQHandler(void)
{	
	static uint16_t pwm_index = 0;//用于SPWM查表
	static uint16_t period_cnt = 0;//用于计算周期数
	static uint16_t amplitude_cnt = 0;//用于计算幅值等级

	if (TIM_GetITStatus(BRE_TIMx, TIM_IT_Update)!=RESET)
 	{
			amplitude_cnt++;
			//每个SPWM表中的每个元素有AMPLITUDE_CLASS个等级,
		  //每增加一级多输出一次脉冲,即SPWM表中的元素多使用一次
		  //使用256次,根据RGB颜色分量设置通道输出
			if(amplitude_cnt > (AMPLITUDE_CLASS-1))		 						
			{		
				period_cnt++;
				//每个SPWM表中的每个元素使用period_class次
				if(period_cnt > period_class)
				{				
					pwm_index++;//标志SPWM表指向下一个元素
					//若PWM表已到达结尾,重新指向表头
					if(pwm_index >= POINT_NUM)			
					{
						pwm_index=0;								
					}
					period_cnt = 0;//重置周期计数标志
				}
				amplitude_cnt=0;//重置幅值计数标志
			}
			else
			{	
					//每个SPWM表中的每个元素有AMPLITUDE_CLASS个等级,
					//每增加一级多输出一次脉冲,即SPWM表中的元素多使用一次
					//根据RGB颜色分量值,设置各个通道是否输出当前的SPWM表元素表示的亮度
					//红
					if(((rgb_color&0xFF0000)>>16) >= amplitude_cnt)				
						BRE_TIMx->BRE_RED_CCRx = indexWave[pwm_index];//根据SPWM表修改定时器的比较寄存器值
					else
						BRE_TIMx->BRE_RED_CCRx = 0;//比较寄存器值为0,通道输出高电平,该通道LED灯灭
					//绿
					if(((rgb_color&0x00FF00)>>8) >= amplitude_cnt)				
						BRE_TIMx->BRE_GREEN_CCRx = indexWave[pwm_index];//根据SPWM表修改定时器的比较寄存器值
					else
						BRE_TIMx->BRE_GREEN_CCRx = 0;//比较寄存器值为0,通道输出高电平,该通道LED灯灭
					//蓝
					if((rgb_color&0x0000FF) >= amplitude_cnt)				
						BRE_TIMx->BRE_BLUE_CCRx = indexWave[pwm_index];//根据SPWM表修改定时器的比较寄存器值
					else
						BRE_TIMx->BRE_BLUE_CCRx = 0;//比较寄存器值为0,通道输出高电平,该通道LED灯灭	
			}
		TIM_ClearITPendingBit (BRE_TIMx, TIM_IT_Update);//清除中断标志位
	}
}

void TIM6_IRQHandler(void){
	if(TIM_GetITStatus(BASIC_TIM, TIM_IT_Update)!=RESET){
		switch(ColorState){
			case 0://红
				rgb_color = 0xFF0000;
				ColorState=1;
				break;
			case 1://绿
				rgb_color = 0x00FF00;
				ColorState=2;
				break;
			case 2://蓝
				rgb_color = 0x0000FF;
				ColorState=3;
				break;
			case 3://白
				rgb_color = 0xFFFFFF;
				ColorState=0;
				break;
		}
	}
	TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update);
}

bsp_spwm.c

#include "bsp_spwm.h"

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


/* SPWM表,正弦曲线*/
const uint16_t indexWave[] = {
      0,   7,  13,  20,  26,  33,  39,  46,  52,  59,  65,  72,
   78,  85,  91,  97, 104, 110, 117, 123, 129, 136, 142, 148,
    154, 161, 167, 173, 179, 185, 191, 197, 203, 209, 215, 221,
    227, 233, 239, 245, 250, 256, 262, 267, 273, 278, 284, 289,
    295, 300, 305, 310, 316, 321, 326, 331, 336, 341, 346, 350,
    355, 360, 364, 369, 373, 378, 382, 387, 391, 395, 399, 403,
    407, 411, 415, 419, 423, 426, 430, 433, 437, 440, 443, 447,
    450, 453, 456, 459, 462, 464, 467, 470, 472, 475, 477, 480,
    482, 484, 486, 488, 490, 492, 494, 495, 497, 499, 500, 501,
    503, 504, 505, 506, 507, 508, 509, 509, 510, 510, 511, 511,
    512, 512, 512, 512, 512, 512, 512, 511, 511, 510, 510, 509,
    509, 508, 507, 506, 505, 504, 503, 501, 500, 499, 497, 495,
    494, 492, 490, 488, 486, 484, 482, 480, 477, 475, 472, 470,
    467, 464, 462, 459, 456, 453, 450, 447, 443, 440, 437, 433,
    430, 426, 423, 419, 415, 411, 407, 403, 399, 395, 391, 387,
    382, 378, 373, 369, 364, 360, 355, 350, 346, 341, 336, 331,
    326, 321, 316, 310, 305, 300, 295, 289, 284, 278, 273, 267,
    262, 256, 250, 245, 239, 233, 227, 221, 215, 209, 203, 197,
    191, 185, 179, 173, 167, 161, 154, 148, 142, 136, 129, 123,
    117, 110, 104,  97,  91,  85,  78,  72,  65,  59,  52,  46,
     39,  33,  26,  20,  13,   7,   0
};

//PWM表元素数量
uint16_t POINT_NUM = 247; 

static void TIMx_GPIO_Config(void) 
{
  GPIO_InitTypeDef GPIO_InitStructure;

  /*GPIO时钟使能*/
  RCC_APB2PeriphClockCmd(BRE_TIM_GPIO_CLK, ENABLE); 

  /*IO设置*/
    BRE_GPIO_REMAP_FUN();
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;// 复用推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  
    /* 配置LED灯用到的引脚 */
    //红
  GPIO_InitStructure.GPIO_Pin =  BRE_RED_TIM_LED_PIN ;  
  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);
}

static void NVIC_Config_PWM(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* Configure one bit for preemption priority */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* 配置TIM3_IRQ中断为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = BRE_TIMx_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

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 = (4-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时为低电平

  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重载寄存器ARR
  TIM_ARRPreloadConfig(BRE_TIMx, ENABLE);
    //使能定时器
  TIM_Cmd(BRE_TIMx, ENABLE);
    //使能update中断
    TIM_ITConfig(BRE_TIMx, TIM_IT_Update, ENABLE);
    //配置中断优先级
    NVIC_Config_PWM();
}

void TIMx_Breathing_Init(void)
{
    TIMx_GPIO_Config();
    TIMx_Mode_Config(); 
}

bsp_spwm.h

#ifndef __SPWM_H
#define	__SPWM_H

#include "stm32f10x.h"

/*电压幅值等级数*/
#define AMPLITUDE_CLASS 256

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

/*PWM表中的点数*/
extern uint16_t POINT_NUM;
/********************定时器通道**************************/

#define BRE_TIMx TIM3

#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

#define BRE_TIMx_IRQn TIM3_IRQn //中断
#define BRE_TIMx_IRQHandler TIM3_IRQHandler

void TIMx_Breathing_Init(void);

#endif /* __SPWM_H */

bsp_TiMbase.c

#include "bsp_TiMbase.h" 

// 中断优先级配置
static void BASIC_TIM_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure; 
    //设置中断组为0
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);     
        //设置中断来源
    NVIC_InitStructure.NVIC_IRQChannel = BASIC_TIM_IRQ ;    
        //设置主优先级为0
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;    
      //设置抢占优先级为3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

static void BASIC_TIM_Mode_Config(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        // 开启定时器时钟,即内部时钟CK_INT=72M
    BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, ENABLE);
        // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
    TIM_TimeBaseStructure.TIM_Period = BASIC_TIM_Period;    
      // 时钟预分频数为
    TIM_TimeBaseStructure.TIM_Prescaler = BASIC_TIM_Prescaler;
        //向上计数
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
      // 初始化定时器
    TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure);
        // 清除计数器中断标志位
    TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update);
        // 开启计数器中断
    TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE);
        // 使能计数器
    TIM_Cmd(BASIC_TIM, ENABLE); 
}

void BASIC_TIM_Init(void)
{
    BASIC_TIM_NVIC_Config();
    BASIC_TIM_Mode_Config();
}

bsp_TiMbase.h

#ifndef __BSP_TIMEBASE_H
#define __BSP_TIMEBASE_H

#include "stm32f10x.h"

#define BASIC_TIM6

#ifdef  BASIC_TIM6
#define BASIC_TIM TIM6
#define BASIC_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define BASIC_TIM_CLK RCC_APB1Periph_TIM6
#define BASIC_TIM_Period 12960
#define BASIC_TIM_Prescaler 9999
#define BASIC_TIM_IRQ TIM6_IRQn
#define BASIC_TIM_IRQHandler TIM6_IRQHandler

#endif

void BASIC_TIM_Init(void);

#endif	/* __BSP_TIMEBASE_H */
#! python3
#coding=utf-8

"""
Python版本:3.x
外部库:matplotlib1.5.3、numpy1.11.2

运行方式:
在命令行中输入:python sinWave.py 

运行结果:
命令行中会打印计算得的各点数据,
在当前目录下会生成py_dac_sinWav.c文件,包含上述数据,
并且会弹出描绘曲线的对话框。
"""

import matplotlib.pyplot as plt 
import numpy as np
import math

#修改本变量可以更改点数,如16、32、64等
POINT_NUM = 247
#正弦函数放大倍数
MULTIPLE = 2**9

#正弦函数X取值范围
X_VALUE = math.pi

# POINT_NUM 个点
n = np.linspace(0,X_VALUE,POINT_NUM)

#计算POINT_NUM个点的正弦值
a = map(math.sin,n)

r =[]
for i in a:
    temp = round(i*MULTIPLE)
    #得到序列
    r.append( temp )
   

print(list(map(int,r)))

#写入序列到文件
with open("py_pwm_sinWave.c",'w',encoding= 'gb2312') as f:
    print(list(map(int,r)),file= f)

#绘图
plt.plot(n,r,"-o")
plt.show()
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
。首先需要了解呼吸灯的原理,呼吸灯即为PWM控制LED灯亮度的变化,通过不断调整LED灯的亮度来实现呼吸灯的效果。在本题中,需要产生4彩呼吸灯,即需要控制4个LED灯的PWM信号。 接下来,我们需要确定呼吸周期和CCR更新周期。呼吸周期为1.6s,即LED灯从最暗到最亮再到最暗的时间为1.6s,因此每个PWM周期需要持续0.8s。CCR更新周期为0.2s,即每0.2s需要更新一次CCR值,用于控制LED灯的亮度。 代码如下: ``` #include "stm32f10x.h" #define LED_RED GPIO_Pin_0 #define LED_GREEN GPIO_Pin_1 #define LED_BLUE GPIO_Pin_2 #define LED_WHITE GPIO_Pin_3 void delay(uint32_t time); void TIM2_IRQHandler(void); int main(void) { // 初始化LED引脚 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = LED_RED | LED_GREEN | LED_BLUE | LED_WHITE; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化定时器2 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); TIM_TimeBaseStructure.TIM_Period = 8000; // PWM周期为0.8s TIM_TimeBaseStructure.TIM_Prescaler = 7199; // 分频系数为72,定时器时钟为72MHz/72=1MHz TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 初始化定时器2通道1~4 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比为0 TIM_OC1Init(TIM2, &TIM_OCInitStructure); TIM_OC2Init(TIM2, &TIM_OCInitStructure); TIM_OC3Init(TIM2, &TIM_OCInitStructure); TIM_OC4Init(TIM2, &TIM_OCInitStructure); // 启动定时器2 TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); // 设置定时器2更新中断 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); while(1) { // do something } } // 延时函数 void delay(uint32_t time) { while(time--); } // 定时器2更新中断 void TIM2_IRQHandler(void) { static uint16_t cnt = 0; static uint16_t pwm_val_red = 0; static uint16_t pwm_val_green = 0; static uint16_t pwm_val_blue = 0; static uint16_t pwm_val_white = 0; if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { cnt++; if (cnt == 4) // 更新CCR值的周期为0.2s,因此每4个PWM周期更新一次CCR值 { cnt = 0; pwm_val_red = (pwm_val_red + 1) % 8000; pwm_val_green = (pwm_val_green + 2) % 8000; pwm_val_blue = (pwm_val_blue + 3) % 8000; pwm_val_white = (pwm_val_white + 4) % 8000; } TIM_SetCompare1(TIM2, pwm_val_red); TIM_SetCompare2(TIM2, pwm_val_green); TIM_SetCompare3(TIM2, pwm_val_blue); TIM_SetCompare4(TIM2, pwm_val_white); TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } ``` 在上述代码中,我们使用了定时器2产生PWM信号,并通过定时器2更新中断不断调整LED灯的亮度。其中,cnt变量用于计数CCR更新周期,pwm_val_x变量用于存储LED_x灯的CCR值。 需要注意的是,在每个CCR更新周期内,不同颜色的LED灯的CCR值需要分别更新,以实现4彩呼吸灯的效果。 在实际的应用中,还需要根据具体的硬件电路和LED灯的特性进行调整,以达到最佳的呼吸灯效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值