STM32F103-定时器编码器模式和高级定时器输出互补的PWM信号

本文介绍了使用STM32的定时器2编码器模式来替代输入捕获中断获取电机转速,以解决高速旋转时中断处理影响系统性能的问题。同时,通过定时器8配置输出互补的PWM信号来控制电机。在定时器3中断服务程序中,每0.05秒读取编码器速度并计算。在不输出PWM时,禁用定时器8并强制输出低电平,确保安全。文章探讨了这种方法的可行性,并提及了计划集成PID控制以及CAN和LIN通信的挑战。
摘要由CSDN通过智能技术生成

STM32F103-定时器编码器模式和高级定时器输出互补的PWM信号

刚使用STM32时使用输入捕获中断的方式计算编码器的速度从而得到电机的转速,经过一段时间的学习发现使用输入捕获中断的方式得到电机的转速,在电机转速不高的情况下可以很好的得到电机的速度,但是当电机转速很高时,STM32会很频繁的进入中断,单片机花费在中断处理的时间会很长,影响系统正常的运行。
经过了解,使用STM32定时器的编码器模式,可以不使用输入捕获中断就可以得到电机转速。
在这里很感谢网上大神的无私分享,让我收获很多也学习了很多。

编码器如何产生A,B两相信号和定时器编码器基础知识就不在这过多的说明,大家网上可以看到很多资料。在这里就再着重说一下编码器模式的难点。这张图个人感觉是整个编码器模式最难理解的部分。个人的理解是:假如有A,B两相编码器信号,A相此时比B相提前90度。在下图第三种情况下:若相对信号电平信号为B相信号,此时B相电平为高电平,此时A相信号下降沿,那么计数器向上计数一次;此时B相电平为低电平,此时A相信号上升沿,那么计数器向上计数一次。若相对信号电平信号为A相信号,此时A相电平为高电平,此时B相信号上升沿,那么计数器向上计数一次;此时A相电平为低电平,此时B相信号下降沿,那么计数器向上计数一次。所以计数器一共会计数4次,提高了计数精度。在后面的程序中除以4就是因为编码器模式采用了这种4倍频。(个人水平有限,如有问题还请包涵~谢谢)在这里插入图片描述
下面是定时器2编码器模式和定时器8输出互补的PWM信号和定时器3中断的具体配置:
(1),将定时器2两个引脚开启时钟和配置成浮空输入
(2),将定时器2配置成编码器模式,A相和B相信号发生变化时都对计数器进行计数。
(3),设定定时器2计数器初始值
(4),配置定时器8,输出两路互补的PWM波控制电机
(5),配置定时器3,每0.05S中断一次读取定时器2计数器的值,计算编码器的速度。并且每次中断更新定时器2的初值

//*****************主函数*****************//
#include "sys.h"
#include "delay.h"
#include "timer.h"
//#include "usart.h"
#include "SEGGER_RTT.h"


int16_t Signal_Fre = 0;


int main(void)
{		
	delay_init();	    	 //延时函数初始化	 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  //uart_init(115200);	 //串口初始化为115200
	
	TIM8_PWM_Init(899,3);	 //PWM频率=72000000/900/4=20Khz
	TIM2_Encoder_Init(65535,0); //定时器2编码器模式
	TIM3_Int_Init(4999,719);//0.05s定时器溢出中断
	
	TIM8_PWMStopGPIO_Init();
	delay_ms(10); 
	SEGGER_RTT_printf(0,"Test \r\n"); 
	 
  while(1)
	{
		
		TIM_SetCompare1(TIM8,450);
	  TIM_SetCompare2(TIM8,0);
			
		delay_ms(10);  
	}	 
}

//**********定时器配置****************//
#include "timer.h"
//#include "usart.h"
#include "SEGGER_RTT.h"

extern int16_t Signal_Fre;

///
//TIM2--编码器模式
//TIM3--0.05秒定时-计算编码器速度
//TIM8--PWM互补输出
///


/***************TIM2初始化********************/  	  
//通用定时器2--编码器模式,PA0,PA1;
//arr:自动重装值
//psc:时钟预分频数
void TIM2_Encoder_Init(u16 arr,u16 psc)
{
    GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_ICInitTypeDef  TIM_ICInitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	//使能定时器2时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	
		
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;                      
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseStructure.TIM_Period=arr;
	TIM_TimeBaseStructure.TIM_Prescaler=psc;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
	
	TIM_EncoderInterfaceConfig(TIM2,TIM_EncoderMode_TI12,TIM_ICPolarity_BothEdge,TIM_ICPolarity_BothEdge); //编码器模式
	TIM_ICStructInit(&TIM_ICInitStructure);
	//TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;
	TIM_ICInitStructure.TIM_ICFilter=6;
	//TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
	//TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;
	//TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;
	TIM_ICInit(TIM2,&TIM_ICInitStructure);
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//清除更新标志位
	//TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
    TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除中断标志位
	TIM_SetCounter(TIM2,32768);
    TIM_Cmd(TIM2,ENABLE);
}

/****************************TIM3初始化*********************************************/
void TIM3_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
  
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
    TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseStructure.TIM_Period=arr;
	TIM_TimeBaseStructure.TIM_Prescaler=psc;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);

	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
	
    NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
    NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM3,ENABLE);
}

/*********************************TIM3中断处理***************************************/
void TIM3_IRQHandler()
{
  if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
	{
		Signal_Fre = TIM_GetCounter(TIM2);
	    Signal_Fre = (int)Signal_Fre * 20 / 4;  //定时器0.05s中断一次,并且编码器模式是4倍频,计算出编码器的速度
		TIM_SetCounter(TIM2,32768);
			
		SEGGER_RTT_printf(0,"Signal_Fre=%d \r\n",Signal_Fre);	
		SEGGER_RTT_printf(0,"TIM3IRQ \r\n");	
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);  //清除TIMx更新中断标志 
	}
}


/********************TIM8初始化***********************/
//TIM8 PWM部分初始化  
//CH1:PC6
//CH1N:PA7
//CH2:PC7
//CH2N:PB0
//CH3:PC8
//CH3N:PB1
//BKIN:PA7
//arr:自动重装值
//psc:时钟预分频数
void TIM8_PWM_Init(u16 arr,u16 psc)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);	//使能定时器8时钟
	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC , ENABLE);  //使能GPIO外设功能模块时钟
	
  //设置该引脚为复用输出功能,输出TIM8 CH1的PWM脉冲波形	GPIOC.6
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //TIM_CH1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIO
	
	//设置该引脚为复用输出功能,输出TIM8 CH1N的PWM脉冲波形	GPIOA.7
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //TIM_CH1N
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
	
	//设置该引脚为复用输出功能,输出TIM8 CH2的PWM脉冲波形	GPIOC.7
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //TIM_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIO
	
	//设置该引脚为复用输出功能,输出TIM8 CH1N的PWM脉冲波形	GPIOB.0
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //TIM_CH2N
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
	
  //设置该引脚为复用输出功能,输出TIM8 CH3的PWM脉冲波形	GPIOC.8
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM_CH3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIO
	
	//设置该引脚为复用输出功能,输出TIM8 CH3N的PWM脉冲波形	GPIOB.1
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM_CH3N
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
	
  //刹车引脚PA6
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //BKIN
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
	
   //初始化TIM1
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	//初始化TIM1 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //互补比较输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;  //占空比 CCR的值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	//TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; //互补输出极性:TIM输出比较极性高
	//TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low ; //互补输出极性:TIM输出比较极性低
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; 
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
	
	TIM_OC1Init(TIM8, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM8
	TIM_OC1PreloadConfig(TIM8, TIM_OCPreload_Enable);  //使能TIM1在CCR上的预装载寄存器
	
	TIM_OCInitStructure.TIM_Pulse = 0;  //占空比 CCR的值
    TIM_OC2Init(TIM8, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM8 
    TIM_OC2PreloadConfig(TIM8, TIM_OCPreload_Enable);  //使能TIM1在CCR上的预装载寄存器
	
	//TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;  //刹车引脚恢复后引脚输出状态
	TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable;  //刹车引脚不能恢复后引脚输出状态
	TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;   //刹车使能
	TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;   //刹车引脚高电平刹车
	TIM_BDTRInitStructure.TIM_DeadTime = 36; //死区时间36:500ns
	
	TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF;
	
	TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
	//TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Disable;
	TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
	//TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Disable;
	TIM_BDTRConfig(TIM8,&TIM_BDTRInitStructure);
 
	TIM_Cmd(TIM8, ENABLE);  //使能TIM8
	TIM_CtrlPWMOutputs(TIM8,ENABLE);  //主输出使能,当使用通用定时器时,这个不需要

}


void TIM8_PWMStopGPIO_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_Cmd(TIM8, DISABLE);  //失能TIM8
	
	//设置该引脚为复用输出功能,输出TIM8 CH1的PWM脉冲波形	GPIOC.6
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //TIM_CH1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIO
	GPIO_ResetBits(GPIOC,GPIO_Pin_6);						 //PC.6 输出低
	
	//设置该引脚为复用输出功能,输出TIM8 CH1N的PWM脉冲波形	GPIOA.7
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //TIM_CH1N
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
	GPIO_ResetBits(GPIOA,GPIO_Pin_7);						 //PA.7 输出低
	
	//设置该引脚为复用输出功能,输出TIM8 CH2的PWM脉冲波形	GPIOC.7
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //TIM_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIO
	GPIO_ResetBits(GPIOC,GPIO_Pin_7);						 //PC.7 输出低
	
	//设置该引脚为复用输出功能,输出TIM8 CH1N的PWM脉冲波形	GPIOB.0
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //TIM_CH2N
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
	GPIO_ResetBits(GPIOB,GPIO_Pin_0);						 //PB.0 输出低
}


void TIM8_PWMStartGPIO_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_Cmd(TIM8, ENABLE);  //使能TIM8
	
	//设置该引脚为复用输出功能,输出TIM8 CH1的PWM脉冲波形	GPIOC.6
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //TIM_CH1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIO
	
	//设置该引脚为复用输出功能,输出TIM8 CH1N的PWM脉冲波形	GPIOA.7
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //TIM_CH1N
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
	
	//设置该引脚为复用输出功能,输出TIM8 CH2的PWM脉冲波形	GPIOC.7
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //TIM_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIO
	
	//设置该引脚为复用输出功能,输出TIM8 CH1N的PWM脉冲波形	GPIOB.0
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //TIM_CH2N
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
}


使用STM32高级定时器输出PWM信号,使用单极性模式控制电机。STM32高级定时器在空闲状态希望两路互补的PWM信号都为低电平,但是在实际应用中发现一路互补的PWM信号,在空闲时一个为低电平但是另外一个恒为高电平,这在实际应用中肯定会导致问题,所以我想的方法是:在不需要输出PWM信号的时候,使用TIM8_PWMStopGPIO_Init()这个函数,在这个函数里将高级定时器失能,并且将两路四个PWM输出引脚推挽输出强制拉低,确定四个引脚都为低电平,不会引起电机运行。在需要输出PWM信号的时候再使能高级定时器,输出PWM信号。

我使用的是RTT打印,这样使用比较方便,打印速度比较快,而且还不占用USART端口。

目前该程序可以准确读出编码器的转速,但是也有几点疑惑和问题:
(1),对于高级定时器,在不输出PWM信号时,失能定时器8并且将定时器8四个引脚推挽输出强制拉低;在输出PWM信号时,再使能定时器8。这种方法是否可行或者有更好的方法呢?
(2),在定时器3中可以加入PID算法,这样可以使用PID调节电机转速。目前这个功能已经实现。但是我想使用CAN和LIN通信组成一个系统,目前CAN通信已经可以实现,但是目前LIN通信我调试了已经有一段时间了,一直没有成功。想请问一个各位大神能否发我一些资料或者代码也可以留一个联系方式,想请教一下各位。
(最后再重申一下哈,本人水平有限,如有错误还请多多包涵,谢谢~)

  • 5
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值