精华内容
下载资源
问答
  • 编码器电机
    千次阅读
    2022-04-28 11:29:27

            本次选用的编码器电机为13线的霍尔编码器电机,电机减速比为30:1,转动一圈输出13*30=390个脉冲。轮胎直径为75mm,轮胎周长为pi*d=3*75=225mm.定时器采用四倍频计数,则一圈输出390*4=1560个脉冲。具体编码器知识这里就不多说了。

             根据测速原理:假设编码器输出的脉冲数为N,而电机转动一圈输出1569个脉冲,转动一圈轮子将前进225mm。那输出脉冲数为N时前进的距离就应该为225*(N/1560)mm,再除以时间及可得速度。

    下面为具体代码:

    encoder.c文件

    #include "encoder.h"
    
    void Encoder_TIM2_Init(void)
    {
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开时钟
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    	
    	GPIO_InitTypeDef GPIO_InitStruct;							//配置IO口
    	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    	GPIO_Init(GPIOA, &GPIO_InitStruct);
    	
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;				//定时器初始化
    	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    	TIM_TimeBaseInitStruct.TIM_Period = 65535;
    	TIM_TimeBaseInitStruct.TIM_Prescaler = 0;
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
    	
    	//定时器编码器模式初始化
    	TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
    	
    	TIM_ICInitTypeDef TIM_ICInitStruct; 						//输入捕获单元配置
    	TIM_ICStructInit(&TIM_ICInitStruct);
    	TIM_ICInitStruct.TIM_ICFilter = 10;
    	TIM_ICInit(TIM2, &TIM_ICInitStruct);
    	
    	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    	
    	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    	
    	TIM_SetCounter(TIM2, 0);
    	
    	TIM_Cmd(TIM2, ENABLE);
    }
    
    void Encoder_TIM4_Init(void)
    {
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    	
    	GPIO_InitTypeDef GPIO_InitStruct;
    	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    	GPIO_Init(GPIOB, &GPIO_InitStruct);
    	
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    	TIM_TimeBaseInitStruct.TIM_Period = 65535;
    	TIM_TimeBaseInitStruct.TIM_Prescaler = 0;
    	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStruct);
    	
    	TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
    	
    	
    	TIM_ICInitTypeDef TIM_ICInitStruct; 
    	TIM_ICStructInit(&TIM_ICInitStruct);
    	TIM_ICInitStruct.TIM_ICFilter = 10;
    	TIM_ICInit(TIM4, &TIM_ICInitStruct);
    	
    	TIM_ClearFlag(TIM4, TIM_FLAG_Update);
    	
    	TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
    	
    	TIM_SetCounter(TIM4, 0);
    	
    	TIM_Cmd(TIM4, ENABLE);
    }
    
    int Read_Spead(int TIMx)			//读取编码器输出脉冲数
    {
    	int value_1;
    	switch(TIMx)
    	{
    		case 2:value_1 = (short)TIM_GetCounter(TIM2);TIM_SetCounter(TIM2, 0);break;
    		case 4:value_1 = (short)TIM_GetCounter(TIM4);TIM_SetCounter(TIM4, 0);break;
    		default:value_1 = 0;
    	}
    	return value_1;
    }
    
    void TIM2_IRQHander(void)
    {
    	if(TIM_GetITStatus(TIM2, TIM_IT_Update) == 1)
    	{
    		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    	}
    }
    
    void TIM4_IRQHander(void)
    {
    	if(TIM_GetITStatus(TIM4, TIM_IT_Update) == 1)
    	{
    		TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    	}
    }
    

            我使用的是简单的delay一下来采集定时器捕获的编码器脉冲数,不过我建议使用定时器中断来处理编码器采集。此函数只采集右轮脉冲进行计算

    主函数循环体内函数:

    while(1)
    	{
    		delay_s(1);
    		{
    			uint16_t right = Read_Spead(2);//采集右轮脉冲数
    			displacement = 0.225 * (right / 1560);//计算位移
    			speed = displacement;//由于我设置的为延时一秒就不用除时间
    			OLED_Float(0, 0, speed, 4);//通过OLED显示速度
    			set_PWM(999);//设置电机PWM
    			TIM_SetCounter(TIM2, 0);//下一次一秒计数前再一次将计数清零
    		}
    	}

    更多相关内容
  • 基于TI drivers库函数,实现编码器电机定速、定角控制示例
  • 37编码器电机

    2019-04-12 17:18:44
    Autodesk inventor 零件模型 xxx.ipt格式 37编码器电机
  • MovidriveB型驱动器软件配置带编码器电机PPT教案.pptx
  • 编码器电机速度控制固件库版,使用的是STM32F103单片机,项目用过
  • 编码器(测电机正反转)程序,通过脉冲的先后,测电机正、反转与静止。 编码器(测电机正反转)程序,通过脉冲的先后,测电机正、反转与静止。
  • 编码器程序,可用于平衡小车和倒立摆等电机的编码,非常简洁明了。
  • 利用定时器产生PWM控制电机,并且利用单片机读取增量式编码器的信息控制电机旋转角度
  • stm32,AB编码器,pid调节
  • 编码器电机测速 部分参考:https://blog.csdn.net/lzzzzzzm/article/details/119416134 其他参考部分见图片水印 1. 编码器种类及原理 常见的编码器有两种,分别为光电编码器和霍尔编码器 1.1 光电编码器 如图,...

    编码器电机测速


    部分参考:https://blog.csdn.net/lzzzzzzm/article/details/119416134
    其他参考部分见图片水印

    1. 编码器种类及原理

    常见的编码器有两种,分别为光电编码器和霍尔编码器

    1.1 光电编码器

    如图,打孔码盘随电机进行旋转。每当光线穿过圆孔,输出电平就会改变,如此产生方波,测量方波的频率即可测出电机转速

    1.2 霍尔编码器

    现在的电机基本上都是霍尔编码器

    霍尔编码器圆盘上分布有磁极,当圆盘随电机主轴转动时,会输出两路相位差90°的方波,用这两路方波可测出电机的转速和转向

    2. 常用测速方法

    2.1 倍频技术

    霍尔编码器会输出两路方波信号,如果只在通道A的上升沿计数,那就是1倍频;通道A的上升、下降沿计数,那就是2倍频;如果在通道A、B的上升、下降沿计数,那就是4倍频

    使用倍频可以最大程度地利用两路信号,提高测速的灵敏度

    下面说的三种测速方法只是在软件计算上的区别,硬件上是没有改变的

    2.1 M法测速

    M法测速(又叫周期测量法)是统计一段时间内的脉冲数,再根据电机转一圈产生的脉冲数来计算转速的测速方法

    设转速为n,r/s;测量时间为 T 0 T_0 T0,s; T 0 T_0 T0时间内的脉冲数为 M 0 M_0 M0;电机转一圈产生的脉冲数为C;则转速计算公式为
    n = M 0 C T 0 n=\frac{M_0}{CT_0} n=CT0M0
    M 0 M_0 M0很大,即转速快时,这个方法测得精度和平稳性都很好,但当 M 0 M_0 M0很小,速度改变带来的 M 0 M_0 M0变化很小,即转速慢时算出的误差就很大。所以M法测速适用于高转速场景

    2.2 T法测速

    T法测速(又叫频率测量法)是这样操作的:是指先建立一个频率已知且固定的高频脉冲,当编码器读到一个信号,开始对高频脉冲进行计数,编码器第二个信号到来后,停止计数。根据对高频脉冲计数的次数、高频脉冲频率和电机转一圈编码器产生的脉冲数进行速度计算

    设转速为n,r/s;两个脉冲的时间间隔为 T E T_E TE,s;电机转一圈产生的脉冲数为C; F 0 F_0 F0为编码器输出脉冲的频率,Hz; M 1 M_1 M1为高频脉冲的计数值,则转速计算公式为

    n = 1 C T E = F 0 C M 1 n=\frac{1}{CT_E}=\frac{F_0}{CM_1} n=CTE1=CM1F0
    其中 T E T_E TE M 1 M_1 M1 F 0 F_0 F0有如下关系
    T E = M 1 F 0 T_E=\frac{M_1}{F_0} TE=F0M1
    理解: C T E CT_E CTE为当前速度下电机转一圈需要的时间,1圈除以1圈所需要的时间即可得到转速

    T E T_E TE很大即转速很慢时,T法测速有较高的精度和平稳度,但当 T E T_E TE很小,即转速很快时,速度改变带来的 T E T_E TE变化很小,算出的误差就很大。所以T法测速适用于低转速场景

    2.3 M/T法测速

    M/T法综合了M法和T法的优势,计算公式如下

    n = F 0 M 0 C M 1 n=\frac{F_0M_0}{CM_1} n=CM1F0M0

    理解:公式中只有 M 0 M_0 M0 T 0 T_0 T0时间内的脉冲数)、 M 1 M_1 M1(高频脉冲的计数值)为变量。当转速快时, M 1 M_1 M1变小, M 0 M_0 M0变大,相当于M法;当转速慢时, M 1 M_1 M1变大, M 0 M_0 M0变小,相当于T法。

    3. STM32实现编码器测速

    3.1 CubeMax配置

    为了进行测速,我们一共需要3个定时器,作用分别是:①输出PWM控制电机;②编码器输入进行测速;③计时,确定每次测速的时间间隔

    具体配置如下:

    TIM2:编码器输入定时器

    这里开启了两个通道计数,就是上文倍频技术的4倍频

    编码器模式下的定时器其实是个计数器,在编码器的脉冲到来时,Counter会相应地加和减,正转时加,反转时减,溢出后到达另一个极端值,比如说向上计数到达20001时会变成0

    TIM3:PWM输出定时器

    设置初始PWM频率为100Hz

    TIM4:计时间隔定时器

    设定为10Hz即1秒计算10次速度

    最后要开启中断,并保证编码器定时器的中断优先级高于计时间隔定时器的中断优先级,避免编码器输入被间隔计时中断

    其他基础配置不再赘述

    3.2 接线

    编码器电机、电机驱动(这里用的L298n)、STM32、电源(可以是12V电池)的接线如下

    编码器电机电机驱动STM32电机驱动供电
    VMVCC
    VDDPWM1PA6
    VSSPWM2PA7
    3V33V3
    GNDGNDGNDGND
    编码器通道1PA0
    编码器通道2PA1

    3.3 代码编写

    encoder.h中的内容

    #ifndef _ENCODER_H_
    
    #define _ENCODER_H_
    
      
    
    #include "stm32f1xx.h"
    
      
    
    //电机1的编码器输入引脚
    
    #define MOTO1_ENCODER1_PORT GPIOA
    
    #define MOTO1_ENCODER1_PIN  GPIO_PIN_0
    
    #define MOTO1_ENCODER2_PORT GPIOA
    
    #define MOTO1_ENCODER2_PIN  GPIO_PIN_1
    
      
    
    //定时器号
    
    #define ENCODER_TIM htim2
    
    #define PWM_TIM     htim3
    
    #define GAP_TIM     htim4
    
      
    
    #define MOTOR_SPEED_RERATIO 45u    //电机减速比
    
    #define PULSE_PRE_ROUND 11 //一圈多少个脉冲
    
    #define RADIUS_OF_TYRE 34 //轮胎半径,单位毫米
    
    #define LINE_SPEED_C RADIUS_OF_TYRE * 2 * 3.14
    
      
    
    #define RELOADVALUE __HAL_TIM_GetAutoreload(&ENCODER_TIM)    //获取自动装载值,本例中为20000
    
    #define COUNTERNUM __HAL_TIM_GetCounter(&ENCODER_TIM)        //获取编码器定时器中的计数值
    
      
    
    typedef struct _Motor
    
    {
    
        int32_t lastCount;   //上一次计数值
    
        int32_t totalCount;  //总计数值
    
        int16_t overflowNum; //溢出次数
    
        float speed;         //电机转速
    
        uint8_t direct;      //旋转方向
    
    }Motor;
    
      
    
    #endif
    

    encoder.c中的内容

    #include "encoder.h"
    
      
    
    Motor motor1;
    
      
    
    void Motor_Init(void)
    
    {
    
        HAL_TIM_Encoder_Start(&ENCODER_TIM, TIM_CHANNEL_ALL);      //开启编码器定时器
    
        __HAL_TIM_ENABLE_IT(&ENCODER_TIM,TIM_IT_UPDATE);           //开启编码器定时器更新中断,防溢出处理
    
        HAL_TIM_Base_Start_IT(&GAP_TIM);                       //开启10ms定时器中断
    
        HAL_TIM_PWM_Start(&PWM_TIM, TIM_CHANNEL_2);            //开启PWM
    
        HAL_TIM_PWM_Start(&PWM_TIM, TIM_CHANNEL_1);            //开启PWM
    
        __HAL_TIM_SET_COUNTER(&ENCODER_TIM, 10000);                //编码器定时器初始值设定为10000
    
        motor1.lastCount = 0;                                   //结构体内容初始化
    
        motor1.totalCount = 0;
    
        motor1.overflowNum = 0;                                  
    
        motor1.speed = 0;
    
        motor1.direct = 0;
    
    }
    
      
    
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//定时器回调函数,用于计算速度
    
    {
    
        if(htim->Instance==ENCODER_TIM.Instance)//编码器输入定时器溢出中断                    
    
        {      
    
            if(COUNTERNUM < 10000) motor1.overflowNum++;       //如果是向上溢出
    
            else if(COUNTERNUM >= 10000) motor1.overflowNum--; //如果是向下溢出
    
            __HAL_TIM_SetCounter(&ENCODER_TIM, 10000);             //重新设定初始值
    
        }
    
        else if(htim->Instance==GAP_TIM.Instance)//间隔定时器中断,是时候计算速度了
    
        {
    
            motor1.direct = __HAL_TIM_IS_TIM_COUNTING_DOWN(&ENCODER_TIM);//如果向上计数(正转),返回值为0,否则返回值为1
    
            motor1.totalCount = COUNTERNUM + motor1.overflowNum * RELOADVALUE;//一个周期内的总计数值等于目前计数值加上溢出的计数值
    
            motor1.speed = (float)(motor1.totalCount - motor1.lastCount) / (4 * MOTOR_SPEED_RERATIO * PULSE_PRE_ROUND) * 10;//算得每秒多少转
    
            //motor1.speed = (float)(motor1.totalCount - motor1.lastCount) / (4 * MOTOR_SPEED_RERATIO * PULSE_PRE_ROUND) * 10 * LINE_SPEED_C//算得车轮线速度每秒多少毫米
    
            motor1.lastCount = motor1.totalCount; //记录这一次的计数值
    
        }
    
    }
    

    这里用的是4倍频的M法测速,如果想使用T法测速,则需要对算法进行更改,以后再写吧

    3.4 结果

    现在将测得的速度值输出到串口,就可以看到电机的实时转速了

    展开全文
  • 基于旋转编码器电机的调速,利用PWM对电机进行全面调速
  • 6线正交编码器电机(带AB相) 其中硬件接线为: PWMA —— PA8 AIN1 —— PB14 AIN2 —— PB15 STBY —— 5V 编码器A相 —— PA1 编码器B相 —— PA0 STM32定时器资源分配: 定时器1(TIM1):产生PWM波,作为TB...

    一、硬件及接线说明

    本实验所基于的硬件分别为:

    • STM32F103C8T6 主控板
    • TB6612FNG 直流电机驱动模块
    • 6线正交编码器电机(带AB相)

    其中硬件接线为:

    • PWMA —— PA8
    • AIN1 —— PB14
    • AIN2 —— PB15
    • STBY —— 5V
    • 编码器A相 —— PA1
    • 编码器B相 —— PA0

    STM32定时器资源分配:

    • 定时器1(TIM1):产生PWM波,作为TB6612的输入,控制电机进行调速;
    • 定时器2(TIM2):读取编码器的波形;
    • 定时器3(TIM3):产生周期为10ms的定时器中断,为控制系统提供稳定的时间基准。

    【说明】上述硬件平台和接线仅给读者提供参考,更换主控或接线方式,请自行对示例程序进行微调。本文对于编码器的工作原理不加赘述,对于其原理请读者自行查阅相关资料。

    二、速度闭环控制程序逻辑

    【说明】下述程序中 control.c 最为重要,包含了速度闭环控制器的详细代码。其他程序模块供读者初始化参考。

    main.c (主函数)

    u8 flag_Stop=1;     //停止标志位
    int Encoder;        //编码器的脉冲计数
    int moto;           //电机PWM变量
    int main(void)
     {	
    	 Stm32_Clock_Init(9);      //系统时钟设置
         delay_init();             //=====延时初始化
    	 LED_Init();               //=====初始化与 LED 连接的硬件接口
    	 uart_init(115200);        //=====初始化串口1
    	 MOTO_Init();              //初始化控制电机所需的IO
    	 pwm_Init(7199,0);         //初始化pwm输出
    	 Encoder_Init_TIM2();      //初始化计数器(定时器)
    	 TIM3_Int_Init(99,7199);   //10ms一次中断
    	 while(1)
    	 {
    	   printf("Encoder:%d \r\n",Encoder);
    	 }
     }
    

    moto.c (电机初始化相关函数)

    void MOTO_Init(void)//初始化控制电机所需的IO
    {
      GPIO_InitTypeDef GPIO_InitStruct;
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    	//PORTB12 13 14 15推挽输出
    	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
    	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOB,&GPIO_InitStruct);	
    }
    
    void pwm_Init(u16 arr,u16 psc) //初始化pwm输出引脚
    {	
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    	TIM_OCInitTypeDef TIM_OCInitStruct;
        GPIO_InitTypeDef GPIO_InitStruct;
    	
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);  //使能定时器1时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能GPIOA的时钟
    	
    	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;         //复用输出
    	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11;   //PA8 PA11
    	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA,&GPIO_InitStruct);
    	
    	TIM_TimeBaseInitStruct.TIM_Period = arr;                     //设定计数器自动重装值 
    	TIM_TimeBaseInitStruct.TIM_Prescaler  = psc;                 //设定预分频器
    	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
    	TIM_TimeBaseInitStruct.TIM_ClockDivision = 0;                //设置时钟分割
    	TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);              //初始化定时器
    	
    	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;               //选择PWM2模式
    	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;   //比较输出使能
    	TIM_OCInitStruct.TIM_Pulse = 0;                              //设置待装入捕获比较寄存器的脉冲值
    	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;       //设置输出极性
    	TIM_OC1Init(TIM1,&TIM_OCInitStruct);                         //初始化输出比较参数
    	
    	TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); //CH1使能预装载寄存器
    	 
    	TIM_CtrlPWMOutputs(TIM1,ENABLE);                 //高级定时器输出必须设置这句
    	
    	TIM_ARRPreloadConfig(TIM1, ENABLE);              //使能TIM1在ARR上的预装载寄存器
    	
    	TIM_Cmd(TIM1,ENABLE);                            //使能定时器1
    }
    

    encoder.c (编码器初始化函数)

    #include "encoder.h"
    /**************************************************************************
    函数功能:把TIM2初始化为编码器接口模式
    入口参数:无
    返回  值:无
    **************************************************************************/
    void Encoder_Init_TIM2(void)
    {
      TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
      TIM_ICInitTypeDef TIM_ICInitStructure;  
      GPIO_InitTypeDef GPIO_InitStructure;
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能定时器2的时钟
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PA端口时钟
    	
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;	//端口配置
      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_CenterAligned1;TIM向上计数  
      TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    	
      TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
    	
      TIM_ICStructInit(&TIM_ICInitStructure); //把TIM_ICInitStruct 中的每一个参数按缺省值填入
      TIM_ICInitStructure.TIM_ICFilter = 10;  //设置滤波器长度
      TIM_ICInit(TIM2, &TIM_ICInitStructure); //根据 TIM_ICInitStruct 的参数初始化外设	TIMx
     
      TIM_ClearFlag(TIM2, TIM_FLAG_Update);//清除TIM的更新标志位
      TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//使能定时器中断
    
      TIM_SetCounter(TIM2,0);//设置TIMx 计数器寄存器值
      TIM_Cmd(TIM2, ENABLE); //使能定时器2
    }
    
    //中断处理函数为空,清除中断标志位后结束中断
    void TIM2_IRQHandler(void)
    {
     if(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update)==SET)//溢出中断
     {
    	 TIM_ClearITPendingBit(TIM2,TIM_IT_Update);  //清除中断标志位 
     }
    }
    
    /**************************************************************************
    函数功能:单位时间读取编码器计数
    入口参数:定时器
    返回  值:速度值
    **************************************************************************/
    int Read_Encoder(u8 TIMX)//读取计数器的值
    {
      int Encoder_TIM;
    	switch(TIMX)
    	{
    	  case 2:Encoder_TIM=(short)TIM2->CNT; TIM2 -> CNT=0;  break;
    	  case 3:Encoder_TIM=(short)TIM3->CNT; TIM3 -> CNT=0;  break;
    	  case 4:Encoder_TIM=(short)TIM4->CNT; TIM4 -> CNT=0;  break;
    	  default: Encoder_TIM=0;
    	}
      return Encoder_TIM;
    }
    
    

    timer.c (定时器中断初始化函数)

    /**************************************************************************
    函数功能:定时中断初始化
    入口参数:arr:自动重装值  psc:时钟预分频数 
    返回  值:无
    **************************************************************************/
    void TIM3_Int_Init(u16 arr,u16 psc)
    {
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    	NVIC_InitTypeDef NVIC_InitStruct;
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
    	
    	TIM_TimeBaseInitStruct.TIM_Period = arr;     //重装载值
    	TIM_TimeBaseInitStruct.TIM_Prescaler = psc;  //预分频系数
    	TIM_TimeBaseInitStruct.TIM_ClockDivision =0; //时钟分割
    	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
    	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
    	
    	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);  //使能定时器中断
    	
    	NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;   //使能外部中断通道
    	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;   //使能外部中断通道
    	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级1
    	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;    //响应优先级3
    	NVIC_Init(&NVIC_InitStruct);
    
    	TIM_Cmd(TIM3,ENABLE);	  //使能定时器3
    }
    

    control.c (核心控制程序,此模块程序可供读者详细阅读)

    #include "control.h"
    int Target_velocity=50;  //设定速度控制的目标速度为50个脉冲每10ms
    
    int TIM3_IRQHandler(void)
    {
     if(TIM_GetFlagStatus(TIM3,TIM_FLAG_Update)==SET)
     {
         TIM_ClearITPendingBit(TIM3,TIM_IT_Update);       //===清除定时器3中断标志位
    	 Encoder=Read_Encoder(2);                         //取定时器2计数器的值
         Led_Flash(100);                                  //LED闪烁
    	 moto=Incremental_PI(Encoder,Target_velocity);    //===速度PID控制器
    	 Xianfu_Pwm();
         Set_Pwm(moto);
     }
     return 0;
    }
    /**************************************************************************
    函数功能:赋值给PWM寄存器
    入口参数:PWM
    返回  值:无
    **************************************************************************/
    void Set_Pwm(int moto)//赋值给PWM寄存器
    {
     if(moto>0) AIN1=0,   AIN2=1;
     else       AIN1=1,   AIN2=0;
     PWMA=myabs(moto);
    }
    /**************************************************************************
    函数功能:限制PWM赋值 
    入口参数:无
    返回  值:无
    **************************************************************************/
     void Xianfu_Pwm(void) //限制幅度的函数
     {
         int Amplitude=7100;  //===PWM满幅是7200 限制在7100
    	 if(moto<-Amplitude)  moto = -Amplitude;
    	 if(moto>Amplitude)   moto =  Amplitude;
     }
    /**************************************************************************
    函数功能:绝对值函数
    入口参数:int
    返回  值:unsigned int
    **************************************************************************/
    int myabs(int a) //取绝对值
    { 		   
    	 int temp;
    	 if(a<0)  temp=-a;  
    	 else temp=a;
    	 return temp;
    }
    /**************************************************************************
    函数功能:增量PI控制器
    入口参数:编码器测量值,目标速度
    返回  值:电机PWM
    根据增量式离散PID公式 
    pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)]
    e(k)代表本次偏差 
    e(k-1)代表上一次的偏差  以此类推 
    pwm代表增量输出
    在我们的速度控制闭环系统里面,只使用PI控制
    pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)
    **************************************************************************/
    int Incremental_PI (int Encoder,int Target)
    { 	
         float Kp=20,Ki=30;	
    	 static int Bias,Pwm,Last_bias;
    	 Bias=Encoder-Target;                //计算偏差
    	 Pwm+=Kp*(Bias-Last_bias)+Ki*Bias;   //增量式PI控制器
    	 Last_bias=Bias;	                 //保存上一次偏差 
    	 return Pwm;                         //增量输出
    }
    
    

    三、位置闭环控制程序逻辑

    【说明】下述程序中 control.c 最为重要,包含了速度闭环控制器的详细代码。其他程序模块供读者初始化参考。

    main.c

    u8 flag_Stop=1;     //停止标志位
    int Encoder,Position=10000;        //编码器的脉冲计数
    int moto;           //电机PWM变量
    int main(void)
     {	
    	 Stm32_Clock_Init(9);      //系统时钟设置
         delay_init();             //=====延时初始化
    	 LED_Init();               //=====初始化与 LED 连接的硬件接口
    	 uart_init(115200);        //=====初始化串口1
    	 MOTO_Init();              //初始化控制电机所需的IO
    	 pwm_Init(7199,0);         //初始化pwm输出
    	 Encoder_Init_TIM2();      //初始化计数器(定时器)
    	 TIM3_Int_Init(99,7199);   //10ms一次中断
    	 while(1)
    	 {
    	   printf("Encoder:%d Position:%d \r\n",Encoder,Position);
    	 }
     }
    

    moto.c

    void MOTO_Init(void)//初始化控制电机所需的IO
    {
        GPIO_InitTypeDef GPIO_InitStruct;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//PORTB12 13 14 15推挽输出
    	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
    	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOB,&GPIO_InitStruct);	
    }
    
    void pwm_Init(u16 arr,u16 psc) //初始化pwm输出引脚
    {	
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    	TIM_OCInitTypeDef TIM_OCInitStruct;
        GPIO_InitTypeDef GPIO_InitStruct;
    	
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);  //使能定时器1时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能GPIOA的时钟
    	
    	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;         //复用输出
    	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11;   //PA8 PA11
    	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA,&GPIO_InitStruct);
    	
    	TIM_TimeBaseInitStruct.TIM_Period = arr;                     //设定计数器自动重装值 
    	TIM_TimeBaseInitStruct.TIM_Prescaler  = psc;                 //设定预分频器
    	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
    	TIM_TimeBaseInitStruct.TIM_ClockDivision = 0;                //设置时钟分割
    	TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);              //初始化定时器
    	
    	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;               //选择PWM2模式
    	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;   //比较输出使能
    	TIM_OCInitStruct.TIM_Pulse = 0;                              //设置待装入捕获比较寄存器的脉冲值
    	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;       //设置输出极性
    	TIM_OC1Init(TIM1,&TIM_OCInitStruct);                         //初始化输出比较参数
    	
    	TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); //CH1使能预装载寄存器
    	 
    	TIM_CtrlPWMOutputs(TIM1,ENABLE);                 //高级定时器输出必须设置这句
    	
    	TIM_ARRPreloadConfig(TIM1, ENABLE);              //使能TIM1在ARR上的预装载寄存器
    	
    	TIM_Cmd(TIM1,ENABLE);                            //使能定时器1
    }
    
    
    

    encoder.c

    void Encoder_Init_TIM2(void)
    {
      TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
      TIM_ICInitTypeDef TIM_ICInitStructure;  
      GPIO_InitTypeDef GPIO_InitStructure;
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能定时器2的时钟
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PA端口时钟
    	
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;	//端口配置
      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_CenterAligned1;TIM向上计数  
      TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    	
      TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
    	
      TIM_ICStructInit(&TIM_ICInitStructure); //把TIM_ICInitStruct 中的每一个参数按缺省值填入
      TIM_ICInitStructure.TIM_ICFilter = 10;  //设置滤波器长度
      TIM_ICInit(TIM2, &TIM_ICInitStructure);//根据 TIM_ICInitStruct 的参数初始化外设	TIMx
     
      TIM_ClearFlag(TIM2, TIM_FLAG_Update);//清除TIM的更新标志位
      TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//使能定时器中断
    
      TIM_SetCounter(TIM2,10000);//设置TIMx 计数器寄存器值
      TIM_Cmd(TIM2, ENABLE); //使能定时器2
    }
    
    //中断处理函数为空,清除中断标志位后结束中断
    void TIM2_IRQHandler(void)
    {
     if(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update)==SET)//溢出中断
     {
    	 TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除中断标志位 
     }
    }
    
    /**************************************************************************
    函数功能:单位时间读取编码器计数
    入口参数:定时器
    返回  值:速度值
    **************************************************************************/
    int Read_Encoder(u8 TIMX)//读取计数器的值
    {
      int Encoder_TIM;
    	switch(TIMX)
    	{
    	    case 2:Encoder_TIM=(short)TIM2->CNT;  break;
    		case 3:Encoder_TIM=(short)TIM3->CNT;  break;
    		case 4:Encoder_TIM=(short)TIM4->CNT;  break;
    		default: Encoder_TIM=0;
    	}
      return Encoder_TIM;
    }
    
    

    timer.c

    #include "timer.h"
    /**************************************************************************
    函数功能:定时中断初始化
    入口参数:arr:自动重装值  psc:时钟预分频数 
    返回  值:无
    **************************************************************************/
    void TIM3_Int_Init(u16 arr,u16 psc)
    {
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    	NVIC_InitTypeDef NVIC_InitStruct;
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
    	
    	TIM_TimeBaseInitStruct.TIM_Period = arr;     //重装载值
    	TIM_TimeBaseInitStruct.TIM_Prescaler = psc;  //预分频系数
    	TIM_TimeBaseInitStruct.TIM_ClockDivision =0; //时钟分割
    	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
    	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
    	
    	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);  //使能定时器中断
    	
    	NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;   //使能外部中断通道
    	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;   //使能外部中断通道
    	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级1
    	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;    //响应优先级3
    	NVIC_Init(&NVIC_InitStruct);
    
    	TIM_Cmd(TIM3,ENABLE);	  //使能定时器3
    }
    

    control.c (核心控制程序,此模块程序可供读者详细阅读)

    #include "control.h"
    int Target_position=11000;    //初始值是10000,目标值是11000
    int TIM3_IRQHandler(void)
    {
     if(TIM_GetFlagStatus(TIM3,TIM_FLAG_Update)==SET)
     {
         TIM_ClearITPendingBit(TIM3,TIM_IT_Update);   //===清除定时器1中断标志位
    	 Encoder=Read_Encoder(2);                     //取定时器2计数器的值
         Led_Flash(100);                              //LED闪烁
    	 moto=Position_PID(Encoder,Target_position);    //===位置PID控制器
    	 Xianfu_Pwm();
         Set_Pwm(moto);
     }
     return 0;
    }
    /**************************************************************************
    函数功能:赋值给PWM寄存器
    入口参数:PWM
    返回  值:无
    **************************************************************************/
    void Set_Pwm(int moto)//赋值给PWM寄存器
    {
        if(moto>0) AIN1=0,   AIN2=1;
    	else      AIN1=1,   AIN2=0;
    	PWMA=myabs(moto);
    }
    /**************************************************************************
    函数功能:限制PWM赋值 
    入口参数:无
    返回  值:无
    **************************************************************************/
     void Xianfu_Pwm(void) //限制幅度的函数
     {
         int Amplitude=7100;  //===PWM满幅是7200 限制在7100
    	 if(moto<-Amplitude)  moto = -Amplitude;
    	 if(moto>Amplitude)   moto =  Amplitude;
     }
    /**************************************************************************
    函数功能:绝对值函数
    入口参数:int
    返回  值:unsigned int
    **************************************************************************/
    int myabs(int a) //取绝对值
    { 		   
    	 int temp;
    	 if(a<0)  temp=-a;  
    	 else temp=a;
    	 return temp;
    }
    /**************************************************************************
    函数功能:位置式PID控制器
    入口参数:编码器测量位置信息,目标位置
    返回  值:电机PWM
    根据位置式离散PID公式 
    pwm=Kp*e(k)+Ki*∑e(k)+Kd[e(k)-e(k-1)]
    e(k)代表本次偏差 
    e(k-1)代表上一次的偏差  
    ∑e(k)代表e(k)以及之前的偏差的累积和;其中k为1,2,,k;
    pwm代表输出
    **************************************************************************/
    int Position_PID (int Encoder,int Target)
    { 	
    	 float Position_KP=80,Position_KI=0.1,Position_KD=500;
    	 static float Bias,Pwm,Integral_bias,Last_Bias;
    	 Bias=Encoder-Target;                                    //计算偏差
    	 Integral_bias+=Bias;	                                 //求出偏差的积分
    	 Pwm=Position_KP*Bias+Position_KI*Integral_bias+Position_KD*(Bias-Last_Bias);       //位置式PID控制器
    	 Last_Bias=Bias;                                         //保存上一次偏差 
    	 return Pwm;                                             //增量输出
    }
    
    

    四、有关速度闭环控制与位置闭环控制的区别

    • 通常,我们一般在移动机器人的车轮控制上会用到速度闭环控制,在倒立摆上会用到位置闭环控制。细心的读者对比上述程序可以发现:速度闭环控制使用了PI控制器,位置闭环控制使用了PID控制器,这也是两者最大区别之一。
    • 完整的示例程序还在整理当中,整理完毕后会发布在GitHub上。
    展开全文
  • 利用单片机的外部中断0和旋转编码器来实现电机的测速,并通过12864液晶显示出数据
  • 该文件是:STM32F103C8T6平衡小车电机编码器原理图和PCB图。 1、用Altium Designer软件绘制。 2、包括原理图、PCB图、原理图相关器件库、PCB图相关器件库。 用该“STM32F103C8T6平衡小车电机编码器原理图和PCB图”做...
  • 直流电机与PID——编码器电机使用(一)

    千次阅读 多人点赞 2022-02-07 09:36:55
    文章目录直流电机与PID学习前言一、直流电机原理二、编码器电机1.减速器作用2.编码器电机接线3.编码器原理4.四倍频技术5.单片机采集编码器数据三、TB6612FNG驱动 前言 编码器电机与电机驱动TB6612使用 一、直流电机...

    直流电机与PID学习

    前言

    编码器电机与电机驱动TB6612使用

    一、直流电机原理

    示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

    我们可以调节施加在电机上面的直流电压大小,即可实现直流电机调速,改变电压极性,即可实现电机换向。

    二、编码器电机

    1.减速器作用

    电机转速通常都很高,于是在电机前加了电机减速器
    在这里插入图片描述
    作用:
    1、降低电机速度
    2、提高输出扭矩

    减速器通常分为3种:
    在这里插入图片描述

    2.编码器电机接线

    在这里插入图片描述

    3.编码器原理

    在这里插入图片描述
    在这里插入图片描述
    通过AB相输出相位差测速

    4.四倍频技术

    在这里插入图片描述
    只要是跳变沿就触发计数

    5.单片机采集编码器数据

    在这里插入图片描述
    例如STM32可以TIM1到TIM4通道1和通道2都可以进行四倍频计数

    三、TB6612FNG驱动

    在这里插入图片描述
    相较于L298N好了很多

    STBY高电平使能端
    红框是一路电机
    蓝框是一路电机
    由IN进行控制,pwm输出在0-100%

    展开全文
  • 基于pid控制编码器在直流电机控制转速和转角,。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
  • msp432p401r 编码器电机程序

    千次阅读 2021-09-25 00:39:39
    编码器电机的原理网上有很多,如果不懂可以看下这个博主的文章STM32应用(九)编码器及其测速原理、L298N电机驱动控制编码器电机_長空雁的博客-CSDN博客 在这里博主只讨论如何去用软件采集测速。 外部中断采集脉冲...
  • 分享一下霍尔编码器电机的使用与测速,我用的是25GA-310直流减速电机。先来看一下最基本的 接线方法——- ——S1与S2连接单片机上的S(我这里用的2号和3号,是中断引脚); ——G与V连接单片机上的G与V(对着接就行...
  • 在本例程中主要介绍STM32与编码器电机之间的搭配使用方法,关于具体的编码器原理只做简略介绍。 本例程中使用的电机为带霍尔编码器的减速电机,电机由三部分组成:减速器,电机以及霍尔编码器。 霍尔编码器工作原理...
  • 本博客主要介绍了编码器的类型和测速原理,初步认识了L298N电机驱动模块。为之后调电机和造车做了铺垫。如果有问题请加QQ1257663033。
  • 使用PID控制器设计基于Arduino的编码器电机 - Arduino专区 - 一板网电子技术论坛 设为首页收藏本站 登录 立即注册 找回密码 用其他账号登陆 快捷导航 切换风格 请 登录 后使用快捷导航没有帐号...
  • Micropython——基于PYB的霍尔编码器电机测速与使用

    千次阅读 多人点赞 2020-07-24 19:54:23
    文章目录编码器电机简介霍尔传感器编码器电机接线编码器读取代码 编码器电机简介 霍尔传感器 编码器电机接线 DM420驱动器 PYB 编码器A相 X1(TIM2,CH1) 编码器B相 X2(TIM2,CH2) 编码器5V V+(Vin) 编码...
  • 文章目录PID算法理论基础自己写的PID模块采用电机实战验证硬件使用基础算法应用测试控制系统分析关键问题分析测试代码算法应用 PID算法理论基础 这里可以直接参阅我写的这篇关于PID控制理论的博客,读者如果还是觉得...
  • 【平衡车】TB6612+编码器电机的使用

    千次阅读 2022-03-05 12:34:42
    一、编码器电机的使用 端口解释: 1.电线+、电机-:用于控制电机正/反转 2.编码器5V/GND:使编码器工作 3.编码器A/B相:连接在单片机,用于反馈电机运动状态,用于输出角度、速度数据,stm32的定时器有具备输入捕获...
  • 单片机仿真编码器电机测速程序

    千次阅读 2021-05-20 17:49:01
    /***********头文件声明************/#include "reg52.h"//此文件中定义了单片机的一些特殊功能寄存器/***********数据类型声明***********/typedef unsigned int u16;//对数据类型进行声明定义typedef unsigned ...
  • 可以使用的通过STM32hal库,应用Cube进行配置的定时器编码器工作模式,且能正常读出编码器的数值
  • 百度网盘链接 芯片型号:STM32F103RCT6 包含多份源码:不带系统、带RT-Thread系统源码 ...1. 编码器电机驱动 2. 麦轮解算 3. PS2遥控控制 4. IMU数据解算 5.手机控制APP 6. 硬件原理图 7. 芯片资料 8. 串口控制小车指令
  • Proteus实现编码器电机转速及方向检测

    万次阅读 多人点赞 2020-06-04 09:38:56
    Proteus实现编码器电机转速及方向检测原理图代码 原理图 控制编码电机采用的是最简单的开关控制正反电压的输入,滑动变阻器控制电机转速,也可以采用数字IC去控制电机转速以及方向,如L298N电机驱动芯片。 代码 /* ...
  • MovidriveB型驱动器软件配置带编码器电机精讲.pptx
  • MovidriveB型驱动器软件配置带编码器电机学习教案.pptx

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,872
精华内容 4,748
关键字:

编码器电机

友情链接: location.zip