stm32f103呼吸灯(PWM脉冲宽度调制)

stm32f103呼吸灯(PWM脉冲宽度调制)

  • 一、PWM脉冲宽度调制
  • 二、硬件说明
  • 三、编程实现
    • 1、硬件相关宏定义
    • 2、GPIO初始化
    • 3、定义 PWM表
    • 4、定时器 PWM配置
    • 5、定时器中断服务函数
    • 6、计算拟合周期
  • 四、呼吸灯演示
    • 1、PWM输出波形
    • 2、呼吸灯

一、PWM脉冲宽度调制

1、使用脉冲占空比拟合不同波形的方式称为 PWM(脉冲宽度调制)控制技术——通过 对一系列脉冲的宽度进行调制,来等效地获得所需要波形(含形状和幅值)。PWM 控制 的基本原理为:冲量相等而开头不同的窄脉冲加在具有惯性的环节上时,其效果基本 相同。其中冲量指窄脉冲的面积;效果相同指环节输出响应波形基本相同。 例如:可以用一系列等幅不用一系列等幅不等宽的脉冲来代替一个正弦半波,见图
stm32f103呼吸灯(PWM脉冲宽度调制)_第1张图片
要改变等效输出正弦波幅值,按同一比例改变各脉冲宽度即可。 若把拟合的波形改成呼吸特性曲线,即可得到控制呼吸灯使用的 PWM 波形,要生成 拟合的 PWM波形,通常使用计算法和调制法,本文中使用计算法:根据拟合波形的频率、幅值和半周期脉冲数,准确计算 PWM 波各脉冲宽度和
间隔,据此控制开关器件的通断,就可得到所需 PWM 波形。在下边编程实现中会详细说明。
2、要改变PWM输出波形的宽度,就要改变比较寄存器 CCRx 的值,想要输出不通宽度来拟合正弦波,则需要CCRx的值呈现如下图的变化趋势,即要生成一张CCRx的数值表,按周期变化将表中元素的值赋给CCRx。
stm32f103呼吸灯(PWM脉冲宽度调制)_第2张图片

二、硬件说明

本文所使用硬件为野火的stm32f103指南者开发板,LED使用PB5引脚控制的红色LED

三、编程实现

利用野火提供的呼吸灯例程说明
资料
提取码:i2u1

1、硬件相关宏定义

bsp_breathing.h 文件
stm32f103呼吸灯(PWM脉冲宽度调制)_第3张图片
stm32f103呼吸灯(PWM脉冲宽度调制)_第4张图片
stm32f103呼吸灯(PWM脉冲宽度调制)_第5张图片
定义了三组LED的宏,通过修改代码中的 #define LIGHT_COLOR RED_LIGHT语句,可以切换使用红、绿、蓝三种颜色的呼吸灯。 在每组宏定义中,定义了定时器编号定时器时钟使能、红灯中PB5引脚重映射操作GPIO 端口引脚号通道对应的比较寄存器名以及中断通道中断服务函数名。 定时器的比较寄存器 CCRx 在控制呼吸灯的单个周期内需要切换为 PWM表中不同的数值,所以需要利用定时器中断。

2、GPIO初始化

bsp_breathing.c 文件
stm32f103呼吸灯(PWM脉冲宽度调制)_第6张图片
野火的库封装度比较高,所以使用的都是宏定义名,在上面的硬件配置中启用不同的宏,便会对应不同的管脚。其中由于红灯使用的引脚需要用到第二功能,本代码 使用宏 BRE_GPIO_REMAP_FUN ()进行了该引脚的功能重定义操作

3、定义 PWM表

PWM表则是一个周期内比较寄存器CCRx的变化值,即脉冲宽度的变化值。
bsp_breathing.c 文件
stm32f103呼吸灯(PWM脉冲宽度调制)_第7张图片
此表用以下python代码生成

#! python3
#coding=utf-8

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

运行结果:
命令行中会打印计算得的各点数据,
在当前目录下会生成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,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,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()

该 python脚本生成 PWM表数据的原理,实质是按照如下函数曲线进行采样:
若 0<= x <=10:
y = 2^x
若 10< x <=20:
y = 2^(20−)
所以PWM表的范围为0~1024stm32f103呼吸灯(PWM脉冲宽度调制)_第8张图片

4、定时器 PWM配置

bsp_breathing.c 文件
stm32f103呼吸灯(PWM脉冲宽度调制)_第9张图片
stm32f103呼吸灯(PWM脉冲宽度调制)_第10张图片
本配置主体与全彩 LED 灯实验中的类似,代码中初始化了控制 RGB 灯用的定时器, 它被配置为向上计数,PWM 通道输出也被配置成当计数器 CNT 的值小于输出比较寄存器CCRx的值时,PWM通道输出低电平,点亮LED灯。在函数的最后还使能了定时器中断,每当定时器的一个计数周期完成时,产生中断,配合中断服务函数,即可切换 CCRx 比较寄存器的值。
代码中的 TIM_PeriodTIM_Prescaler是关键配置。 其中 TIMPeriod 被配置为(1024-1),它控制定时器的定时周期,定时器的计数寄存器 CNT从 0开始,每个时钟会对计数器加 1,计数至 1023时完成一次计数,产生中断,也 就是说一共 1024 个计数周期,与 PWM 表元素中的最大值相同。若定时器的输出比较寄存器CCRx被赋值为PWM表中的元素,即可改变输出对应占空比的PWM波,控制LED灯, 如: 若CCRx=1,那么在CNTCCRx时,输出高电平,LED灯灭,此时IME(_) / TIME(_) = 1 / 1024 ;
根据PWM 表更新 CCRx 的值,即可输出占空比呈呼吸特性曲线变化的 PWM波形,达到呼吸灯的效果。 最终,拟合曲线的周期由 TIMPeriodPWM表的点数、TIM_Prescaler 以及下面中断服务函数的 period_cnt比较值共同决定,本工程需要调整这些参数使得拟合曲线的周期约为 3 秒,从而达到较平缓的呼吸效果。

5、定时器中断服务函数

stm32f10x_it.c 文件
stm32f103呼吸灯(PWM脉冲宽度调制)_第11张图片
在中断服务函数中,包含两个静态变量 period_cntpwm_index。 其中 pwm_index 比较容易理解,它用于指示当前要使用 PWM 表中的哪个元素,从而 在BRE_TIMx->BRE_CCRx = indexWave[pwm_index];语句中可以给 CCRx赋予正确的数值,而且当 PWM表中的数据都使用一遍时,pwm_index将重新指向 PWM表的开头,开始下一次呼吸循环。 在本例的单次呼吸循环中,每个PWM表元素都会使用10次,代码中利用period_cnt 变量指示当前使用的次数,当 period_cnt> period_class 时(即 period_cnt>10 时),pwm_index 才会指向下一个元素。每个 PWM 表元素使用多次,主要是为了在 TIMPeriod、PWM 表的 点数、TIM_Prescaler 都固定的情况下,通过调整每个元素的重复次数可以调整整个拟合波 形的周期。如把代码中的比较值 period_class 改为 100,每个 PWM 表遍历一次的时间就变 为原来配置的 10 倍,其拟合的呼吸周期也就相应地改变了。

period_classbsp_breathing.c 文件中定义
stm32f103呼吸灯(PWM脉冲宽度调制)_第12张图片

6、计算拟合周期

TIMPeriod、PWM 表的点数、TIM_Prescaler 以及 period_cnt 都会影响到 拟合曲线的周期,而在实际应用中又有如下要求:

  • TIMPeriod:定时器的计数周期,它的值必须与 PWM 表中的极大值相等(应用中 赋值需要减1),而PWM表的极大值决定了控制的分辨率。例如极大值为10时, PWM 占空比只有 10 个等级,精确到 0.1,当极大值为 1000 时,PWM 占空比有 1000个等级,精确到 0.001。
  • TIM_Prescaler:定时器时钟分频因子,它控制定时器计数器 CNT 计数加 1 所需要 的时间,它的值太大会导致输出的单个 PWM 波周期过长,影响控制的动态特性。 如控制LED灯时,该值太大会导致LED灯开关时间变长,闪烁明显。一般来说, 该值越小越好。
  • PWM 表的点数:PWM 表的点数即对拟合曲线的采样点数,采样点越多,能更好 地还原拟合曲线,采样点太少,可能会导致失真
  • period_class:周期倍数,即 PWM 表中每个元素的循环次数,它影响拟合曲线的 周期。当 period_class=1时,可以输出本配置中周期最短的拟合曲线。

周期计算公式如下
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
最终,遍历 PWM表的周期,即拟合曲线的周期:
T_PWM = T_Point * POINT_NUM
本例周期计算
PWM点数: POINT_NUM = 110

周期倍数: PERIOD_CLASS = 10

定时器定时周期: TIMER_TIM_Period = 1024

定时器分频: TIMER_TIM_Prescaler = 200

代入公式,计算得 T_PWM = 3.128 秒

  • 拟合周期计算也可用以下python代码计算
#! python3
#coding=utf-8

"""
Python版本:3.x

计算不同配置下呼吸灯的周期

运行结果:
打印出当前配置中一个呼吸周期的时间
"""

#PWM点数
POINT_NUM = 110

#周期倍数
PERIOD_CLASS = 10

#幅值等级
AMPLITUDE_CLASS = 1

#定时器定时周期
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 * AMPLITUDE_CLASS

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

print ("呼吸周期:",T_Up_Down_Cycle)

四、呼吸灯演示

1、PWM输出波形

2、呼吸灯


内容参考自《零死角玩转STM32—F103指南者》

你可能感兴趣的:(stm32,stm32,调制与编码策略,arm,keil,mdk)