一、实验目的
- 了解呼吸灯的原理
- 了解正弦脉宽调制(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()