STM32直流减速电机控制篇(三)编码器测速程序编写

  1. 编程思路
    任何一个程序的编写我们都应该先理清楚编程思路,通过上一篇讲解的编码器测速原理我们应该知道要想通过编码器得知电机转速我们第一步就应该是捕获A相和B相输出的脉冲
    在这里插入图片描述
    因为电机速度的定义是单位时间内的转数,所以第二步在一个定时中断里读取一次捕获到的脉冲数(即电机的几何位移)并清零记录脉冲数的变量。经过这两个步骤我们就完成了电机的速度测量。
  2. 代码编写
    利用32单片机(以STM32F103VET6为例,用Keil编程)捕获AB相的脉冲有两种方法第一种利用GPIO的外部中断来捕获跳变沿从而可以记录脉冲数。第二种利用定时器的编码器模式来记录脉冲数。
    外部中断捕获波形
/**************************************************************************
函数功能:外部中断采集编码器初始化
入口参数:无
返回  值:无
**************************************************************************/
void Encoder_Init_TIM_Exit0(void)//用于检测编码器A相使用PA0引脚
{
   	GPIO_InitTypeDef GPIO_InitStructure;
   	EXTI_InitTypeDef EXTI_InitStructure;
   	NVIC_InitTypeDef NVIC_InitStructure;
   
   	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟
   	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
   	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;	            //端口配置
   	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;         //上拉输入
   	GPIO_Init(GPIOA, &GPIO_InitStructure);					      //根据设定参数初始化GPIOB 
 	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
 	EXTI_InitStructure.EXTI_Line=EXTI_Line0;
 	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
 	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//跳变沿触发
 	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
 	EXTI_Init(&EXTI_InitStructure);	 	//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
     
  // 配置中断优先级 													
   NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;	  
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_Init(&NVIC_InitStructure);
}


void Encoder_Init_TIM_Exit1(void)//用于检测编码器B相使用PA1引脚
{
   	GPIO_InitTypeDef GPIO_InitStructure;
   	EXTI_InitTypeDef EXTI_InitStructure;
   	NVIC_InitTypeDef NVIC_InitStructure;
   	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟
   	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
   	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;	            //端口配置
   	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;         //上拉输入
   	GPIO_Init(GPIOA, &GPIO_InitStructure);					      //根据设定参数初始化GPIOB 
 	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);
 	EXTI_InitStructure.EXTI_Line=EXTI_Line1;
 	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
 	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//跳变沿触发
 	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
 	EXTI_Init(&EXTI_InitStructure);	 	//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
     
  //配置中断优先级												
   NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;	  
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;	
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_Init(&NVIC_InitStructure);
}

以上代码完成了A相 B相外部中断的初始化当有跳变沿发生时则进入外部中断服务函数,在外部中断服务函数中对脉冲进行计数,如果电机正转则计数的变量Encoder_A_EXTI(在中断服务函数中计数时使用)就加1,反转则减1。使用四倍频计数提高测速精度,所以A相和B相的跳变沿检测到后我们都进行计数。首先看A相

void EXTI0_IRQHandler(void)  //外部中断线0服务函数
{
		EXTI->PR=1<<0;  //清除LINE上的中断标志位
		if(PAin(0)==0)   //这里判断检测到的是否是下降沿
	{
	if(PAin(1)==0)   Encoder_A_EXTI++;//看B相的电平如果是高电机就是正转则加1,否则就是反转减1
	else             Encoder_A_EXTI--;
	}
	else                  //上升沿
	{ 
	if(PAin(1)==0)  Encoder_A_EXTI--; //B相低电平为正转,加1,高电平反转减1
	else             Encoder_A_EXTI++;
	}		
}

B相同理

void EXTI1_IRQHandler(void)//外部中断线1服务函数
{			
		EXTI->PR=1<<1;  //清除LINE上的中断标志位
	if(PAin(1)==1) //这里判断检测到的是否是上升沿
	{
	if(PAin(0)==0)  Encoder_A_EXTI++;  //看A相的电平如果是低,电机就是正转则加1,否则就是反转减1 
	else            Encoder_A_EXTI--;
	}
	else
	{
	if(PAin(0)==0)  Encoder_A_EXTI--;
	else            Encoder_A_EXTI++;
	}		
}

通过上数代码我们就可以在Encoder_A_EXTI这个变量中记录A相和B相的脉冲数,然后我们再通过一个函数来读取并清零Encoder_A_EXTI。

int Read_Encoder(u8 TIMX)
{
    int Encoder_TIM;    
   switch(TIMX)
	 {
	   case 2:  Encoder_TIM=(short)Encoder_A_EXTI;  Encoder_A_EXTI=0; break;	
	   default:  Encoder_TIM=0;
	 }
		return Encoder_TIM;
}

然后再一个定时中断中使用此函数即可知道电机的转速。下面是第二种方法的代码
定时器编码器模式

/**************************************************************************
函数功能:把TIM3初始化为编码器接口模式
入口参数:无
返回  值:无
**************************************************************************/
void Encoder_Init_TIM3(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
  TIM_ICInitTypeDef TIM_ICInitStructure;  
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//使能定时器3的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PB端口时钟
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;	//端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);					      //根据设定参数初始化GPIOB
  
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器 
  TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD-1; //设定计数器自动重装值
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上计数  
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
  TIM_ICStructInit(&TIM_ICInitStructure);
  TIM_ICInitStructure.TIM_ICFilter = 10;
  TIM_ICInit(TIM3, &TIM_ICInitStructure);
  TIM_ClearFlag(TIM3, TIM_FLAG_Update);//清除TIM的更新标志位
  TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
  //Reset counter
  TIM_SetCounter(TIM3,0);
  TIM_Cmd(TIM3, ENABLE); 
}

初始化完成后我们可以直接在TIM3的CNT计数器中读取到我们捕获的脉冲数,这里要注意一点应为CNT寄存器的最大只能是65536超过这个值该计数器(CNT)就会溢出,所以我们要即使去读取CNT然后把它清空。同样是利用 Read_Encoder()这个函数

int Read_Encoder(u8 TIMX)
{
    int Encoder_TIM;    
   switch(TIMX)
	 {
	   case 2:  Encoder_TIM=(short)Encoder_A_EXTI;  Encoder_A_EXTI=0; break;	
		 case 3:  Encoder_TIM= (short)TIM3 -> CNT;  TIM3 -> CNT=0;break;		
		 default:  Encoder_TIM=0;
	 }
		return Encoder_TIM;
}

显而易见利用定时器的编码器模式来测速更加快捷简单,并且没有这么多中断占用的MCU资源更少,所以测速我们一般优先使用第二种方法,只有当定时器资源不足时才会使用第一种方法。完整的Keil代码我也会提供给大家。代码链接:https://pan.baidu.com/s/1ESA4myZIz2QCGx0Q6EKPpw
提取码:01kd

  • 123
    点赞
  • 889
    收藏
    觉得还不错? 一键收藏
  • 75
    评论
以下是使用STM32控制直流减速电机的PWM代码: ```c #include "stm32f10x.h" #define PWM_PERIOD 1000 // PWM周期,单位为us #define MOTOR_SPEED 500 // 电机速度,取值范围为0~1000 int main(void) { // 使能GPIOB时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能TIM3时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 配置GPIOB10为TIM3_CH3输出 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); // 配置TIM3 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD - 1; // PWM周期 TIM_TimeBaseStructure.TIM_Prescaler = 71; // 分频系数,时钟为72MHz,分频后为1MHz TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 配置TIM3_CH3 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = MOTOR_SPEED; // 占空比,取值范围为0~PWM_PERIOD TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC3Init(TIM3, &TIM_OCInitStructure); TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); // 启动TIM3 TIM_Cmd(TIM3, ENABLE); while (1) { // 电机控制代码 } } ``` 在此代码中,我们使用了TIM3的PWM功能来控制直流减速电机的速度。首先,我们需要将GPIOB10配置为TIM3_CH3输出,并且使能GPIOB和TIM3的时钟。然后,我们配置TIM3的基本参数,包括PWM周期、分频系数等。接着,我们配置TIM3_CH3的参数,包括占空比、输出极性等。最后,启动TIM3,并在while循环中编写电机控制代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值