赞
踩
本文记录的是单色呼吸灯教程
开发板:野火指南者stm32f103vet6
目的:实现单色呼吸灯1S渐亮,5S常亮,1S渐灭。
原理:利用PWM输出,改变占空比,也就改变了平均电压。形成一个“呼吸”的效果。
PWM原理:
TTL电平中,高电平为5V,低电平为0V,但是我们想要输出不同的模拟电压,比如输出3.75V应该怎么操作?
这里就要用到PWM,通过改变IO引脚输出方波的占空比,从而得到不同的模拟电压值,理论上来讲,可以输出任意不大于最大电压值(即0~5V之间任意大小)的模拟电压。
模拟电压=最大电压*占空比,这里的模拟电压是平均值,占空比越大,则模拟电压也越大。
占空比:一个周期内,高电平所占的时间除以总时间
代码如下(示例):
#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**********************/
代码如下(示例):
#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 */
#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**********************/
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); //必须要清除中断标志位 } }
#! 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()
也可以用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;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。