第四节:STM32定时器(2.输出PWM波控制电机转速)

本文详细介绍了STM32定时器的工作原理,特别是输出比较功能,以及如何利用PWM波实现灯光控制(如呼吸灯)、电机转速调节和舵机操作。通过实例演示了如何使用STM32的通用定时器和TB6612电机驱动模块来控制电机速度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 关联:STM32总结超全笔记【秋招自用】

 四.STM32定时器(重中之重中之重)

【前言】

我觉得在冗长的理论之前,我们首先要对定时器的输出比较功能做一个理解。

【问】输出比较用来做什么?应用场景是什么??

输出比较,就是可以控制定时器,去比较 CNT 和 CCR 寄存器值的关系,来对输出的电平进⾏置1、置0或翻转的操作,⽤于输出⼀定频率和占空比的 PWM 波形。

【问】什么是PWM波?

PWM全称为脉冲宽度调制。

PWM波输出原理:

当 CNT<CCRx 时, IO 输出低电平(逻辑 0 );
当 CNT>=CCRx 时, IO 输出高电平(逻辑 1 );
当 CNT=ARR 时,定时器溢出, CNT 的值被清零,然后继续递增,依次循环;

占空比:高电平所占时间 / 周期时间
占空比 = CCR / (ARR + 1)

频率 =  CK_PSC / (PSC + 1) / (ARR + 1)

【PWM的应用场景】

【问】我们已经知道了输出比较,就是通过cnt和arr的比较,输出高低电平,输出PWM波,那么PWM波有什么用???

1.PWM输出呼吸灯

之前学完了最简单的基本定时器,我们来学习通用定时器。

首先人眼对80Hz以上的刷新频率是感受不到闪烁的。

我们来分析一下比如说:

  • 频率100Hz的场景:10毫秒内,5毫秒打开,5毫秒关闭,(占空比50%)此时常量。
  • 频率1Hz的场景:1秒内,0.5秒打开,0.5秒关闭,此时很明显的闪烁。
  • 频率100Hz的场景:10毫秒内,9毫秒打开,1毫秒关闭,(占空比90%)此时亮度比第一个例子亮。

所以:频率相同,占空比越大,灯越亮。

改变占空比,即可改变灯的亮度,实现呼吸灯。

2.PWM控制电机转速

直流电机中,电机转速是周期内输出的平均电压值,那么占空比越大,输出电压越大,速度越大。

改变占空比,即可改变电机转速。

3.PWM控制舵机

舵机频率一般为50Hz,相当于20毫秒的脉冲,脉冲的高电平部分范围是0.5ms到2.5ms之间。如果是180度的舵机,那么:

0.5ms对应0度

1ms对应45度

以此类推,2.5ms对应180度

---------------------------------------------------------------------------------------------------------------------------------

有了这些知识铺垫后,我们开始定时器输出比较的学习。

之前学完了最简单的基本定时器,我们来学习通用定时器。

【通用定时器】

 我们着重看一下输出比较这部分框图:

【输出比较】

说白了就是先初始化定时器模块和输出比较模块,然后通过控制CCR的值,改变灯的亮度或者是电机的速度。

那么例程就写一个控制直流电机转速,为后面的STM32小车做铺垫。

【例程6】输出PWM波控制电机转速

首先看一下硬件连接:

因为直流电机属于大功率器件,单片机引脚输出电压太小无法直接驱动,所以需要一个电机驱动模块,这里用TB6612。

【PWM初始化】

void PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*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);							//将PA2引脚初始化为复用推挽输出	
																	//受外设控制的引脚,均需要配置为复用模式
	
	/*时基单元初始化*/
	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 = 36 - 1;               //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
	
	/*输出比较初始化*/ 
	TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量                                                                                           
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;               //输出比较模式,选择PWM模式1
	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_OC3Init,配置TIM2的输出比较通道3
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

【问】为什么PA2被设置为了复用推挽输出?

因为PA2接的是PWMA,由内部外设控制的时候,需要设置为复用推挽输出。

后面都是比较基础的设置了。

【电机驱动模块的GPIO初始化】

void Motor_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟
	
	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();													//初始化直流电机的底层PWM
}

【设置电机速度的函数】

void PWM_SetCompare3(uint16_t Compare)
{
	TIM_SetCompare3(TIM2, Compare);		//设置CCR3的值
}

这里是对TIM_SetCompare3做一个封装。

void Motor_SetSpeed(int8_t Speed)
{
	if (Speed >= 0)							//如果设置正转的速度值
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_4);	//PA4置高电平
		GPIO_ResetBits(GPIOA, GPIO_Pin_5);	//PA5置低电平,设置方向为正转
		PWM_SetCompare3(Speed);				//PWM设置为速度值
	}
	else									//否则,即设置反转的速度值
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_4);	//PA4置低电平
		GPIO_SetBits(GPIOA, GPIO_Pin_5);	//PA5置高电平,设置方向为反转
		PWM_SetCompare3(-Speed);			//PWM设置为负的速度值,因为此时速度值为负数,而PWM只能给正数
	}
}

此函数就是根据TB6612的数据手册编写的(所以查Datasheet非常重要)

 此时在主函数中利用封装好的Motor_SetSpeed函数即可控制电机速度了。

### 使用 STM32 PWM 控制直流电机转速测量与调试 #### 硬件连接 为了实现基于STM32单片机PWM电机调速系统,需采用L298N电机驱动器来控制直流电机转速,并通过OLED显示屏实时显示当前转速和设定转速[^1]。具体硬件连接如下: - 将STM32定时器通道引脚配置为PWM输出模式并与L298N模块的输入端相连; - L298N模块电源正极接电池正极,负极接地;IN1、IN2分别接到STM32相应GPIO口用于方向控制; - OUT1/OUT2连接到直流电机两端。 ```c // 配置TIM3_CH1作为PWM输出 __HAL_RCC_TIM3_CLK_ENABLE(); htim3.Instance = TIM3; htim3.Init.Prescaler = 79; // 设置预分频值,假设APB1=80MHz,则频率为1KHz htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 999; // 周期设置为1ms if (HAL_TIM_PWM_Init(&htim3) != HAL_OK){ Error_Handler(); } ``` #### 软件编程 编写程序时需要注意`SetPWM()`函数并非简单地将CV值视为绝对PWM占空比。如果这样做,在某些情况下可能会导致控制系统失效,因为不断减小的误差会让输出趋近于零,而电机运行需要保持一定水平以上的PWM信号强度才能正常工作[^2]。因此应合理调整PID参数并确保最小PWM宽度大于启动阈值。 ```c void SetPWM(uint16_t duty_cycle){ __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,duty_cycle); } float pid_output(float setpoint,float process_value){ static float last_error = 0.0f; static float integral = 0.0f; float error = setpoint - process_value; // 计算积分项累加 integral += Ki * error; // PID计算公式 float output = Kp*error + integral + Kd*(error-last_error); // 更新上次误差 last_error = error; return constrain(output,min_pwm,max_pwm); // 输出范围限定 } ``` #### 实验测试 完成上述步骤之后就可以进入实际操作阶段了。先给定一个期望速度值,利用编码器反馈回来的实际转动情况去调节PWM形的比例关系直到两者趋于一致为止。期间可以借助示器观察形变化趋势以便更精准地微调各项系数直至达到理想效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值