STM32输出PWM波控制舵机总结

STM32输出PWM波总结

前言

刚刚用STM32(F103ZET)点过灯我就开始了PWM控制舵机的学习。所有的问题都是从比较基础的环节开始的,在此总结一下我所遇到的问题,希望与大家交流交流。
有很多博文都讲述了关于PWM波的输出原理,所以我再次也就不再赘述。

正文


1.选用:PWM输出我使用的是定时器3,通道4,PB1端口。

…以下是相关定时器,通道和端口的映射:
------------------------TIM1_ETR PA12 PE7
------------------------TIM1_CH1 PA8 PE9
------------------------TIM1_CH2 PA9 PE11
------------------------TIM1_CH3 PA10 PE13
------------------------TIM1_CH4 PA11 PE14
------------------------TIM1_BKIN PB12 PA6 PE15
------------------------TIM1_CH1N PB13 PA7 PE8
------------------------TIM1_CH2N PB14 PB0 PE10
------------------------TIM1_CH3N PB15 PB1 PE12
------------------------TIM2_CH1_ETR PA0 PA15 PA0 PA15
------------------------TIM2_CH2 PA1 PB3 PA1 PB3
------------------------TIM2_CH3 PA2 PB10
------------------------TIM2_CH4 PA3 PB11
------------------------TIM3_CH1 PA6 PB4 PC6
------------------------TIM3_CH2 PA7 PB5 PC7
------------------------TIM3_CH3 PB0 PC8
TIM3_CH4 PB1 PC9
------------------------TIM4_CH1 PB6 PD12
------------------------TIM4_CH2 PB7 PD13
------------------------TIM4_CH3 PB8 PD14
------------------------TIM4_CH4 PB9 PD15
------------------------TIM5_CH4


2.代码1:PWM主程序:
名称:servomotor.c
变量:(TIM_TypeDef* timer,uint8_t tim_channe1,uint16_t freq,uint16_t duty)
//(定时器(TIMx),通道(1~4),定时重装值(2565),占空比(455~2565))
注意:虽然有一套关于PWM周期与占空比的计算方式,但是因为我们用的是HSI(高速内部RC振荡器 72MHz)所以有一定的误差,特别是误差累积起来后,计算出的数值与现实数值有很大差异。
计算方法:
频率:
F = 72M / ((定时器装入值+1)*(分频+1))(单位:Hz)
占空比:
W = TIM3->CCR1 / 定时器装入值(单位:%)
/*------------------------------------------------------------------------------------------
		仿真时添加端口格式:例如PC5 为 PORTC.5  。
		PWM占空比主要由寄存器 CCRx 控制,其值与 CNT 比较并输出。
--------------------------------------------------------------------------------------------*/

#include "servomotor.h"

void Pwm_Start(TIM_TypeDef* timer,uint8_t tim_channe1,uint16_t freq,uint16_t duty)//定时器,通道,定时重装值,占空比(455~2565)
{
//	u16 arr = 36000/freq;//设定自动重装寄存器周期的值,freq单位为KHz,所以直接用72000就可。
//	uint32_t arr;
	GPIO_InitTypeDef GPIO_InitStructure;   //声明一个结构体变量,用来初始化GPIO//1.声明结构体
	//时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPIOx的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 ,ENABLE);//使能TIM3的时钟
	//端口
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;// 锁定所用端口
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出 
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;// 设置时钟 
	GPIO_Init(GPIOB,&GPIO_InitStructure);//写入
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//声明一个结构体变量,用来初始化定时器
	TIM_OCInitTypeDef TIM_OCInitStructure;//根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	//TIM3初始化
	TIM_DeInit(timer);//把定时器3设置为缺省值
	TIM_TimeBaseInitStructure.TIM_Period = freq; //设置自动重装载寄存器周期的值(在72MHz的时钟下执行?次溢出)
	TIM_TimeBaseInitStructure.TIM_Prescaler = 71;//TIMx时钟频率预分频值  将72MHz 72分频为1MHz。
	TIM_TimeBaseInitStructure.TIM_ClockDivision = 0x0;//设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//TIM向上计数模式	
	TIM_TimeBaseInit(timer, & TIM_TimeBaseInitStructure);//写入	
/*-----------------------------
TIM_OCMode    TIM_OCPolarity两者可控制PWM波形
------------------------------*/
	//PWM初始化
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//TIM的PWM1模式
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//PWM输出使能
	TIM_OCInitStructure.TIM_Pulse= duty;//设置占空比
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//TIM输出比较极性高
	switch(tim_channe1)
	{
		case 1: TIM_OC1Init(timer,&TIM_OCInitStructure);
					  TIM_OC1PreloadConfig(timer,TIM_OCPreload_Enable);//输出比较预装载
						break;
		case 2: TIM_OC2Init(timer,&TIM_OCInitStructure);
					  TIM_OC2PreloadConfig(timer,TIM_OCPreload_Enable);
						break;
		case 3: TIM_OC3Init(timer,&TIM_OCInitStructure);
					  TIM_OC3PreloadConfig(timer,TIM_OCPreload_Enable);
						break;
		case 4: TIM_OC4Init(timer,&TIM_OCInitStructure);
					  TIM_OC4PreloadConfig(timer,TIM_OCPreload_Enable);
						break;
	}	
	TIM_ARRPreloadConfig(timer,ENABLE);//使能TIM3的ARR的预装载寄存器
	TIM_Cmd(timer,ENABLE);//使能TIM3
}

3.代码2:PWM头文件:
名称:servomotor.h
变量:
#ifndef __SERVOMOTOR_H
#define __SERVOMOTOR_H
#include "stm32f10x.h"
void Pwm_Start(TIM_TypeDef* timer,uint8_t tim_channe1,uint16_t freq,uint16_t duty);
#endif

4.代码3:main.c:
/*---------------------------------------------------------------------
TIM3 CH4 PB1
---------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "delay.h"
#include "servomotor.h"

int main()
{	  
	delay_init();
	while(1)
	{
	Pwm_Start(TIM3,4,29988,1510);//占空比取值范围(455~2565)
	delay_ms(200);
	Pwm_Start(TIM3,4,29988,2265);
	delay_ms(200);
	Pwm_Start(TIM3,4,29988,2565);
	delay_ms(200);
	}
}

5.审查

我们怎样才能检查端口输出波形的正确性呢?
这时需要进行KEIL5仿真,因为篇幅过长,所以有关keil5仿真功能大家可以去搜索一下相关文章进行设置。
对于main.c函数中的while循环的第一句:

Pwm_Start(TIM3,4,29988,1510);//占空比取值范围(455~2565)

其PB1端口输出波形周期与括号内倒数第二个元素相关,占空比与倒数第一个元素相关。
对于main.c函数中的while循环的第一句:
关于此句的输出波形为:(周期=20ms,高电平持续时间=1ms)
长度为20ms,高电平持续时间为1ms高电平
----------特别声明:因为我们用的是HSI(高速内部RC振荡器 72MHz)所以受环境温度电压等等的影响,函数Pwm_Start(x,y,z,w);中, z和w变量需要进行相应的调整!因为是向上计数所以,设置周期随x增大而增大,占空比随w增大而减小。

6.总结与链接

(1)因为算是刚刚入门STM32,当我完成后,发现舵机并不能被驱动,后来才知道开发板的5V输出端口不支持ST-link供电(电压依然是3.3V)所以难以驱动,后来改用串口烧录(供电)便可。
ST-link

(2)在使用ST-link联合Keil5烧录STM32时时常常出现无法识别芯片等问题,个人感觉ST-link老版本固件更加好用。
友情链接: 安装方法.

(3)注意:keil5进行仿真后无法与STlink进行联合调试,请注意备份工程。如果已进行了仿真可以将User目录下“工程名.uvoptx”和“工程名.uvguix.电脑用户名”两个文件删除,以及OBJ目录下(工程编译生成文件夹)所有文件删除。删除之前必须关闭Keil5。再次打开你会发现所有编译设置都已初始化,将其再次配置即可。

当你的keil工程不兼容或者烧录时总是闪退也可以这样做。

拓展 : 另一种控制方式 : 频率固定, 占空比可变.

main.c

/*
TIM4_CH4    PB9  PD15
TIM3_CH1    PA6  PB4  PC6 
TIM3_CH2    PA7  PB5  PC7 
*/
#include "stm32f10x.h"
#include "delay.h"
#include "servomotor.h"
int main()
{	  
	uint16_t a = 0;
	delay_init();
	Pwm_Start();   // PWM 初始化
	while(1)
	{	
		for(a = 1;a < 480;a++)
		{
			TIM_SetCompare1(TIM3,a);  // 设置 TIM3 通道一 ARR 值
			TIM_SetCompare2(TIM3,a);  // 设置 TIM3 通道二 ARR 值
			TIM_SetCompare4(TIM4,a);  // 设置 TIM4 通道四 ARR 值
			delay_ms(4);
		}
		
		for(a = 480;a > 1;a--)
		{
			TIM_SetCompare1(TIM3,a);
			TIM_SetCompare2(TIM3,a);
			TIM_SetCompare4(TIM4,a);
			delay_ms(4);
		}
	}
}

servomotor.h

#ifndef __SERVOMOTOR_H
#define __SERVOMOTOR_H
#include "stm32f10x.h"
void Pwm_Start(void);
#endif

servomotor.c

/*------------------------------------------------------------------------------------------
		仿真时添加端口格式:例如PC5 为 PORTC.5  。
		PWM占空比主要由寄存器 CCRx 控制,其值与 CNT 比较并输出。
--------------------------------------------------------------------------------------------*/
#include "servomotor.h"
void Pwm_Start(void)
{

	GPIO_InitTypeDef GPIO_InitStructure;  
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 ,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4 ,ENABLE);
	
	//端口
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_OCInitTypeDef TIM_OCInitStructure;
	

	TIM_DeInit(TIM3);//把定时器3设置为缺省值
	TIM_DeInit(TIM4);//把定时器4设置为缺省值
	
	TIM_TimeBaseInitStructure.TIM_Period = 720-1; //设置自动重装载寄存器周期的值(在72MHz的时钟下执行?次溢出)
	TIM_TimeBaseInitStructure.TIM_Prescaler = 100-0;//TIMx时钟频率预分频值   72* 10/72000000 = 720/72000000S = 100KHZ
	TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;//设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//TIM向上计数模式	
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter =0;
	
	TIM_TimeBaseInit(TIM3, & TIM_TimeBaseInitStructure);//写入	
	
	TIM_TimeBaseInit(TIM4, & TIM_TimeBaseInitStructure);//写入	
	
/*-----------------------------
TIM_OCMode    TIM_OCPolarity两者可控制PWM波形
------------------------------*/
	//PWM初始化
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//TIM的PWM1模式
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//PWM输出使能
	TIM_OCInitStructure.TIM_Pulse= 0;//设置占空比
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//TIM输出比较极性高
	
	TIM_OC1Init(TIM3,&TIM_OCInitStructure);
	TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);//输出比较预装载   PA6  PB4  PC6 
	
	TIM_OC2Init(TIM3,&TIM_OCInitStructure);
	TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);//                  PA7  PB5  PC7 
	
	TIM_OC4Init(TIM4,&TIM_OCInitStructure);
	TIM_OC4PreloadConfig(TIM4,TIM_OCPreload_Enable);//                 TIM4_CH4    PB9  
	
	TIM_ARRPreloadConfig(TIM3,ENABLE);//使能TIM3的ARR的预装载寄存器
	TIM_Cmd(TIM3,ENABLE);//使能TIM3
	
	TIM_ARRPreloadConfig(TIM4,ENABLE);//使能TIM4的ARR的预装载寄存器
	TIM_Cmd(TIM4,ENABLE);//使能TIM4
}
  • 32
    点赞
  • 134
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值