stm32呼吸灯

发布于:2023-07-04 ⋅ 阅读:(113) ⋅ 点赞:(0)


前言

本文记录的是单色呼吸灯教程
开发板:野火指南者stm32f103vet6
目的:实现单色呼吸灯1S渐亮,5S常亮,1S渐灭。
原理:利用PWM输出,改变占空比,也就改变了平均电压。形成一个“呼吸”的效果。


一、前期准备

PWM原理:
TTL电平中,高电平为5V,低电平为0V,但是我们想要输出不同的模拟电压,比如输出3.75V应该怎么操作?
这里就要用到PWM,通过改变IO引脚输出方波的占空比,从而得到不同的模拟电压值,理论上来讲,可以输出任意不大于最大电压值(即0~5V之间任意大小)的模拟电压。
在这里插入图片描述
模拟电压=最大电压*占空比,这里的模拟电压是平均值,占空比越大,则模拟电压也越大。
占空比:一个周期内,高电平所占的时间除以总时间

在这里插入图片描述

二、硬件原理图

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、代码

1.main.c

代码如下(示例):


#include "stm32f10x.h"
#include "./breathinglight/bsp_breathing.h"
#include "bsp_led.h"
#include "bsp_systick.h"
#include "bsp_usart.h"

extern volatile uint16_t temp;


int main(void)
{			
	/* 初始化呼吸灯 */
	TIMx_Breathing_Init();
	
	//初始化串口
	USART_Config();
	while(1)
	{
		if(temp == 605) {
			TIM_ITConfig(BRE_TIMx, TIM_IT_Update, DISABLE);
			SysTick_Delay_ms(5000);
			TIM_ITConfig(BRE_TIMx, TIM_IT_Update, ENABLE);
		}
		if(temp == 1210) {
			TIM_ITConfig(BRE_TIMx, TIM_IT_Update, DISABLE);
			temp = 0;
		}
	}		
}



/*********************************************END OF FILE**********************/



2.bsp_breathing.h

代码如下(示例):

#ifndef __PWM_BREATHING_H
#define	__PWM_BREATHING_H

#include "stm32f10x.h"

/*PWM表中的点数*/
extern uint16_t  POINT_NUM	;
//控制输出波形的频率
extern __IO uint16_t period_class ;


#define RED_LIGHT 		1
#define GREEN_LIGHT 	2
#define BLUE_LIGHT		3

/*要使用什么颜色的呼吸灯,可选RED_LIGHT、GREEN_LIGHT、BLUE_LIGHT*/
#define LIGHT_COLOR 	RED_LIGHT

/********************定时器通道**************************/
#if  LIGHT_COLOR == RED_LIGHT
/************红灯***************/
	#define   BRE_TIMx                      TIM3

	#define   BRE_TIM_APBxClock_FUN        RCC_APB1PeriphClockCmd
	#define   BRE_TIM_CLK                   RCC_APB1Periph_TIM3
	#define   BRE_TIM_GPIO_APBxClock_FUN   RCC_APB2PeriphClockCmd
	#define   BRE_TIM_GPIO_CLK              (RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO)

	//红灯的引脚需要重映射
	#define   BRE_GPIO_REMAP_FUN()						GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); 				

	#define  BRE_TIM_LED_PORT               GPIOB
	#define  BRE_TIM_LED_PIN                GPIO_Pin_5

	#define  BRE_TIM_OCxInit                TIM_OC2Init            //通道选择,1~4
	#define  BRE_TIM_OCxPreloadConfig       TIM_OC2PreloadConfig 
	#define  BRE_CCRx                       CCR2

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

#elif LIGHT_COLOR == GREEN_LIGHT
/************绿灯***************/
	#define   BRE_TIMx                      TIM3

	#define   BRE_TIM_APBxClock_FUN        RCC_APB1PeriphClockCmd
	#define   BRE_TIM_CLK                   RCC_APB1Periph_TIM3
	#define   BRE_TIM_GPIO_APBxClock_FUN   RCC_APB2PeriphClockCmd
	#define   BRE_TIM_GPIO_CLK              (RCC_APB2Periph_GPIOB)

	//绿灯不需要重映射
	#define   BRE_GPIO_REMAP_FUN()				


	#define  BRE_TIM_LED_PORT               GPIOB
	#define  BRE_TIM_LED_PIN                GPIO_Pin_0

	#define  BRE_TIM_OCxInit                TIM_OC3Init            //通道选择,1~4
	#define  BRE_TIM_OCxPreloadConfig       TIM_OC3PreloadConfig 
	#define  BRE_CCRx                       CCR3

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

#elif LIGHT_COLOR == BLUE_LIGHT
/************蓝灯***************/
	#define   BRE_TIMx                      TIM3

	#define   BRE_TIM_APBxClock_FUN        RCC_APB1PeriphClockCmd
	#define   BRE_TIM_CLK                   RCC_APB1Periph_TIM3
	#define   BRE_TIM_GPIO_APBxClock_FUN   RCC_APB2PeriphClockCmd
	#define   BRE_TIM_GPIO_CLK              (RCC_APB2Periph_GPIOB)

	//蓝灯不需要重映射
	#define   BRE_GPIO_REMAP_FUN()	

	#define   BRE_TIM_LED_PORT             GPIOB
	#define   BRE_TIM_LED_PIN              GPIO_Pin_1

	#define   BRE_TIM_OCxInit              TIM_OC4Init            //通道选择,1~4
	#define   BRE_TIM_OCxPreloadConfig    TIM_OC4PreloadConfig 
	#define   BRE_CCRx                      CCR4

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

#endif



void      TIMx_Breathing_Init          (void);



#endif /* __PWM_BREATHING_H */

3.bsp_breathing.c



#include "./breathinglight/bsp_breathing.h"


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

/* LED亮度等级 PWM表,指数曲线 ,此表使用工程目录下的python脚本index_wave.py生成*/
uint16_t indexWave[] = {
0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4,
4, 5, 5, 6, 7, 8, 9, 10, 11, 13,
15, 17, 19, 22, 25, 28, 32, 36,
41, 47, 53, 61, 69, 79, 89, 102,
116, 131, 149, 170, 193, 219, 250,
284, 323, 367, 417, 474, 539, 613,
697, 792, 901, 1024, 1024, 901, 792,
697, 613, 539, 474, 417, 367, 323,
284, 250, 219, 193, 170, 149, 131, 
116, 102, 89, 79, 69, 61, 53, 47, 41,
36, 32, 28, 25, 22, 19, 17, 15, 13, 
11, 10, 9, 8, 7, 6, 5, 5, 4, 4, 3, 3,
2, 2, 2, 2, 1, 1, 1, 0
	
};

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


 /**
  * @brief  配置TIM复用输出PWM时用到的I/O
  * @param  无
  * @retval 无
  */
static void TIMx_GPIO_Config(void) 
{
  GPIO_InitTypeDef GPIO_InitStructure;

  /*  clock enable */
  RCC_APB2PeriphClockCmd(BRE_TIM_GPIO_CLK, ENABLE); 
  BRE_TIM_GPIO_APBxClock_FUN  ( BRE_TIM_GPIO_CLK, ENABLE );
		
	BRE_GPIO_REMAP_FUN();  

  /* 配置呼吸灯用到的引脚 */
  GPIO_InitStructure.GPIO_Pin =  BRE_TIM_LED_PIN ;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	// 复用推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init( BRE_TIM_LED_PORT, &GPIO_InitStructure );
}


/**
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  无
  * @retval 无
  */
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);
}

/**
  * @brief  配置TIM输出的PWM信号的模式,如周期、极性
  * @param  无
  * @retval 无
  */

static void TIMx_Mode_Config(void)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;																				
	
	
	/* 设置TIM3CLK 时钟 */
	BRE_TIM_APBxClock_FUN ( BRE_TIM_CLK, ENABLE ); 
	
	/* 基本定时器配置 ,配合PWM表点数、中断服务函数中的period_cnt循环次数设置*/	
	
	/* 设置使得整个呼吸过程为3秒左右即可达到很好的效果 */	
	
	//要求:
	//TIM_Period:与PWM表中数值范围一致
	//TIM_Prescaler:越小越好,可减轻闪烁现象
	//PERIOD_CLASS:中断服务函数中控制单个点循环的次数,调整它可控制拟合曲线的周期
	//POINT_NUM:PWM表的元素,它是PWM拟合曲线的采样点数

	/*************本实验中的配置***************/	
	/***********************************************
	#python计算脚本	count.py
	#PWM点数
	POINT_NUM = 110

	#周期倍数
	PERIOD_CLASS = 10

	#定时器定时周期
	TIMER_TIM_Period = 2**10
	#定时器分频
	TIMER_TIM_Prescaler = 200

	#STM32系统时钟频率和周期
	f_pclk = 72000000
	t_pclk = 1/f_pclk

	#定时器update事件周期
	t_timer = t_pclk*TIMER_TIM_Prescaler*TIMER_TIM_Period

	#每个PWM点的时间
	T_Point = t_timer * PERIOD_CLASS

	#整个呼吸周期
	T_Up_Down_Cycle = T_Point * POINT_NUM

	print ("呼吸周期:",T_Up_Down_Cycle)
	
	#运行结果:
	
	呼吸周期:3.12888
	************************************************************/

  /* 基本定时器配置 */		  
  TIM_TimeBaseStructure.TIM_Period = (1024-1);;       							  //当定时器从0计数到 TIM_Period+1 ,为一个定时周期
  TIM_TimeBaseStructure.TIM_Prescaler = (128-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;  	  //当定时器计数值小于CCR1_Val时为低电平

  BRE_TIM_OCxInit ( BRE_TIMx, &TIM_OCInitStructure );	 									//使能通道
	

  BRE_TIM_OCxPreloadConfig ( BRE_TIMx, TIM_OCPreload_Enable );						//使能预装载	

  TIM_ARRPreloadConfig(BRE_TIMx, DISABLE);			 										//使能TIM重载寄存器ARR

  /* TIM3 enable counter */
  TIM_Cmd(BRE_TIMx, ENABLE);                   										//使能定时器	
	
	TIM_ITConfig(BRE_TIMx, TIM_IT_Update, ENABLE);										//使能update中断
		
	NVIC_Config_PWM();																					//配置中断优先级		
	
}

/**
  * @brief  TIM 呼吸灯初始化
  *         配置PWM模式和GPIO
  * @param  无
  * @retval 无
  */
void TIMx_Breathing_Init(void)
{
	TIMx_GPIO_Config();
	TIMx_Mode_Config();	
}

/*********************************************END OF FILE**********************/


4.中断服务函数

extern uint16_t indexWave[];

/* 呼吸灯中断服务函数 */
void BRE_TIMx_IRQHandler(void)
{	
	static uint16_t pwm_index = 0;			//用于PWM查表
	static uint16_t period_cnt = 0;		//用于计算周期数
	if (TIM_GetITStatus(BRE_TIMx, TIM_IT_Update) != RESET)	//TIM_IT_Update
 	{			
			period_cnt++;
			temp++;
			BRE_TIMx->BRE_CCRx = indexWave[pwm_index];	//根据PWM表修改定时器的比较寄存器值

			//每个PWM表中的每个元素使用period_class次
			if(period_cnt > period_class)				 				
			{				

				pwm_index++;												//标志PWM表指向下一个元素
				

				//若PWM表已到达结尾,重新指向表头
				if( pwm_index >=  POINT_NUM)			
				{
					pwm_index = 0;								
				}
				
				period_cnt=0;											//重置周期计数标志
			}	
			else
			{
			}	

		TIM_ClearITPendingBit (BRE_TIMx, TIM_IT_Update);	//必须要清除中断标志位
	}
}

5.占空比数组用脚本实现

1.python脚本

#! python3
#coding=utf-8

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

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

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

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

#修改本变量可以更改点数,如16、32、64等
POINT_NUM = 110

#指数曲线最大为2的MAX_POWER次方
MAX_POWER = 10


# POINT_NUM 个点
x1 = np.linspace(0, MAX_POWER, int(POINT_NUM / 2))

#f = 2^(x)
up =[]
for i in x1:
    temp = round(2**i)
    #得到升序列
    up.append( temp )
   


x2 = np.linspace(MAX_POWER, 2 * MAX_POWER, int(POINT_NUM / 2))

#f = 2^(2*MAX_POWER-x)
down=[]
for i in x2:
    temp = round(2**(MAX_POWER*2-i))

    #得到降序列
    down.append( temp )




line = list(x1)+list(x2)
val = list(up)+list(down)


print(line)
print("*"*80)
print(list(map(int,val)))

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

#绘图
plt.plot(line,val,"-o")
plt.show()




在这里插入图片描述

1.C脚本

也可以用C语言来生成这个数组

#include <stdio.h>
#include <string.h>
#include <math.h>

#define		POINT_NUM			110
#define		MAX_POWER			10

#define		HALF_POINT_NUM		(POINT_NUM / 2)


void Get_Index_Array(int* indexWave);

int main(void) {
	int i;
	int indexWave[POINT_NUM+1] = {0};
	Get_Index_Array(indexWave);
	
		/* printf arr */
	for(i = 0; i < POINT_NUM; i++) {
		printf("%3d  ", indexWave[i]);
		if( (i & 15) == 15) {
			printf("\n\r");
		}
	}

	return 0;
}


void Get_Index_Array(int* indexWave) {
	int i, j, temp;
	double aver, sum = 0;
	aver = double(MAX_POWER) / (HALF_POINT_NUM - 1);

	double arr1[HALF_POINT_NUM + 1] = {0};
	double arr2[HALF_POINT_NUM + 1] = {0};
	
	
	for(i = 0; i < HALF_POINT_NUM; i++) {
		arr1[i] = sum;							/* get the  Arithmetic progression*/
		sum += aver;
		arr1[i] = int(pow(2.0, arr1[i]) + 0.5);	/* change into index array */
	}	
		
	j = HALF_POINT_NUM;	
	for(i = 0; i < HALF_POINT_NUM; i++) {
		arr2[i] = arr1[j-1];
		--j;
	}
	
	j = 0;
	for(i = 0; i < POINT_NUM; i++) {
		if(i < HALF_POINT_NUM) {
			indexWave[i] = int(arr1[i]);	
		} else {
			indexWave[i] = int(arr2[j]);;	
			++j;
		}
	}	

	
	return ;
}

结果如图所示:
在这里插入图片描述

四、时间计算

①定时器时钟频率:timer_clk = 72M / psc (psc是预分频系数
②计数器+1所需时间:timer_time = 1 / timer_clk
③定时器定时溢出的时间(溢出后会发生中断或事件):t_timer = psc * period / 72M (period是定时器自动装寄存器的值
④每个PWM点的时间:T_POINT = t_timer * PERIOD_CLASS
⑤T_PWM = T_POINT * PIONT_NUM;

本文含有隐藏内容,请 开通VIP 后查看