学习stm32 pwm 多路舵机的控制(PWM 的频率的理解)

接着上一节,pwm 的使用,控制多路舵机进行角度旋转和设置。

参考stm32f103x中文说明时钟树如下图

1.当HSI被用于作为PLL时钟的输入时,系统时钟能得到的最大频率是64MHz。
2.对于内部和外部时钟源的特性,请参考相应产品数据手册中“电气特性”章节。
用户可通过多个预分频器配置AHB、高速APB(APB2)和低速APB(APB1)域的频率。AHB和
APB2域的最大频率是72MHz。APB1域的最大允许频率是36MHz。SDIO接口的时钟频率固定
为HCLK/2。
RCC通过AHB时钟(HCLK)8分频后作为Cortex系统定时器(SysTick)的外部时钟。通过对SysTick
控制与状态寄存器的设置,可选择上述时钟或Cortex(HCLK)时钟作为SysTick时钟。ADC时钟
由高速APB2时钟经2、4、6或8分频后获得。
定时器时钟频率分配由硬件按以下2种情况自动设置:

  1. 如果相应的APB预分频系数是1,定时器的时钟频率与所在APB总线频率一致。
  2. 否则,定时器的时钟频率被设为与其相连的APB总线频率的2倍。

科普小知识下面会用

1Hz代表每秒周期震动1次
1MHZ=1,000,000HZ
1hz是一秒1次,即1mhz是一百万秒
1s = 1000ms = 1,000,000us
秒 毫秒 微妙

定时器PWM 的频率

根据STM32的定时器时钟的频率,比如时钟的频率是72MHZ,可以理解为一秒钟STM32会自己数72M次,
预分频系数就是将频率分割,比如分频系数是72,则该时钟的频率会变成72MHZ/72=1MHZ,
但是在设置的时候要注意,数值应该是72-1。
假定分频系数是72-1,那么频率变成1MHZ,也就意味着STM32在一秒钟会数1M次,即1us数一次。1/1000 000 =0.000 001s = 1us
₣apb = 72MHZ;

理解转化:分频把时钟的频率分割开来达到需要的频率

预分频 频率为
72MHZ 72(分频系数0~72) 1MHZ(1mhz是一百万秒)
72MHZ 7200 -1 1/10 000 = 0.0001 s = 100us
72MHZ 7200 -1 1/10 000 = 0.0001 s = 100us

如此类推,在预分频系数确定的情况下,定时的时长就由预装载值确定了

不同舵机设置的预分频系数和频率

PWM的频率: 是指1秒钟内信号从高电平到低电平再回到高电平的次数(一个周期);
也就是说一秒钟PWM有多少个周期
单位: Hz
表示方式: 50Hz 100Hz
PWM的周期:
T=1/f
周期=1/频率
50Hz = 20ms 一个周期
按照公式 如下

				72x1,000,000HZ
Fpwm =   ---------------------------------- = 50HZ
				(PSC-1)x(ARR-1)	            
 PWM周期为20ms = (2000*720)/72000000 = 0.2,通过程序设置的TIM_Period = 719,TIM_Prescaler = 1999(本文程序)

多路舵机的控制只需要要定时器初始化多个通道连接舵机的时钟线即可。

代码出处来与博客 (部分修改)
版权声明:本代码出处为CSDN博主「菜菜Chicken」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_37957854/article/details/105666609

timx.c

#include "timx.h"
#include "system.h"

/**
	定时器3,可产生四路的PWM输出,四个通道分别对应的引脚情况如下
	TIM3_CH1,TIM3_CH2,TIM3_CH3,TIM3_CH4
	没有重映像的对应情况:
	PA6,PA7,PB0,PB1
	部分重映像:
	PB4,PB5,PB0,PB1
	完全重映像:
	PC6,PC7,PC8,PC9	

	当我们的IO口不仅仅是做普通的输入输出使用的时候,作为别的外设(AD,串口,定时器等)的特定功能引脚,就需要开启外设.
	这里我们还需要开启APB2外设上的复用时钟AFIO,同时IO口采用的是复用输出!

	我们这里是没有使用重映射功能.
*/
// 宏定义
//判断当前是处于哪一种模式,以便于我们初始化IO口
#define NO_REAMP   0
#define PART_REAMP 1
#define FULL_REAMP 2

// ---> 这里是需要制定的参数

//指定这里的 当前的模式,我们给她默认指定是 没有重映射
#define CURRENT_MODE PART_REAMP 

//*************根据当前模式初始化IO口 函数
void MY_TIM3_GPIO_Init(void){
	
	GPIO_InitTypeDef 	GPIO_InitStructure;
	
	//1.开启AFIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	//2. 根据当前的重映像的模式 配置时钟 和 初始化相关引脚
	switch(CURRENT_MODE){
		
		//2.1 如果没有重映射
		case NO_REAMP:{
			
			// 时钟分配
			RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);
			// 初始化IO口
			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
			GPIO_Init(GPIOA,&GPIO_InitStructure);
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
			GPIO_Init(GPIOB,&GPIO_InitStructure);
			
			break;
		}
		//2.2 部分重映射
		case PART_REAMP:{
			
			// 时钟分配
			RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
			// 初始化IO口
			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5;
			GPIO_Init(GPIOB,&GPIO_InitStructure);
			
			GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);//改变指定管脚的映射	
			break;
		}
		//2.3 全映射
		case FULL_REAMP:{
			
			// 时钟分配
			RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
			// 初始化IO口
			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
			GPIO_Init(GPIOC,&GPIO_InitStructure);
			
			GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);//改变指定管脚的映射	
			break;
		}
		default:break;
	}	
}
/*
	* 初始化定时器的时候指定我们分频系数psc,这里是将我们的系统时钟(72MHz)进行分频
	* 然后指定重装载值arr,这个重装载值的意思就是当 我们的定时器的计数值 达到这个arr时,定时器就会重新装载其他值.
		例如当我们设置定时器为向上计数时,定时器计数的值等于arr之后就会被清0重新计数
	* 定时器计数的值被重装载一次被就是一个更新(Update)
	* 计算Update时间公式
	Tout = ((arr+1)*(psc+1))/Tclk
	公式推导详解:
		Tclk是定时器时钟源,在这里就是72Mhz 
		我们将分配的时钟进行分频,指定分频值为psc,就将我们的Tclk分了psc+1,我们定时器的最终频率就是Tclk/(psc+1) MHz
		这里的频率的意思就是1s中记 Tclk/(psc+1)M个数 (1M=10的6次方) ,每记一个数的时间为(psc+1)/Tclk ,很好理解频率的倒数是周期,这里每一个数的周期就是(psc+1)/Tclk 秒
		然后我们从0记到arr 就是 (arr+1)*(psc+1)/Tclk
	举例:比如我们设置arr=7199,psc=9999
	我们将72MHz (1M等于10的6次方) 分成了(9999+1)等于 7200Hz
	就是一秒钟记录9000数,每记录一个数就是1/7200秒
	我们这里记录9000个数进入定时器更新(7199+1)*(1/7200)=1s,也就是1s进入一次更新Update
*/
//简单进行定时器初始化,设置 预装载值 和 分频系数
void MY_TIM3_Init(u16 arr,u16 psc){
	
	//初始化结构体
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	
	//1.分配时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	//2.初始化定时器相关配置
	TIM_TimeBaseStructure.TIM_Period = arr;
	TIM_TimeBaseStructure.TIM_Prescaler = psc;
	
	/*在这里说一下这个TIM_ClockDivision 是设置与进行输入捕获相关的分频
		设置的这个值不会影响定时器的时钟频率,我们一般设置为TIM_CKD_DIV1,也就是不分频*/
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
	
	//3.打开定时器
	TIM_Cmd(TIM3,ENABLE);
}


//***************** 定时器PWM输出初始化函数
void MY_TIM3_PWM_Init(u16 arr,u16 psc){
	
	//初始化结构体
	TIM_OCInitTypeDef TIM_OCInitstrcuture;
	
	//1.初始化定时器 和 相关的IO口
	MY_TIM3_Init(arr,psc); 
	MY_TIM3_GPIO_Init();
	
	//2.初始化PWM的模式
	
	/**
	选择PWM模式:
		PWM1模式:
			向上计数时,当我们 当前的 计数值 小于我们的设置阈值为有效电平,否则为无效电平,向下计数时与向上计数时相反
		PWM2模式:
			与PWM1模式向上向下计数时完全相反
	*/
	TIM_OCInitstrcuture.TIM_OCMode = TIM_OCMode_PWM2;
	TIM_OCInitstrcuture.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitstrcuture.TIM_OCPolarity = TIM_OCPolarity_High;   //输出电平为高,也就是有效电平为高
	TIM_OC1Init(TIM3,&TIM_OCInitstrcuture);						//这里是设置利用通道1输出
	
	//这里初始化通道1、2、3、4,我们可以根据自己需求初始化其它通道
	
	TIM_OC2Init(TIM3,&TIM_OCInitstrcuture);
	TIM_OC3Init(TIM3,&TIM_OCInitstrcuture);
	TIM_OC4Init(TIM3,&TIM_OCInitstrcuture);

	TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能预装载寄存器
	TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);
	TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);
	TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);
	
}


//TIM4 PWM初始化
void TIM4_CH1_PWM_Init(u16 arr,u16 psc)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
	TIM_OCInitTypeDef TIM_OCInitTypeStruct;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
	
	//要开启复用功能的时钟才能重映射
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  ,ENABLE); 
	
	//TIM3部分重映射
	/*
	*查看数据手册,引脚的定时器通道是完全映射,还是部分映射
	*二者调用参数不相同
	*完全映射 :GPIO_FullRemap_TIM4
	*部分映射 :GPIO_PartialRemap_TIM4
	*/
//	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM4,ENABLE);
	
	//设置该引脚为复用输出功能
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	//初始化TIM4
	TIM_TimeBaseStruct.TIM_Period = arr;//重装载值 
	TIM_TimeBaseStruct.TIM_Prescaler = psc;//预分频值 
	TIM_TimeBaseStruct.TIM_ClockDivision = 0; //时钟分频1、2、4分频	
	TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;//设置计数模式
	TIM_TimeBaseInit(TIM4,&TIM_TimeBaseStruct);
	
	//初始化输出比较参数
	TIM_OCInitTypeStruct.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式
	TIM_OCInitTypeStruct.TIM_OutputState = TIM_OutputState_Enable;//比较输出使能
	TIM_OCInitTypeStruct.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性
	TIM_OC1Init(TIM4,&TIM_OCInitTypeStruct); //根据TIMX的参数设定初始化外设 TIMX OC2,TIMX OC3....
	
	//使能预装载寄存器
	TIM_OC1PreloadConfig(TIM4,TIM_OCPreload_Enable);
	
	//使能定时器
	TIM_Cmd(TIM4,ENABLE);
}

timx.h

#ifndef _timx_H
#define _timx_H

#include "system.h"


void MY_TIM3_PWM_Init(u16 arr,u16 psc);
void TIM4_CH1_PWM_Init(u16 arr,u16 psc);
#endif

主函数


int main(){
		u16 i=0;
		u8 fx=0;
		SysTick_Init(72);	    	 //延时函数初始化	  
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
		USART1_Init(115200);	 	//串口初始化为115200
		LED_Init();		  		//初始化与LED连接的硬件接口
		TFTLCD_Init();	
		FRONT_COLOR=RED;//设置字体为红色 
		LCD_ShowString(10,10,tftlcd_data.width,tftlcd_data.height,16,"PRECHIN STM32F1");	
		LCD_ShowString(10,30,tftlcd_data.width,tftlcd_data.height,16,"PWM CARD TEST");	


	
	
	//例如:STM32F103的主频为 72M
	//按如上设置可知
	//ARR = 100 -1
	//PSC = 72-1
	//所以 Fpwm = 72M/(100*72)= 10kHz 也就是 1/100k S = 100us
	//Fpwm = 主频 / ((ARR+1)*(PSC+1))(单位:Hz)
	//MY_TIM3_PWM_Init(1999,719);//PWM频率=72000000/(719+1)/(1999+1)=50hz=20m
	MY_TIM3_PWM_Init(500,72-1); //频率是2Kh
	
//	TIM_SetCompare4(TIM3,50);
	while(1)
		{
			

			
			//-90度
	//	TIM_SetCompare2(TIM3,1750);//占空比(2000-1750)/2000*20ms=2.5ms
 
//		//45度
//		TIM_SetCompare1(TIM3,1800);//占空比(2000-1800)/2000*20ms=2ms
// 
//		//0度
//		TIM_SetCompare1(TIM3,1850);//占空比(2000-1850)/2000*20ms=1.5ms
// 
//		//-45度
//		TIM_SetCompare1(TIM3,1900);//占空比(2000-1900)/2000*20ms=1ms
// 
//		//-90度
//		TIM_SetCompare1(TIM3,1945);//占空比(2000-1945)/2000*20ms=0.5ms

		if(fx==0)
		{
			i++;
			if(i==300)
			{
				fx=1;
			}
		}
		else
		{
			i--;
			if(i==0)
			{
				fx=0;
			}
		}
		TIM_SetCompare2(TIM3,i);  //i值最大可以取499,因为ARR最大值是499.
		delay_ms(10);	
	};
}

物联沃分享整理
物联沃-IOTWORD物联网 » 学习stm32 pwm 多路舵机的控制(PWM 的频率的理解)

发表评论