定时器&PWM应用编程
深入了解STM32定时器原理,掌握脉宽调制pwm生成方法。一. 使用STM32F103的 Tim2~Tim5其一定时器的某一个通道pin(与GPIOx管脚复用,见下图),连接一个LED,用定时器计数方式,控制LED以2s的频率周期性地亮-灭。
二. 接上,采用定时器pwm模式,让 LED 以呼吸灯方式渐亮渐灭,周期为1~2秒,自己调整到一个满意效果。使用Keil虚拟示波器,观察 pwm输出波形。
三. 再接上,采用定时器的另外一个通道,编程采集上面的pwm输出信号,获得其周期和脉宽,并重定向输出到串口显示。
首先:定时器是一个非常关键的组件,用于产生精确的时间延迟或时间间隔。定时器可以通过编程来设置其计数值,当计数值达到预设值时,定时器会产生一个中断或信号,以触发相应的操作。
而,PWM(脉冲宽度调制)是一种常见的定时器应用,用于生成占空比可调的脉冲信号。通过调节定时器的计数值,可以控制脉冲信号的宽度,从而实现模拟信号的数字控制。在PWM应用中,定时器通常被配置为自动重装模式,当定时器溢出时,会自动将计数值重置为预设值,并重新开始计数。
定时器的工作原理可以分为四个部分:时钟产生部分、时基单元部分、输入捕获部分和输出比较部分。
- 时钟产生部分:这是定时器工作的基础,通常由一个振荡器和一个分频器组成。振荡器产生一个原始的时钟信号,然后分频器将这个原始时钟信号分频,得到所需的定时器时钟信号。
- 时基单元部分:这部分是定时器的核心,它负责产生定时器的计时基准时间。时基单元通常由一个计数器和一个预分频器组成。计数器对定时器时钟信号进行计数,而预分频器则对定时器时钟信号进行分频,以得到所需的计时基准时间。
- 输入捕获部分:这个部分用于捕获外部事件的发生时间。当外部事件发生时,输入捕获部分会立即停止当前正在计时的计数器,并记录下当前的时间值。这样,我们就可以知道外部事件发生的确切时间了。
- 输出比较部分:这个部分用于比较计数器的值和预置值。当计数器的值达到预置值时,输出比较部分会输出相应的信号,以触发外部设备或程序。输出比较部分通常由一个比较器和一组输出触发器组成。比较器比较计数器的值和预置值,当两者相等时,输出触发器会触发相应的动作或事件。
PWM(Pulse Width Modulation)是一种模拟控制方式,通过对一系列脉冲的宽度进行调制,来等效的获得所需要的波形(含形状和幅值)。脉宽调制(PWM,Pulse Width Modulation)是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。
PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用PWM进行编码。
PWM的一个优点是从处理器到被控系统信号都是数字形式的,无需进行数模转换。让信号保持为数字形式可将噪声影响降到最小。在接收端,通过适当的RC或LC网络可以滤除调制高频方波并将信号还原为模拟形式。
总之:通过PWM,改变IO口输出的方波的占空比从而获得使用数字信号模拟成的模拟电压信号。
用定时器控制小灯周期性闪烁
主要代码:
main.c文件:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint8_t i;
int main(void)
{
OLED_Init();
PWM_Init();
while (1)
{
PWM_SetCompare1(0);
Delay_ms(2000);
PWM_SetCompare1(100);
Delay_ms(2000);
}
}
PWM.c文件:
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启时钟
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);//给结构体赋初始值
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//设置输出比较的模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较极性
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出状态
TIM_OCInitStructure.TIM_Pulse = 0; //设置CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);//放入Init函数中
TIM_Cmd(TIM2, ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);
}
PWM.h文件:
#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);
#endif
电路连接
GND — GND
3v3 — 3v3
TXD — A10
RXD — A9
再连一根线将小灯和PA0口连接
通过串口烧录然后 将BOOT0置0,再次按下RESRT键,即可看见小灯周期性闪烁。
用PWM模式运行呼吸灯
在上面基础上对代码进行修改即添加循环,便能完成呼吸灯。
main.c文件:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint8_t i;
int main(void)
{
OLED_Init();
PWM_Init();
while (1)
{
for (i = 0; i <= 100; i++)
{
PWM_SetCompare1(i);
Delay_ms(10);
}
for (i = 0; i <= 100; i++)
{
PWM_SetCompare1(100 - i);
Delay_ms(10);
}
}
}
使用Keil的软件仿真逻辑分析仪功能观察串口输出波形:
点击“魔法棒”后在Debug下,勾选Use Simulator、Load Application at Startup、Run to main(),并将Dialog.DLL下改为DARMSTM.DLL,Parameter下改为-pSTM32F103RC:
可以看到占空比逐渐变大,LED逐渐变亮;占空比逐渐变小,LED逐渐变暗;