分享

stm32 TIM定时器[操作寄存器+库函数]-Changing's Blog

 杭州文轩 2014-11-27
stm32配备了2个高级定时器TIM1和TIM8,4个通用定时器 TIM2,TIM3,TIM4和TIM5,还有两个基本定时器TIM6和TIM7。 高级定时器常用于电机控制,因为其加入了死区控制,紧急制动,定时器同步等高级特性。基本定时器可以为数模转化器提供准确的时间基准。
stm32的通用定时器由一个通过可编程预分频器驱动的16位自动装载计数器构成。通用定时器可以用于测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。
通用计时器的使用,需要先配置一个时基单元,就是设定一个基准时间,确定计数一次耗去的时间,可以设定在几个微妙到几个毫秒之间。
通用定时器的都有4个独立通道(TIMx_CH1~4),这些通道可以用来作为:
  • 输入捕获
  • 输出比较
  • PWM生成
  • 单脉冲模式输出
时基单元核心部件是一个16位分频器,通过对定时器时钟的分频实现确定时间基准的功能。
根据手册可以知道 基准时钟的计算公式:     
T = (分频寄存器+1)/TIM时钟 
需要注意的是TIM时钟的大小,以TIM2为例,虽然其挂载在APB1总线上,PCLK时钟为36Mhz,但TIM2得到的却是72Mhz。所有挂载在APB1总线上的通用定时器时钟频率都为72Mhz;
通用寄存器的四个通道,每一个通道相当于一个中断触发源,可以设置一个计数值,当TIM计数值和此计数值相等时,触发中断。 
本例实现以TIM2为例产生一组不同频率的时钟,使4个LED不同频率闪烁

直接操作寄存器
首先是控制寄存器(TIMx_CR1),该寄存器各位描述如下:
timx_cr1.png
需要注意的是
ARPE位 :要开启自动重装必须将此为置1;
DIR位:  0:计数器向上计数;    1:计数器向下计数。 注:当计数器配置为中央对齐模式或编码器模式时,该位为只读。
CEN位:计数器使能位
计数器中断使能寄存器:
timx_dier.png
TIE:触发中断使能位;
UIE:允许更新中断位,允许由更新事件来产生中断;
  • 更新事件包括: 计数器向上/向下溢出,计数器初始化
  • 触发时间包括:计数器启动,停止,初始化
CC1IE~CC4IE:允许捕获/比较1~4中断
TDE,UDE,CC1DE~CC4DE为DMA相关中断设置,这里不讨论。
预分频寄存器(TIMx_PSC),低16位有效,该寄存器用于设置时钟进行分频,然后提供给计数器作为时钟。
自动重装载寄存器(TIMx_ARR),低16位有效。
状态寄存器(TIMx_SR),该寄存器用于标识当前与定时器相关的各种事件和中断是否发生。
描述如下:
timx_sr.png
UIF:更新中断标记 (Update interrupt flag)  当产生更新事件时该位由硬件置’1’。它由软件清’0’。
  • 若TIMx_CR1寄存器的UDIS=0、URS=0,当TIMx_EGR寄存器的UG=1时产生更新事件(软件对计数器CNT重新初始化);
  •  若TIMx_CR1寄存器的UDIS=0、URS=0,当计数器CNT被触发事件重初始化时产生更新事件。
CC1IF~CC4IF:捕获/比较1~4 中断标记 (Capture/Compare 1 interrupt flag)
TIF:触发器中断标记 (Trigger interrupt flag)  
当发生触发事件(当从模式控制器处于除门控模式外的其它模式时,在TRGI输入端检测到有效边沿,或门控模式下的任一边沿)时由硬件对该位置’1’。它由软件清’0’。
代码如下:(system.h 和 stm32f10x_it.h 等相关代码参照 stm32 直接操作寄存器开发环境配置
User/main.c
#include <stm32f10x_lib.h>	 
#include "system.h" 
#include "tim.h" 	

void Gpio_Init(void);

int main(void)
{				  

	Rcc_Init(9); 			 //系统时钟设置
	//Usart1_Init(72,9600);	//设置系统时钟和波特率

	// 相关TIM_x,CCR_x参数定义tim.h文件

	Tim_Init(TIM_2,65535,7199);  //初始化TIM2定时器,设定重装值和分频值

	Tim_CCR_Set(TIM_2,CCR_1,40000); 
	Tim_CCR_Set(TIM_2,CCR_2,20000);
	Tim_CCR_Set(TIM_2,CCR_3,10000);
	Tim_CCR_Set(TIM_2,CCR_4,5000);
	 
	Nvic_Init(0,0,TIM2_IRQChannel,0);	  //设置抢占优先级为0,响应优先级为0,中断分组为0

	Gpio_Init();

	while(1);

}

void Gpio_Init(void)
{
	RCC->APB2ENR|=1<<2;    //使能PORTA时钟	   	 

	GPIOA->CRL&=0x0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出
	GPIOA->CRL|=0x33334444; 
	//GPIOA->ODR &=0xFFFFFFF0;
	
	//USART1 串口I/O设置

	//GPIOA -> CRH&=0xFFFFF00F;   //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
	//GPIOA -> CRH|=0x000008B0;	  
}
User/stm32f10x_it.c
#include "stm32f10x_it.h"
#include "system.h"

#define LED0 PAout(4)
#define LED1 PAout(5)
#define LED2 PAout(6)
#define LED3 PAout(7)
 
void TIM2_IRQHandler(void)
{
	if(TIM2->SR&0x02)   //捕获比较中断1触发
	{
		LED0 = !LED0;	
			
		TIM2 ->CCR1 = TIM2 -> CNT + 40000;	 //更新捕获/比较1的值

		TIM2->SR &= 0xFD;  //清除捕获比较中断
	}

	if(TIM2->SR&0x04)   //捕获比较中断2触发
	{
		LED1 = !LED1;		
		TIM2 ->CCR2 = TIM2 -> CNT + 20000;

		TIM2->SR &= 0xFB;  //清除捕获比较中断
	}

	if(TIM2->SR&0x08)   //捕获比较中断3触发
	{
		LED2 = !LED2;	

		TIM2 ->CCR3 = TIM2 -> CNT + 10000;
		TIM2->SR &= 0xF7;  //清除捕获比较中断
	
	}

	if(TIM2->SR&0x10)   //捕获比较中断4触发
	{
		LED3 = !LED3;

		TIM2 ->CCR4 = TIM2 -> CNT + 5000;	
		TIM2->SR &= 0x0F;  //清除捕获比较中断
	
	}
	
	TIM2->SR &= ~(1<<0);    //清除中断

}

Library/src/tim.c
#include <stm32f10x_lib.h>	 
#include "tim.h" 

//通用定时器初始化
//参数说明:TIM_x 为选择定时器 TIM_1为通用寄存器1又一次类推(定义于tim.h), arr为自动重装值 ;psc 为时钟预分频数
//待完善 目前只支持TIM2
void Tim_Init(u8 TIM_x,u16 arr,u16 psc)
{
	switch(TIM_x)
	{
	 	case 1 :{

			RCC->APB2ENR |=1<<11;

			break;
		}

		case 2 :{
			RCC->APB1ENR |=1<<0;

			TIM2->ARR = arr;			//设定自动重装值
			TIM2->PSC = psc;		    //设定预分频值
			TIM2->DIER |= 1<<0;			//允许更新中断
			TIM2->DIER |= 1<<6;			//允许触发中断

			TIM2->CR1 |= 0x81;			//使能定时器,自动重装允许 						

			break;
		}

		case 3 :{
			RCC->APB1ENR |=1<<1;

			break;
		}
		case 4 :{
			RCC->APB1ENR |=1<<2;				

			break;
		}

		case 5 :{
			RCC->APB1ENR |=1<<3;		

			break;
		}
	 	case 6 :{

			RCC->APB1ENR |=1<<4;

			break;
		}	
	 	case 7 :{

			RCC->APB1ENR |=1<<5;	

			break;
		}

	 	case 8 :{

			RCC->APB2ENR |=1<<13;

			break;
		}

	}
}

//捕获比较值设定函数
//参数说明:
//			TIM_x 为选择定时器 TIM_1为通用寄存器1又一次类推(定义于tim.h)
//			CCR_x 为选择捕获/比较寄存器(1~4)(定义于tim.h)
//			val   为要设定的捕获/比较寄存器的值
// 待完善,目前只支持TIM2

void Tim_CCR_Set(u8 TIM_x,u8 CCR_x,u32 val)
{
	switch(TIM_x)
	{
	 	case 1 :{
			break;
		}

		case 2 :{

			TIM2->DIER |= 1 << CCR_x;			//开启相应允许捕获/比较中断

			switch(CCR_x){

				case 1: {
					TIM2 ->CCR1 = val;		 //设置捕获/比较1的值 
					break;
				}

				case 2: {
					TIM2 ->CCR2 = val;		 //设置捕获/比较2的值 
					break;
				}

				case 3: {
					TIM2 ->CCR3 = val;		 //设置捕获/比较3的值 
					break;
				}

				case 4: {
					TIM2 ->CCR4 = val;		 //设置捕获/比较4的值 
					break;
				}
			}
					
			break;
		}

		case 3 :{
			break;
		}
		case 4 :{
			break;
		}

		case 5 :{
			break;
		}
	 	case 6 :{
			break;
		}	
	 	case 7 :{
			break;
		}

	 	case 8 :{
			break;
		}

	}

}
Library/inc/tim.h
#include <stm32f10x_lib.h>

#define  TIM_1  0x01
#define  TIM_2  0x02
#define  TIM_3  0x03
#define  TIM_4  0x04
#define  TIM_5  0x05
#define  TIM_6  0x06
#define  TIM_7  0x07
#define  TIM_8  0x08


#define  CCR_1  0x01
#define  CCR_2  0x02
#define  CCR_3  0x03
#define  CCR_4  0x04

void Tim_Init(u8 TIM_x,u16 arr,u16 psc); 
void Tim_CCR_Set(u8 TIM_x,u8 CCR_x,u32 val);
库函数操作
通用定时器有4个通道,每个通道都有6种工作模式:
TIM_OCMODE定义
TIM_OCMode 描述
    TIM_OCMODE_Timging TIM输出比较时间模式,中断时管脚无变化
    TIM_OCMODE_Active TIM输出比较时间模式,中断时管脚强制为有效电平
    TIM_OCMODE_Inactive TIM输出比较时间模式,中断时管脚强制为无效电平
    TIM_OCMODE_Toggle TIM输出比较时间模式,中断时管脚状态翻转,高变低,低变高
    TIM_OCMODE_PWM1 TIM脉冲宽度调制模式1
    TIM_OCMODE_PWM2 TIM脉冲宽度调制模式2
PS:至于有效电平是高还是低,要看CCER寄存器的CCxP位设置。两种PWM模式,区别在于通道的电平极性是相反的。
main.c
#include "stm32f10x.h"

vu16 CCR1_Val = 40000;
vu16 CCR2_Val = 20000;
vu16 CCR3_Val = 10000;
vu16 CCR4_Val = 5000;              

void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void TIM_Configuration(void);

int main(void)
{
   
  	RCC_Configuration();
  	GPIO_Configuration();
 	NVIC_Configuration();
	TIM_Configuration();
	while(1);
}

void TIM_Configuration(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_TimeBaseStructure.TIM_Period = 65535;
	TIM_TimeBaseStructure.TIM_Prescaler = 7199;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);

	//TIM_PrescalerConfig(TIM2,7199,TIM_PSCReloadMode_Immediate);

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
	TIM_OC1Init(TIM2,&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
	TIM_OC2Init(TIM2,&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
	TIM_OC3Init(TIM2,&TIM_OCInitStructure);
 	TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
	TIM_OC4Init(TIM2,&TIM_OCInitStructure);
	
	TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Disable);
	TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Disable);
	TIM_OC3PreloadConfig(TIM2,TIM_OCPreload_Disable);
	TIM_OC4PreloadConfig(TIM2,TIM_OCPreload_Disable);

	TIM_ITConfig(TIM2,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4,ENABLE);
	
	TIM_Cmd(TIM2,ENABLE);

}

void NVIC_Configuration(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;

	#ifdef	VECT_TAB_RAM
		NVIC_SetVectorTable(NVIC_VectTab_RAM,0x0);
	#else
		NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x0);
	#endif

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

}

  
void GPIO_Configuration(void)
{
  	GPIO_InitTypeDef GPIO_InitStructure;

  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  	GPIO_Init(GPIOA , &GPIO_InitStructure); 
}


void RCC_Configuration(void)
{
	/* 定义枚举类型变量 HSEStartUpStatus */
	ErrorStatus HSEStartUpStatus;

  	/* 复位系统时钟设置*/
  	RCC_DeInit();
  	/* 开启HSE*/
  	RCC_HSEConfig(RCC_HSE_ON);
  	/* 等待HSE起振并稳定*/
  	HSEStartUpStatus = RCC_WaitForHSEStartUp();
	/* 判断HSE起是否振成功,是则进入if()内部 */
  	if(HSEStartUpStatus == SUCCESS)
  	{
    	/* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
    	RCC_HCLKConfig(RCC_SYSCLK_Div1); 
    	/* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
    	RCC_PCLK2Config(RCC_HCLK_Div1); 
    	/* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
    	RCC_PCLK1Config(RCC_HCLK_Div2);
    	/* 设置FLASH延时周期数为2 */
    	FLASH_SetLatency(FLASH_Latency_2);
    	/* 使能FLASH预取缓存 */
    	FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
    	/* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
    	RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
    	/* 使能PLL */ 
    	RCC_PLLCmd(ENABLE);
    	/* 等待PLL输出稳定 */
    	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
    	/* 选择SYSCLK时钟源为PLL */
    	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
    	/* 等待PLL成为SYSCLK时钟源 */
    	while(RCC_GetSYSCLKSource() != 0x08);
  	} 
  	/* 打开APB2总线上的GPIOA时钟*/
  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);
	
}
stm32f10x_it.c

#include "stm32f10x_it.h"

extern vu16 CCR1_Val;
extern vu16 CCR2_Val;
extern vu16 CCR3_Val;
extern vu16 CCR4_Val;



void TIM2_IRQHandler(void)
{
	vu16 capture=0;
	if(TIM_GetITStatus(TIM2,TIM_IT_CC1) != RESET)
	{
		GPIO_WriteBit(GPIOA , GPIO_Pin_4,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_4))); 		
		capture = TIM_GetCapture1(TIM2);
		TIM_SetCompare1(TIM2,capture + CCR1_Val);
		TIM_ClearITPendingBit(TIM2,TIM_IT_CC1);
	}
	else if(TIM_GetITStatus(TIM2,TIM_IT_CC2) != RESET)
	{
		GPIO_WriteBit(GPIOA , GPIO_Pin_5,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_5))); 		
		capture = TIM_GetCapture2(TIM2);
		TIM_SetCompare2(TIM2,capture + CCR2_Val);
		TIM_ClearITPendingBit(TIM2,TIM_IT_CC2);
	}
	else if(TIM_GetITStatus(TIM2,TIM_IT_CC3) != RESET)
	{
		GPIO_WriteBit(GPIOA , GPIO_Pin_6,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_6))); 		
		capture = TIM_GetCapture3(TIM2);
		TIM_SetCompare3(TIM2,capture + CCR3_Val);
		TIM_ClearITPendingBit(TIM2,TIM_IT_CC3);
	}
	else
	{
		GPIO_WriteBit(GPIOA , GPIO_Pin_7,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_7))); 		
		capture = TIM_GetCapture4(TIM2);
		TIM_SetCompare4(TIM2,capture + CCR4_Val);
		TIM_ClearITPendingBit(TIM2,TIM_IT_CC4);
	}
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多