使用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;

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

一、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接电机电源线(两轮);

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

 

 

提醒:

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,速度Speed= \frac{\frac{p}{836}}{t},这里的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就需要轮子的直径,大家都会算,再写一个计算函数罢了。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用STM32控制PWM输出驱动电机旋转并读取编码器脉冲数

    发表评论