(STM32)PWM输出控制电机旋转并且使用编码器读取脉冲数

这篇具有很好参考价值的文章主要介绍了(STM32)PWM输出控制电机旋转并且使用编码器读取脉冲数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

 前言

一、pwm输出让电机转 

1.电机的接线说明

2.驱动的接线说明

3.pwm输出代码

 pwm.c

pwm.h

4.输出pwm控制电机旋转

二、配置定时器编码器模式

1.定时器编码器模式

编码器原理

编码器相关的概念

2.编码器模式——代码部分

3.获取脉冲数

三、定时读取编码器读取的脉冲数

四、计算速度(本篇最重要部分)

1.速度计算原理

2. 速度计算代码


 前言

正文之前先介绍一下我使用的主控芯片、电机以及驱动。

主控芯片是STM32F103C8T6(这个芯片比较普遍、便宜,这款芯片使用熟练之后,我的建议是转到CH32V307VCT6);

这里我还想在说一点就是C8T6内的定时器只有4个(TIM1、TIM2、TIM3、TIM4),资源比较少。

电机是JGB37-520霍尔编码器直流减速电机(DC:12V)(530rpm);

磁环转一圈是11个脉冲(即11线);

电机的减速比为19;

pwm脉冲捕获脉冲数,stm32,单片机

驱动是TB6612FNG(这种驱动比较稳定,但是特别容易烧,注意一定不要接错线)

pwm脉冲捕获脉冲数,stm32,单片机

一、pwm输出让电机转 

1.电机的接线说明

电机电源线(红线和白线)接AO1、AO2(或者BO1、BO2);

编码器电源线(黑线和蓝线)接地和5V,一定不要接错,不然电机上的编码器会出问题;

编码器信号线(黄线和绿线)接定时器编码器接口;

2.驱动的接线说明

PWMA接PA0;PWMB接PA1;

AIN1、AIN2、BIN1、BIN2接单片机IO口;

STBY接5V;VM接12V;VCC接5V;

AO1、AO2、BO1、BO2接电机电源线(两轮);

电机和驱动的具体接线原理图如下:

pwm脉冲捕获脉冲数,stm32,单片机

pwm脉冲捕获脉冲数,stm32,单片机 

pwm脉冲捕获脉冲数,stm32,单片机 

提醒:

1.电机电源线接线问题:正接反接影响的是轮子的正转反转(自己可以按照实测一下);

2.编码器信号线接线问题:正接反接影响的是脉冲数的读取的正负(画板子的时候自己得重点注意);

3.pwm输出代码

 使用#define,可以方便我们修改IO口。

  •  pwm.c

#include "pwm.h"

void AIN_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	//AIN1
	RCC_APB2PeriphClockCmd(AIN1_GPIO_CLK,ENABLE);		 //开启时钟
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;    //推挽输出
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;	 //50MHz
	GPIO_InitStruct.GPIO_Pin = AIN1_GPIO_PIN;
	GPIO_Init(AIN1_GPIO_PORT ,&GPIO_InitStruct);
	GPIO_SetBits(AIN1_GPIO_PORT,GPIO_Pin_All);			//初始化			
	//AIN2
	RCC_APB2PeriphClockCmd(AIN2_GPIO_CLK,ENABLE);
	GPIO_InitStruct.GPIO_Pin = AIN2_GPIO_PIN;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(AIN2_GPIO_PORT ,&GPIO_InitStruct);
	GPIO_SetBits(AIN2_GPIO_PORT,GPIO_Pin_All);
	//BIN1
	RCC_APB2PeriphClockCmd(BIN1_GPIO_CLK,ENABLE);
	GPIO_InitStruct.GPIO_Pin = BIN1_GPIO_PIN;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(BIN1_GPIO_PORT ,&GPIO_InitStruct);
	GPIO_SetBits(BIN1_GPIO_PORT,GPIO_Pin_All);
	//BIN2
	RCC_APB2PeriphClockCmd(BIN2_GPIO_CLK,ENABLE);
	GPIO_InitStruct.GPIO_Pin = BIN2_GPIO_PIN;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(BIN2_GPIO_PORT ,&GPIO_InitStruct);
	GPIO_SetBits(BIN2_GPIO_PORT,GPIO_Pin_All);
}
static void GENERAL_TIM_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
	//输出比较通道1GPIO初始化
	RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin   = GENERAL_TIM_CH1_PIN;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;    // 复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);
	//输出比较通道2GPIO初始化
	RCC_APB2PeriphClockCmd(GENERAL_TIM_CH2_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin   = GENERAL_TIM_CH2_PIN;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;    // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GENERAL_TIM_CH2_PORT, &GPIO_InitStructure);
}
static void GENERAL_TIM_Mode_Config(void)
{
	// 开启定时器时钟,即内部时钟CK_INT=72M
	GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);
    /*--------------------时基结构体初始化-------------------------*/
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
	TIM_TimeBaseStructure.TIM_Period = GENERAL_TIM_PERIOD;	
	// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
	TIM_TimeBaseStructure.TIM_Prescaler = GENERAL_TIM_PSC;	
	// 初始化定时器
	TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
	/*--------------------输出比较结构体初始化-------------------*/		
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	// 配置为PWM模式1
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	// 输出使能
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	// 输出通道电平极性配置
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	// 设置占空比大小GENERAL_TIM_CH1_PULSE=2000
	TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CH1_PULSE;
	TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
	TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
	// 设置占空比大小GENERAL_TIM_CH2_PULSE=4000
	TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CH2_PULSE;
	TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure);
	TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
	// 使能计数器
	TIM_Cmd(GENERAL_TIM, ENABLE);	
}
void GENERAL_TIM_Init(void)
{
	GENERAL_TIM_Mode_Config();
	GENERAL_TIM_GPIO_Config(); 
}
/*正负表示正转和反转,0表示停止
	PERIOD = 8000-1固定
	*/
void GENERAL_TIM_Change_PULSE(int lun, int direction, int input_PULSE)
{
	
	int PULSE = (GENERAL_TIM_PERIOD + 1) * input_PULSE / 100;//input_PULSE设置为最高为100

	if(lun == 1)
	{
//		TIM_SetCompare1(TIM2, input_PULSE);
		GENERAL_TIM->CCR1 = PULSE;	
		if(direction == 0)//停止
		{
			GPIO_WriteBit(AIN1_GPIO_PORT,AIN1_GPIO_PIN,Bit_RESET);
			GPIO_WriteBit(AIN2_GPIO_PORT,AIN2_GPIO_PIN,Bit_RESET);
		}
		else if(direction == 1)//正转
		{
			GPIO_WriteBit(AIN1_GPIO_PORT,AIN1_GPIO_PIN,Bit_RESET);
			GPIO_WriteBit(AIN2_GPIO_PORT,AIN2_GPIO_PIN,Bit_SET);
		}
		else if(direction == -1)//反转
		{
			GPIO_WriteBit(AIN1_GPIO_PORT,AIN1_GPIO_PIN,Bit_SET);
			GPIO_WriteBit(AIN2_GPIO_PORT,AIN2_GPIO_PIN,Bit_RESET);
		}
	}
	if(lun == 2)
	{
//		TIM_SetCompare2(TIM2, input_PULSE);
		GENERAL_TIM->CCR2 = PULSE;
		if(direction == 0)//停止
		{
			GPIO_WriteBit(BIN1_GPIO_PORT,BIN1_GPIO_PIN,Bit_RESET);
			GPIO_WriteBit(BIN2_GPIO_PORT,BIN2_GPIO_PIN,Bit_RESET);
		}
		else if(direction == 1)//正转
		{
			GPIO_WriteBit(BIN1_GPIO_PORT,BIN1_GPIO_PIN,Bit_RESET);
			GPIO_WriteBit(BIN2_GPIO_PORT,BIN2_GPIO_PIN,Bit_SET);
		}
		else if(direction == -1)//反转
		{
			GPIO_WriteBit(BIN1_GPIO_PORT,BIN1_GPIO_PIN,Bit_SET);
			GPIO_WriteBit(BIN2_GPIO_PORT,BIN2_GPIO_PIN,Bit_RESET);
		}
	}
}



  • pwm.h

#ifndef _PWM_H
#define _PWM_H

#include "stm32f10x.h"

// 这里我们使用通用定时器TIM2

#define            GENERAL_TIM                   TIM2
#define            GENERAL_TIM_APBxClock_FUN     RCC_APB1PeriphClockCmd
#define            GENERAL_TIM_CLK               RCC_APB1Periph_TIM2
// PWM 信号的频率 F = TIM_CLK/{(ARR+1)*(PSC+1)}
// 占空比是 PULSE / (PERIOD+1)
#define            GENERAL_TIM_PERIOD            (8000-1)
#define            GENERAL_TIM_PSC               (9-1)
#define            GENERAL_TIM_CH1_PULSE         2000
#define            GENERAL_TIM_CH2_PULSE         4000

#define            GENERAL_TIM_IRQ               TIM2_UP_IRQn
#define            GENERAL_TIM_IRQHandler        TIM2_UP_IRQHandler

//输出通道1
#define            GENERAL_TIM_CH1_GPIO_CLK      RCC_APB2Periph_GPIOA
#define            GENERAL_TIM_CH1_PORT          GPIOA
#define            GENERAL_TIM_CH1_PIN           GPIO_Pin_0
//输出通道2
#define            GENERAL_TIM_CH2_GPIO_CLK      RCC_APB2Periph_GPIOA
#define            GENERAL_TIM_CH2_PORT          GPIOA
#define            GENERAL_TIM_CH2_PIN           GPIO_Pin_1
//对PA0初始化---AIN1
#define            AIN1_GPIO_CLK                 RCC_APB2Periph_GPIOB
#define            AIN1_GPIO_PORT				 GPIOB
#define 	   	   AIN1_GPIO_PIN                 GPIO_Pin_14
//对PA1初始化---AIN2
#define            AIN2_GPIO_CLK				 RCC_APB2Periph_GPIOB
#define            AIN2_GPIO_PORT                GPIOB
#define 		   AIN2_GPIO_PIN                 GPIO_Pin_15

//对PB0初始化---BIN1
#define            BIN1_GPIO_CLK                 RCC_APB2Periph_GPIOB
#define            BIN1_GPIO_PORT				 GPIOB
#define 		   BIN1_GPIO_PIN                 GPIO_Pin_13
//对PB1初始化---BIN2
#define            BIN2_GPIO_CLK                 RCC_APB2Periph_GPIOB
#define            BIN2_GPIO_PORT				 GPIOB
#define 		   BIN2_GPIO_PIN                 GPIO_Pin_12

void AIN_GPIO_Config(void);
void GENERAL_TIM_Init(void);
void GENERAL_TIM_Change_PULSE(int lun, int direction, int input_PULSE);

#endif

4.输出pwm控制电机旋转

 这里把控制电机旋转的函数提取出来,让大家看得更明白。

void GENERAL_TIM_Change_PULSE(int lun, int direction, int input_PULSE)
{
	
	int PULSE = (GENERAL_TIM_PERIOD + 1) * input_PULSE / 100;//input_PULSE设置为最高为100

	if(lun == 1)
	{
//		TIM_SetCompare1(TIM2, input_PULSE);
		GENERAL_TIM->CCR1 = PULSE;	
		if(direction == 0)//停止
		{
			GPIO_WriteBit(AIN1_GPIO_PORT,AIN1_GPIO_PIN,Bit_RESET);
			GPIO_WriteBit(AIN2_GPIO_PORT,AIN2_GPIO_PIN,Bit_RESET);
		}
		else if(direction == 1)//正转
		{
			GPIO_WriteBit(AIN1_GPIO_PORT,AIN1_GPIO_PIN,Bit_RESET);
			GPIO_WriteBit(AIN2_GPIO_PORT,AIN2_GPIO_PIN,Bit_SET);
		}
		else if(direction == -1)//反转
		{
			GPIO_WriteBit(AIN1_GPIO_PORT,AIN1_GPIO_PIN,Bit_SET);
			GPIO_WriteBit(AIN2_GPIO_PORT,AIN2_GPIO_PIN,Bit_RESET);
		}
	}
	if(lun == 2)
	{
//		TIM_SetCompare2(TIM2, input_PULSE);
		GENERAL_TIM->CCR2 = PULSE;
		if(direction == 0)//停止
		{
			GPIO_WriteBit(BIN1_GPIO_PORT,BIN1_GPIO_PIN,Bit_RESET);
			GPIO_WriteBit(BIN2_GPIO_PORT,BIN2_GPIO_PIN,Bit_RESET);
		}
		else if(direction == 1)//正转
		{
			GPIO_WriteBit(BIN1_GPIO_PORT,BIN1_GPIO_PIN,Bit_RESET);
			GPIO_WriteBit(BIN2_GPIO_PORT,BIN2_GPIO_PIN,Bit_SET);
		}
		else if(direction == -1)//反转
		{
			GPIO_WriteBit(BIN1_GPIO_PORT,BIN1_GPIO_PIN,Bit_SET);
			GPIO_WriteBit(BIN2_GPIO_PORT,BIN2_GPIO_PIN,Bit_RESET);
		}
	}
}

在这个函数中设定了三个参数,分别是电机(A或B)、旋转方向(正转反转停止)、占空比(以百分比的形式);

可以先下面代码中这样使用:

	GENERAL_TIM_Change_PULSE(1, 1, 25);
	GENERAL_TIM_Change_PULSE(2, -1, 35);

二、配置定时器编码器模式

这里先介绍一下定时器编码器模式

1.定时器编码器模式

编码器原理

        如果俩个相位差为90度,这俩个信号称为正交。由于俩个信号相差90度,可以根据俩个信号那个先那个后判断方向,根据编码器的脉冲数量及编码轮的周长可以算出行驶的距离。加上一个定时器去计数单位时间内采集到的编码脉冲数量就可以算出电机的速度。

编码器相关的概念

1.分辨率:编码器的轴每转一圈所输出的脉冲数。编码器以每旋转360度提供多少的通或暗刻线称为分辨率,也称解析分度、或直接称多少线,一般在每转分度5~10000线。

2.最大响应频率:编码器在1秒钟内能响应的最大脉冲数。其公式为: 最高响应频率(Hz) =  编码器分辨率 × 轴的转速(r/min)/60  另称PPS

3.最大转速:是指编码器机械系统能够承受的最高转速。

4.绝对编码器信号传输方式:并行、串行输出或总线型输出。输出电路与增量编码器相似,有集电极开路PNP、NPN型、差分驱动、推挽式。

2.编码器模式——代码部分

 两个霍尔编码器于是就使用了两个定时器(TIM3和TIM4)

/*下面是编译器配置函数*/
//编码器1初始化函数
void Encoder_TIM3_Init(void)
{
	//结构体定义
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	GPIO_InitTypeDef GPIO_InitStruct;
	TIM_ICInitTypeDef TIM_ICInitStruct;
	NVIC_InitTypeDef  NVIC_InitStructure;
	//时钟配置
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 ,ENABLE);  // 开启定时器3时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE); // 开启GPIO时钟
	
    //GPIO配置
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;    // 浮空输入
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;   // 编码器1:PA0/PA1
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA ,&GPIO_InitStruct);
	//定时器配置
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;        // 不分频
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;    //向上计数
	TIM_TimeBaseInitStruct.TIM_Period = 65535;											 
    //重装载值65535
	TIM_TimeBaseInitStruct.TIM_Prescaler = 0;												 
    //分频系数0(自动加1)
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
	//编码器配置:定时器3,模式3,上升沿
	 TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising); 
	//输入捕获配置
	TIM_ICStructInit(&TIM_ICInitStruct);
	TIM_ICInitStruct.TIM_ICFilter = 10;			//滤波器设置为10
	TIM_ICInit(TIM3,&TIM_ICInitStruct);
	
	NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;                   //定时器3中断分组配置
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;                   //使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01;      //抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =0x02;            //响应优先级2
	NVIC_Init(&NVIC_InitStructure);                                 //配置定时器3
	//清除定时器溢出更新标志位(清除计数值)
	TIM_ClearFlag(TIM3,TIM_FLAG_Update);
	//定时器3,溢出更新,使能
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); 
	//定时数据清零(输入捕获的值从0开始计数)
	TIM_SetCounter(TIM3,0);
	//定时器3使能
	TIM_Cmd(TIM3,ENABLE);
}
void Encoder_TIM4_Init(void)
{
	//结构体定义
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	GPIO_InitTypeDef GPIO_InitStruct;
	TIM_ICInitTypeDef TIM_ICInitStruct;
	NVIC_InitTypeDef  NVIC_InitStructure;
	
	//时钟配置
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);  //开启定时器4时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); //开启GPIO时钟
	
	//GPIO配置
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;		//编码器2:PB6/PB7
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB ,&GPIO_InitStruct);
	
	//定时器配置
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;        // 不分频
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;    //向上计数
	TIM_TimeBaseInitStruct.TIM_Period = 65535;											//重装载值65535
	TIM_TimeBaseInitStruct.TIM_Prescaler = 0;												//分频系数0(自动加1)
	TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStruct);
	//编码器配置:定时器4,模式3,上升沿
	TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising); 
	//输入捕获配置
	TIM_ICStructInit(&TIM_ICInitStruct);
	TIM_ICInitStruct.TIM_ICFilter = 10;			//滤波器设置为10
	TIM_ICInit(TIM4,&TIM_ICInitStruct);
	
	NVIC_InitStructure.NVIC_IRQChannel=TIM4_IRQn;                   //定时器4中断分组配置
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;                   //使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01;      //抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =0x03;            //响应优先级2
	NVIC_Init(&NVIC_InitStructure);                                 //配置定时器4
	
	//清除定时器溢出更新标志位(清除计数值)
	TIM_ClearFlag(TIM4,TIM_FLAG_Update);
	//定时器4,溢出更新,使能
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); 
	//定时数据清零(输入捕获的值从0开始计数)
	TIM_SetCounter(TIM4,0);
	//定时器4使能
	TIM_Cmd(TIM4,ENABLE);
}
// 定时器3中断服务函数
void TIM3_IRQHandler(void)
{
  if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)  // 中断标志位置1
  {
    TIM_ClearITPendingBit(TIM3,TIM_IT_Update);  // 清楚中断标志位
  }
}

// 定时器4中断服务函数
void TIM4_IRQHandler(void)
{
  if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET)  // 中断标志位置1
  {
    TIM_ClearITPendingBit(TIM4,TIM_IT_Update);  // 清楚中断标志位
  }
}

3.获取脉冲数

 配置好定时器的编码器模式后并且能够很好的运行,就可以进行下一步,来读取编码器的计数值。

下面是代码:

// 编码器速度读取函数
// 入口参数:定时器
// 编码器产生的是脉冲,计数器计脉冲数(位移)
int Read_Speed(int x)
{
  int value_1;
  switch(x)
  {
    case 3:
      // 单周期位移作为速度值
      value_1 = (short)TIM_GetCounter(TIM3);  // 采集编码器的计数值并保存
      TIM_SetCounter(TIM3,0);                 // 将定时器的计数值清零
      break;
    case 4:
      // 单周期位移作为速度值
      value_1 = (short)TIM_GetCounter(TIM4);  // 采集编码器的计数值并保存
      TIM_SetCounter(TIM4,0);                 // 将定时器的计数值清零
      break; 
    default: value_1 = 0;
  }
  return value_1;
}

这个函数比较简单,但是我还是想提一下下面两个函数,必须要认真去理解两个函数里的内容!

uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);

void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);

三、定时读取编码器读取的脉冲数

 之所以要定时来读取值,是因为我们在计算速度的时候必须要有个时间,很简单的原理我就不解释了。

这部分又涉及到定时器中断的问题,我使用的是TIM2;

在这里跟大家说一下定时器pwm输出和定时器中断能不能使用一个定时器来运行的问题。

答案是可以的,但是需要注意一个问题,就是pwm输出和中断两者的定时器频率一定要相同,不然会影响实际的运行。什么是定时器频率大家应该知道吧就是对arr与psc的配置。

再补充说一下,定时器中断与串口中断能不能同时运行?

我测试过,是不能够同时运行的,具体什么原因我就不知道了,上次因为这个问题纠缠我很久!

回归正文,下面是TIM2定时器代码:

#include "TIME.h"

/*********************************************陀螺仪数据读取,PID计算刷新时钟2***********************************/
void TIM2_Getsample_Int(u16 arr,u16 psc)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 计数到5000为500ms
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
 
	TIM_ITConfig( TIM2,TIM_IT_Update|TIM_IT_Trigger,ENABLE);//使能定时器2更新触发中断
 
	TIM_Cmd(TIM2, ENABLE);  //使能TIMx外设
 	
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM2中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure); 
}
//***************TIME2的中断*******************/
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) //溢出中断
	{
		i++;
		if(i == 100)
		{
			i = 0;	
			count1 = abs(Read_Speed(3));			//读取脉冲数(TIM3)
			count2 = abs(Read_Speed(4));			//读取脉冲数(TIM4)
			speed1 = Speed_calculate(count1);
			speed2 = Speed_calculate(count2);
			/*最初的闭环控制
			PWM1 = Velocity_Control(Target,count1); //速度环闭环控制
			TIM_SetCompare1(TIM2, PWM1/100);
			PWM2 = Velocity_Control(Target,count2); //速度环闭环控制
			TIM_SetCompare2(TIM2, PWM2/100);
			*/
		}	
	}	
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);  //清除中断标志位
}

我在主函数里定时器初始化为1ms中断一次,很容易看出读取脉冲数的间隔时间是100ms;

在这里我们就读取到了编码器上的脉冲数了,接下来我们就进入本篇文章最重要的计算速度的部分!

四、计算速度(本篇最重要部分)

1.速度计算原理

 定时器编码器模式使用的是四倍频(具体的编码器模式还请大家去CSDN上搜搜),所以电机转一圈的脉冲数应该是11×19×4=836;

假设编码器读取的脉冲数为x,速度,这里的t等于100ms,相当于Speed=p*0.0119617224880383;

这样就可以写速度计算函数了

2. 速度计算代码

 注意:得出来的值一定要是float型的,这样更加精确!

float Speed_calculate(int y)
{
	float value_2;
	value_2 = (float)y * 0.0119617224880383;		//100ms
	return value_2;
}

这个时候就可以把这个函数写到定时器中断函数里面了,这样就可以计算出速度。

这里还是要提醒一下,此函数计算出来的速度的单位是r/s,想要转换成m/s就需要轮子的直径,大家都会算,再写一个计算函数罢了。文章来源地址https://www.toymoban.com/news/detail-772679.html

到了这里,关于(STM32)PWM输出控制电机旋转并且使用编码器读取脉冲数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • AS5600步进电机编码器(原理图+pcb+stm32控制代码)

    AS5600是一个易于编程的磁性旋转位置传感器,具有高分辨率的12位模拟或PWM输出。这种非接触式系统测量一个直径磁化的轴上磁铁的绝对角度。 引脚如下图 他有两种供电模式:5V和3.3V 我们为了和stm32F103C8T6单片机的电压一致,也使用3.3V供电,然后开始画PCB。 使用嘉立创EDA画

    2024年02月03日
    浏览(52)
  • 基于STM32F103C8T6使用Arduino IDE编程闭环控制4个带编码器的有刷直流电机

    题记:标题有点长了,纯粹为了方便被检索到~~~本贴主要用于支持南方科技大学SDIM学院工业设计专业大三综合项目移动底盘学习,也是我自己按照费曼学习方法的一次尝试,用从底层搭建一个机器人底盘来复习自动控制原理。         由于工业设计专业没有开设嵌入式课程

    2024年02月05日
    浏览(45)
  • STM32PWM控制直流电机

    PWM介绍 脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用 微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽 度的控制 PWM的原理: 假定定时器工作在向上计数 PWM 模式,且当 CNT=CCRx 时输出 1。那么就可以

    2024年02月08日
    浏览(46)
  • STM32第八课:PWM控制直流电机

            TB6612FNG是一款新型驱动器件,能独立双向控制2个直流电机,它具有很高的集成度,同时能提供足够的输出能力,运行性能和能耗方面也具有优势因此在集成化、小型化的电机控制系统中,它可以作为理想的电机驱动器件。                           

    2024年03月24日
    浏览(49)
  • 一文搞懂如何使用STM32驱动直流电机(普通PWM输出和L298N、高级定时器输出带死区双通道互补PWM和IR2110S及自举电路、H桥电路和电机正反转)

    笔者从开始接触嵌入式单片机开始,就和驱动电机相伴而走。从最开始的直接买L298N驱动模块直接驱动直流电机,到现在自己设计PCB电路板驱动直流电机,可以说是和电机驱动共同成长了。现在笔者将这一过程的收获记录下来,希望对大家有所帮助。 不用详细了解L298N芯片和

    2024年02月02日
    浏览(45)
  • 搭建stm32电机控制代码框架(五)——Stm32CubeMx配置PWM

    采样配置完成后,进行PWM的配置。PWM的生成依赖于STM32的TIM1定时器,其功能完备如下图所示,电机控制中主要应用其PWM生成功能。 我们当前阶段的目标是生成占空比为50%的三路PWM,其开关频率为10kHz。 那么开始CubeMx的配置,依据stm32f405数据手册中地址总线部分内容,TIM1是挂

    2024年02月16日
    浏览(54)
  • 搭建stm32电机控制代码框架(四)——单路PWM生成

    STM32中单路PWM的生成一般是基于某一个通用定时器,本次小实验选取TIM2通用定时器,选择PA5作为PWM端口输出。配置步骤如下: 第一步:配置外部晶振与基本的时钟,如下图所示。 (1)选择外部晶振  (2)配置时钟树,如下图所示,TIM2的时钟总线挂在ABP1上,此时时钟源为

    2024年02月07日
    浏览(46)
  • STM32-微项目03-pwm控制直流电机运行

    一,项目目标 实现利用STM32F103C8T6+TB6612,输出不同占空比输出的PWM波,从而实现电机不同转速的运行、正反转的功能;    二、硬件涉及 1,STM32核心板 2,TB6612直流电机驱动模块 3,直流电机  三、硬件接线涉及 1,先看TB6612直流电机驱动模块: ①驱动模块是具备两路PWM输入

    2024年02月04日
    浏览(47)
  • 用STM32定时器中断产生PWM控制步进电机

    控制步进电机可以使用PWM、定时器中断、延时,这里用的就是 定时器中断来让它转动。 板子用的是正点原子的STM32F103 mini板,驱动器是DM420(DM420驱动器资料),用开关电源供电,电机就是普通的42步进电机,步距角为1.8°,虽然按照图片来看它是个蠕动泵。如下图 PUL+——PB0,

    2023年04月09日
    浏览(48)
  • 【STM32学习】——定时器输出比较功能&PWM脉宽调制&通用/高级定时器输出比较通道&舵机/直流电机简介&PWM驱动呼吸灯/舵机/直流电机代码实操

    声明:学习笔记根据b站江科大自化协stm32入门教程编辑,仅供学习交流使用!

    2024年02月03日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包