STM32F4 PWM输出驱动

STM32F4的定时器时钟有点复杂,当APB1和APB2分频数为1的时候,TIM1、TIM8~TIM11的时钟为APB2的时钟,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟;
而如果APB1和APB2分频数不为1,那么TIM1、TIM8~TIM11的时钟为APB2的时钟的两倍,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟的两倍

因此专门写了一个函数,用于获取定时器时钟频率 u32 TIMER_GetTimeClockSpeed(TIMER_CH timerCh)

/*************************************************************************************************************
 * 文件名		:	stm32f4_timer.c
 * 功能			:	STM32F4 timer驱动
 * 作者			:	[email protected]
 * 创建时间		:	2017-08-24
 * 最后修改时间	:	2017-08-24
 * 详细:			高级定时器:tim1,time8(16位)
					通用定时器:tim3,tim4(16位),tim2,tim5(32位)
					通用定时器:tim9-tim14(16位)
					基本定时器:tim6-tim7(16位)
					2019-10-24:增加 u32 Timer_GetTimeClockSpeed(TTIMER_CH timerCh) 支持
					
					因为系统初始化SystemInit函数里初始化APB1总线时钟为4分频即42M,APB2总线时钟为2分频即84M,
					所以TIM1、TIM8~TIM11的时钟为APB2时钟的两倍即168M,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟的两倍即84M。
*************************************************************************************************************/	
#include "stm32f4_timer.h"
#include "system.h" 



//定时器结构体指针
static const TIM_TypeDef *TimerX[14]	= {TIM1,TIM2,TIM3,TIM4,TIM5,TIM6,TIM7,TIM8,TIM9,TIM10,TIM11,TIM12,TIM13,TIM14};


/*************************************************************************************************************************
* 函数			:	u32 TIMER_GetTimeClockSpeed(TIMER_CH timerCh)
* 功能			:	STM32F4获取定时器时钟频率
* 参数			:	timerCh:定时器选择,见TIMER_CH;
* 返回			:	定时器时钟频率,单位Hz
* 依赖			:	底层宏定义
* 作者			:	[email protected]
* 时间			:	2019-10-24
* 最后修改时间 	: 	2019-10-24
* 说明			: 	当APB1和APB2分频数为1的时候,TIM1、TIM8~TIM11的时钟为APB2的时钟,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟;
					而如果APB1和APB2分频数不为1,那么TIM1、TIM8~TIM11的时钟为APB2的时钟的两倍,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟的两倍
*************************************************************************************************************************/
u32 TIMER_GetTimeClockSpeed(TIMER_CH timerCh)
{
	u32 clock = 0;
	u8 PPRE;
	
	switch(timerCh)
	{
		case TIMER1:	
		case TIMER8:
		case TIMER9:
		case TIMER10:
		case TIMER11:	//来自APB2高速时钟总线
		{
			PPRE = (RCC->CFGR>>13)&0X7;	//获取PPRE2 分频值
			if(PPRE == 0)	//分频数位0,直接等于APB2时钟
			{
				clock = SYS_GetAPB2ClockSpeed();
			}
			else //分频数不为1,时钟为APB2的2倍
			{
				clock = SYS_GetAPB2ClockSpeed()*2;
			}
		}break;	
		case TIMER2:	
		case TIMER3:
		case TIMER4:
		case TIMER5:
		case TIMER6:
		case TIMER7:	
		case TIMER12:
		case TIMER13:
		case TIMER14:	//来自APB1低速时钟	
		{
			PPRE = (RCC->CFGR>>10)&0X7;	//获取PPRE1 分频值
			if(PPRE == 0)	//分频数位0,直接等于APB1时钟
			{
				clock = SYS_GetAPB1ClockSpeed();
			}
			else //分频数不为1,时钟为APB1的2倍
			{
				clock = SYS_GetAPB1ClockSpeed()*2;
			}
		}break;
		default:
		{
			clock = 0;
		}break;
	}
	
	return clock;
}




/*************************************************************************************************************************
* 函数	:	void TimerPWM_Init(TIMER_CH timerCh, TIMER_PWM_CH pwmCh,u16 psc, u16 arr)
* 功能	:	初始化定时器PWM输出
* 参数	:	timerCh:定时器选择,见TIMER_CH	
			pwmCh :PWM输出通道选择,见TIMER_PWM_CH
			psc: 预分频器
			arr: 重装值
* 返回	:	无
* 依赖	:	底层读写函数
* 作者	:	[email protected]
* 时间	:	20130331
* 最后修改时间 : 20130331
* 说明	: 	初始为边沿对齐模式,向上计数,更新时计数器不停止
			此函数不初始化外部引脚,需要初始化对应的PWM输出脚为复用推挽输出即可
			同一定时器产生的不同通道的PWM频率一样
*************************************************************************************************************************/ 
void TimerPWM_Init(TIMER_CH timerCh, TIMER_PWM_CH pwmCh,u16 psc, u16 arr)
{
	TIM_TypeDef *TIMx;
	const SYS_DEV_CLOCK DEV_TimeBuff[] = {DEV_TIM1,DEV_TIM2,DEV_TIM3,DEV_TIM4,DEV_TIM5,DEV_TIM6,DEV_TIM7,DEV_TIM8,DEV_TIM9,DEV_TIM10,
	DEV_TIM11,DEV_TIM12,DEV_TIM13,DEV_TIM14};

	if(timerCh == TIMER6 || timerCh == TIMER7)	//定时器6和定时器7为基本定时器,不带PWM输出
		return;

	TIMx = (TIM_TypeDef *)TimerX[timerCh];					//获取指定定时器寄存器结构指针	
	SYS_DeviceClockEnable(DEV_TimeBuff[timerCh],TRUE);		//使能定时器时钟
	
	//初始化配置
	TIMx->CR1 = 0;							//关闭定时器,并复位相关设置
	TIMx->CR2 = 0;							//复位相关设置
	TIMx->PSC = psc;						//预分频器
	TIMx->ARR = arr;						//自动重装值 
	//配置PWM通道
	switch (pwmCh)
	{
	 	case PWM_CH1:
		{	
			TIMx->CCMR1 &= ~0xff;			//清除之前设置
			TIMx->CCMR1 |= 7 << 4;			//PWM2模式
			TIMx->CCMR1 |= 1 << 3;  		//CH1预装载使能
			TIMx->CCER &= ~(0xf<<0);   		//清除之前设置
			TIMx->CCER |= 1<<1;				//OC1 低电平有效
			TIMx->CCER |= 1<<0;   			//OC1 输出使能
		}break;
		case PWM_CH2:
		{
			TIMx->CCMR1 &= ~(0xff << 8);	//清除之前设置
			TIMx->CCMR1 |= (7 << 12);		//PWM2模式
			TIMx->CCMR1 |= 1 << 11;  		//CH2预装载使能	
			TIMx->CCER &= ~(0xf<<4);   		//清除之前设置
			TIMx->CCER |= 1<<5;				//OC2 低电平有效
			TIMx->CCER |= 1<<4;   			//OC2 输出使能
		}break;
		case PWM_CH3:
		{
			TIMx->CCMR2 &= ~0xff;			//清除之前设置
			TIMx->CCMR2 |= 7 << 4;			//PWM2模式
			TIMx->CCMR2 |= 1 << 3;  		//CH3预装载使能
			TIMx->CCER &= ~(0xf<<8);   		//清除之前设置
			TIMx->CCER |= 1<<9;				//OC3 低电平有效
			TIMx->CCER |= 1<<8;   			//OC3 输出使能
		}break;
		case PWM_CH4:
		{
			TIMx->CCMR2 &= ~(0xff << 8);	//清除之前设置
			TIMx->CCMR2 |= 7 << 12;			//PWM2模式
			TIMx->CCMR2 |= 1 << 11;  		//CH4预装载使能
			TIMx->CCER &= ~(0xf<<12);   	//清除之前设置
			TIMx->CCER |= 1<<13;			//OC4 低电平有效
			TIMx->CCER |= 1<<12;   			//OC4 输出使能
		}break;
		default : break;
	}
	TIMx->CR1 |= BIT7;						//定时器自动重装使能
	TIMx->EGR |= BIT0;						//更新
	TIMx->BDTR |= BIT15;					//主输出使能,只针对TIM1,TIM8
	TIMx->CR1 |= BIT0;    					//使能定时器
}


/*************************************************************************************************************************
* 函数	:	void TimerPWM_Set(TIMER_CH timerCh, TIMER_PWM_CH pwmCh,u16 pwm)
* 功能	:	设置PWM占空比
* 参数	:	timerCh:定时器选择,见TIMER_CH	
			pwmCh :PWM输出通道选择,见TIMER_PWM_CH
			pwm:PWM为高的时间
* 返回	:	无
* 依赖	:	底层读写函数
* 作者	:	[email protected]
* 时间	:	20130331
* 最后修改时间 : 20130331
* 说明	: 	需要先初始化定时器
			值越大,占空比越大
*************************************************************************************************************************/ 
void TimerPWM_Set(TIMER_CH timerCh, TIMER_PWM_CH pwmCh,u16 pwm)
{
	TIM_TypeDef *TIMx;

	if(timerCh == TIMER6 || timerCh == TIMER7)				//定时器6和定时器7为基本定时器,不带PWM输出
		return;	

	TIMx = (TIM_TypeDef *)TimerX[timerCh];					//获取指定定时器寄存器结构指针
	//设置捕获/比较寄存器的值,调节占空比
	switch (pwmCh)
	{
	 	case PWM_CH1 : TIMx->CCR1 = pwm; break;
		case PWM_CH2 : TIMx->CCR2 = pwm; break;
		case PWM_CH3 : TIMx->CCR3 = pwm; break;
		case PWM_CH4 : TIMx->CCR4 = pwm; break;
		default : break;
	}
}






/*************************************************************************************************************
 * 文件名		:	stm32f4_timer.h
 * 功能			:	STM32F4 timer驱动
 * 作者			:	[email protected]
 * 创建时间		:	2017-08-24
 * 最后修改时间	:	2017-08-24
 * 详细:			高级定时器:tim1,time8(16位)
					通用定时器:tim3,tim4(16位),tim2,tim5(32位)
					通用定时器:tim9-tim14(16位)
					基本定时器:tim6-tim7(16位)
*************************************************************************************************************/	
#ifndef __STM32F4_TIMER_H_
#define __STM32F4_TIMER_H_
#include "system.h" 




//定时器选择,定时器1,8高级定时器,2,3,4,5 9-14为通用定时器,6,7为基本定时器
typedef enum 
{
	TIMER1	=	0,
	TIMER2	=	1,
	TIMER3	=	2,
	TIMER4	=	3,
	TIMER5	=	4,
	TIMER6	=	5,
	TIMER7	=	6,
	TIMER8	=	7,
	TIMER9	=	8,
	TIMER10	=	9,
	TIMER11	=	10,
	TIMER12	=	11,
	TIMER13	=	12,
	TIMER14	=	13,
} TIMER_CH;


//定时器PWM通道,每个定时器有4路PWM输出通道
typedef enum 
{
	PWM_CH1	=	0,
	PWM_CH2	=	1,
	PWM_CH3	=	2,
	PWM_CH4	=	3,
} TIMER_PWM_CH;



//API
void TimerPWM_Init(TIMER_CH timerCh, TIMER_PWM_CH pwmCh,u16 psc, u16 arr);	//初始化定时器PWM输出
void TimerPWM_Set(TIMER_CH timerCh, TIMER_PWM_CH pwmCh,u16 pwm);			//设置PWM占空比
u32 TIMER_GetTimeClockSpeed(TIMER_CH timerCh);								//STM32F4获取定时器时钟频率

#endif //__STM32F4_TIMER_H_


















/*************************************************************************************************************************
* 函数			:	u32 SYS_GetSystemClockSpeed(void)		
* 功能			:	STM32F4 获取系统时钟频率
* 参数			:	无
* 返回			:	系统时钟频率HZ
* 依赖			:	底层宏定义
* 作者			:	[email protected]
* 时间			:	2016-03-13
* 最后修改时间 	: 	2016-03-13
* 说明			:	用于获取系统时钟,SYSCLK时钟
					PLL使能后:SYSCLK=PLLOUT=VCO/PLLP
					f(VCO 时钟) = f(PLL 时钟输入) × (PLLN / PLLM)
					f(PLL 常规时钟输出) = f(VCO 时钟) / PLLP
*************************************************************************************************************************/
u32 SYS_GetSystemClockSpeed(void)
{
	static const u8 PLLP[] = {2,4,6,8};	//PLL 分频系数。
	u32 temp;
	
	switch(SYS_GetSystemClockSource())	//获取时钟来源
	{
		case SYS_CLOCK_HSI:	//HSI作为系统时钟
		{
			return HSI_CLOCK*1000000;
		}
		case SYS_CLOCK_HSE: //HSE作为系统时钟
		{
			return HSE_CLOCK*1000000;
		}
		default:	//PLL
		{
			temp = (RCC->PLLCFGR>>6)&0x1ff;			//获取PLLN,(PLL输入时钟/PLLM = 1MHZ)
			temp /= PLLP[(RCC->PLLCFGR>>16)&0x3];	//除以PLLP
			temp *= 1000000;
			return temp;
		}
	}
}

/*************************************************************************************************************************
* 函数			:	u32 SYS_GetAHBClockSpeed(void)		
* 功能			:	STM32F4 获取AHB时钟频率
* 参数			:	无
* 返回			:	时钟频率HZ
* 依赖			:	底层宏定义
* 作者			:	[email protected]
* 时间			:	2016-03-13
* 最后修改时间 	: 	2016-03-13
* 说明			:	AHB=SYSCLK/PRESC
*************************************************************************************************************************/
u32 SYS_GetAHBClockSpeed(void)
{
	static const u16 HPRE[] = {1,1,1,1,1,1,1,1,2,4,8,16,64,128,256,512};	//AHB 时钟分频系数。
	
	return SYS_GetSystemClockSpeed()/HPRE[((RCC->CFGR>>4)&0XF)];
}


/*************************************************************************************************************************
* 函数			:	u32 SYS_GetAPB1ClockSpeed(void)	
* 功能			:	STM32F4 获取APB1时钟频率
* 参数			:	无
* 返回			:	时钟频率HZ
* 依赖			:	底层宏定义
* 作者			:	[email protected]
* 时间			:	2016-03-13
* 最后修改时间 	: 	2016-03-13
* 说明			:	APB1=AHB/PPRE1
*************************************************************************************************************************/
u32 SYS_GetAPB1ClockSpeed(void)
{
	static const u8 PPRE1[] = {1,1,1,1,2,4,8,16};
	
	return SYS_GetAHBClockSpeed()/PPRE1[((RCC->CFGR>>10)&0X7)];
}

/*************************************************************************************************************************
* 函数			:	u32 SYS_GetAPB2ClockSpeed(void)	
* 功能			:	STM32F4 获取APB2时钟频率
* 参数			:	无
* 返回			:	时钟频率HZ
* 依赖			:	底层宏定义
* 作者			:	[email protected]
* 时间			:	2016-03-13
* 最后修改时间 	: 	2016-03-13
* 说明			:	APB1=AHB/PPRE2
*************************************************************************************************************************/
u32 SYS_GetAPB2ClockSpeed(void)
{
	static const u8 PPRE2[] = {1,1,1,1,2,4,8,16};
	
	return SYS_GetAHBClockSpeed()/PPRE2[((RCC->CFGR>>13)&0X7)];
}

这样就可以动态的去调节系统时钟频率而保障PWM频率不变。

发布了143 篇原创文章 · 获赞 370 · 访问量 81万+

猜你喜欢

转载自blog.csdn.net/cp1300/article/details/103185970