自学内容网 自学内容网

【STM32】定时器 —— 输出比较&PWM

使用的单片机机型为STM32F103C8T6

在这里插入图片描述

PWM

对于5V电路来说,输出只有高电平5V和低电平0V,控制LED灯就是点亮和熄灭,但如果想要控制其亮度呢?这就需要PWM

PWM

PWM (Pulse Width Modulation) 脉冲宽度调制

具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常用于电机控速等领域

拿LED灯举例
当输出高电平时,LED灯点亮;当输出低电平时,LED灯熄灭。但如果在一定周期内,比如1ms内,前100us高电平,后900us低电平,那么在人眼看来,LED灯就是很低的亮度。随着高电平所占时间增多,LED亮度就会增大

换成电机,由于惯性,从高电平转为低电平,电机并不会立刻停止,同样在较短时间内分配高低电平的比率就可以实现不同档位的电机


PWM参数
在这里插入图片描述

  • Ts:周期,如1ms
  • Ton:高电平时间,如400us
  • Toff:低电平时间,如600us

频率 = 1 / Ts = 1KHz
占空比:高电平时间占周期的比率。Ton / Ts = 40%
分辨率:1 / (计数器最大值 + 1)


输出比较

定时器输出比较模块如下
在这里插入图片描述

OC (Output Compare) 输出比较

通过比较 CNT 和 CCR 寄存器值的关系,对输出电平置1、置0 或翻转的操作,用于输出一定频率和占空比的 PWM 波形

通用定时器和高级定时器都拥有4个输出比较通道,如上图的 TIMx_CH1 ~ TIMx_CH4
高级定时器的前3个通道额外拥有死去生成和互补输出的功能

输出部分电路如下:

在这里插入图片描述

通过 CNT 和 CCR比较控制输出使能电路
例如:当CNT < CRR 时,输出高电平,CNT >= CRR 时输出低电平。
配合时基单元(参看【STM32】定时中断),即可输出PWM。

如定时器频率为1MHz,即每1微秒CNT计数加一,设置重装值ARR = 100,总周期为100us,比较器CRR = 40。
CNT计数前40us都输出高电平,后60us输出低电平。如此就输出了频率为10KHz,占空比为40%,分辨率为1%


输出比较模式

在这里插入图片描述

常用的为 PWM模式1 和 PWM模式2,二者效果相反,根据实际场景选择

编程实例

输出比较呼吸灯

  • 程序目标:通过定时器输出比较实现呼吸灯(亮度周期变化)
  • 程序原理:每20ms修改CRR值,输出不同PWM波形

接线图如下:

在这里插入图片描述

通过引脚定义得知,TIM2 的 CH1输出引脚为 A0
同时,将LED灯的正极接在输出引脚上,方便占空比的计算

在这里插入图片描述

PWM基本结构

在这里插入图片描述
PWM使用步骤:

  1. 开启相关外设时钟
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  1. 定时器使用内部时钟源72MHz
//时钟源使用内部时钟源,72MHz
TIM_InternalClockConfig(TIM2);
  1. 配置GPIO,注意引脚使用复用推挽输出
//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//注意使用复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
  1. 配置时基单元,频率为1KHz,分辨率为1%
//时基单元
TIM_TimeBaseInitTypeDef TIM_InitStructure;
TIM_TimeBaseStructInit(&TIM_InitStructure);
TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频
TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_InitStructure.TIM_Period = 100 - 1;//重装值
TIM_InitStructure.TIM_Prescaler = 720 - 1;//预分频
TIM_InitStructure.TIM_RepetitionCounter = 0;//重复计数,高级定时器才有
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
  1. 配置输出比较,通过修改CCR值改变占空比
//输入/比较器模块
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);
  1. 启动定时器
//开启时钟
TIM_Cmd(TIM2, ENABLE);

完整代码如下:

/**
  * @brief初始化PWM功能
  * @parm无
  * @retval无
  */
void PWM_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

//时钟源使用内部时钟源,72MHz
TIM_InternalClockConfig(TIM2);

////引脚重定义
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);//TIM2引脚重定向
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//关闭A15的调试接口,使其变成普通引脚使用

//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//注意使用复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

//时基单元
TIM_TimeBaseInitTypeDef TIM_InitStructure;
TIM_TimeBaseStructInit(&TIM_InitStructure);
TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频
TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_InitStructure.TIM_Period = 100 - 1;//重装值
TIM_InitStructure.TIM_Prescaler = 720 - 1;//预分频
TIM_InitStructure.TIM_RepetitionCounter = 0;//重复计数,高级定时器才有
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);

//输入/比较器模块
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);

//开启时钟
TIM_Cmd(TIM2, ENABLE);
}

同时提供修改CRR值的函数

/**
  * @brief设置比较值
  * @parmCompare:比较值  范围:0 ~ 255
  * @retval无
  */
void PWM_SetCompare(uint8_t Compare)
{
TIM_SetCompare1(TIM2, Compare);
}

主程序每隔20ms修改CRR值,实现LED灯由 熄灭 -> 亮度增大 -> 完全点亮 -> 亮度减小 -> 熄灭 的循环

int main()
{
PWM_Init();
while(1)
{
for(uint8_t i = 0; i < 100; ++i)
{
PWM_SetCompare(i);
Delay_ms(20);//延迟20ms
}
for(uint8_t i = 0; i < 100; ++i)
{
PWM_SetCompare(100 - i);
Delay_ms(20);//延迟20ms
}
}
}

完整程序链接:【STM32】输出比较PWM

舵机转向

舵机是一种根据输入PWM信号占空比来控制输出角度的装置
在这里插入图片描述

输入PWM信号要求:周期为20ms,高电平宽度为0.5ms ~ 2.5ms

在这里插入图片描述

硬件电路如下:
在这里插入图片描述

接线图如下:舵机需要5V驱动,正极接STLINK
在这里插入图片描述

PWM初始化大抵相同,需要注意 ARR、PSC 的设置

/**
  * @brief初始化PWM功能
  * @parm无
  * @retval无
  */
void PWM_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//注意使用复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

//内部时钟
TIM_InternalClockConfig(TIM2);

//时基单元
TIM_TimeBaseInitTypeDef TIM_InitStructure;
TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频
TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_InitStructure.TIM_Period = 20000 - 1;//重装值,计数周期
TIM_InitStructure.TIM_Prescaler = 72 - 1;//预分频
TIM_InitStructure.TIM_RepetitionCounter = 0;//重复计数,高级定时器才有
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);

//输入/比较器模块
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_OC2Init(TIM2, &TIM_OCInitStructure);

//开启时钟
TIM_Cmd(TIM2, ENABLE);
}

此处设置 PSC = 72,ARR = 20000
频率为50Hz,即周期为20ms

-90° 为0.5ms,对应CRR为500,-45°对应为1000…

/**
  * @brief设置舵机角度  -90为0.5ms,对应CRR为500,-45°对应为1000.....
  * @parm角度   范围:0 ~ 180
  * @retval无
  */
void Servos_SetAngle(float Angle)
{
PWM_SetCompare(Angle / 180 * 2000 + 500);
}

完整程序链接:

PWM控制直流电机

直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转
直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作
TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向

在这里插入图片描述

硬件电路

在这里插入图片描述

PWMA 和 PWMB 接收PWM波形
AIN1、AIN2 和 BIN1、BIN2控制电机正反转
定义AIN1高电平,AIN2低电平为正转,则AIN1低电平,AIN2高电平为反转

接线图如下

在这里插入图片描述

PWM配置

/**
  * @brief初始化PWM功能
  * @parm无
  * @retval无
  */
void PWM_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//注意使用复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

//内部时钟
TIM_InternalClockConfig(TIM2);

//时基单元
TIM_TimeBaseInitTypeDef TIM_InitStructure;
TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频
TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_InitStructure.TIM_Period = 100 - 1;//重装值,计数周期
TIM_InitStructure.TIM_Prescaler = 36 - 1;//预分频
TIM_InitStructure.TIM_RepetitionCounter = 0;//重复计数,高级定时器才有
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);

//输入/比较器模块
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_OC3Init(TIM2, &TIM_OCInitStructure);

//开启时钟
TIM_Cmd(TIM2, ENABLE);
}

还需要配置 A4 和 A5引脚控制电机正反转

/**
  * @brief舵机初始化
  * @parm无
  * @retval无
  */
void Motor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//初始化输出高低电平的引脚
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
PWM_Init();
}

/**
  * @brief设置舵机角度  0为500,45为1000,90为1500....
  * @parm角度   范围:0 ~ 180
  * @retval无
  */
void Motor_SetSpeed(int16_t Speed)
{
if(Speed >= 0)//正转
{
//设置GPIO引脚高低电平
GPIO_SetBits(GPIOA, GPIO_Pin_4);
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
//设置占空比,输出PWM波形
PWM_SetCompare(Speed);
}
else//反转
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
GPIO_SetBits(GPIOA, GPIO_Pin_5);
PWM_SetCompare(-Speed);
}
}

完整程序链接:


以上就是本篇博客的所有内容,感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述


原文地址:https://blog.csdn.net/m0_72563041/article/details/144156838

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!