1. 程式人生 > >STM32控制兩路直流電機_1

STM32控制兩路直流電機_1

手頭上有一個差分驅動的小車,使用兩個直流電機驅動,要實現小車的在給定速度下運動,完成直線行駛,轉向,加速,剎車等複雜運動。
使用的電機是12v供電的直流電機,帶編碼器反饋,這樣就可以採用閉環速度控制,這裡電機使用PWM驅動,速度控制框圖如下:
這裡寫圖片描述
由以上框圖可知,STM32通過定時器模組輸出PWM波來控制兩個直流電機的轉動,通過改變PWM佔空比的大小可以改變電機的轉速,由於我們的控制目標是實現電機執行在速度範圍內任意給定的速度,這裡就需要採用閉環控制的思想,通過編碼器獲取電機的實時轉速,通過與給定速度做差,將偏差作為PID控制器的輸入,通過PID控制改變PWM佔空比的大小,從而使電機的速度執行在給定的速度上。
這裡使用的電機驅動晶片為TB6612,該晶片可以十分方便的驅動兩個直流電機的執行,其驅動邏輯表如下:
這裡寫圖片描述


AIN1,AIN2的不同組合可以實現電機的正反轉和停車,PWMA為PWM的輸入引腳,通過輸入不同的佔空比可以改變電機轉速的快慢。BIN1,BIN2,PWMB是控制另一路電機的引腳。
首先我們需要利用STM32的定時器模組輸出兩路PWM波,這是使電機轉起來的第一步。初始化PWM:

//初始化PWM引腳
void motorPWMPin_init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
    GPIO_InitStructure.GPIO
_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_11 ;//TIM1_Chn_1,TIM1_Chn_2 GPIO_Init(GPIOE,&GPIO_InitStructure);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_TIM1); GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_TIM1); } //初始化PWM void motorPWM_init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrecture; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE); TIM_TimeBaseInitStrecture.TIM_Period = 400;/*PWM's frequency is 20KHz*/ TIM_TimeBaseInitStrecture.TIM_Prescaler =21-1;//將TIM1的時鐘頻率設定為8MHz TIM_TimeBaseInitStrecture.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStrecture.TIM_CounterMode = TIM_CounterMode_Up;//定時器向上計數 TIM_TimeBaseInitStrecture.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStrecture); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High ; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; TIM_OC1Init(TIM1,&TIM_OCInitStructure); TIM_OC2Init(TIM1,&TIM_OCInitStructure); // TIM_Cmd(TIM1,ENABLE); TIM_CtrlPWMOutputs(TIM1,ENABLE); }

然後初始化電機控制引腳,程式如下:

//初始化電機控制引腳
void motorCtrlPin_init(void)
{
   GPIO_InitTypeDef  GPIO_InitStructure;
   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);

    //PE7,PE8控制電機A,PE9,PE10控制電機B
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_10;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
   GPIO_Init(GPIOE, &GPIO_InitStructure);
}

需要注意的是設定PWM輸出引腳時要講引腳複用到定時器TIM1,而電機控制引腳只需要設定成簡單的推輓輸出模式即可。
接著我們需要使用兩個定時器的編碼器功能用於讀取電機的實時轉動速度,這裡我使用的是定時器3和定時器4.
這裡的編碼器是精度較低的霍爾感應式編碼器,但是基本滿足控制精度的要求,驅動程式碼如下:

void encoderA_init(void)
{
    GPIO_InitTypeDef          GPIO_InitStructure;
    NVIC_InitTypeDef          NVIC_InitStructure;
    TIM_TimeBaseInitTypeDef   TIM_TimeBaseStructure;
    TIM_ICInitTypeDef         TIM_ICInitStructure;

    /*CLOCK Enable*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);    //PC6,PC7

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//複用引腳模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;    //100MHz
  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; 
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //無上下拉

        /*Configure PC6,PC7 as encoder A,B Input*/
    GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3);
  GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM3);

  GPIO_Init(GPIOC,&GPIO_InitStructure); //initialize PORTC

    /* Timer configuration in Encoder mode */
    /* Enable the TIM3 Update Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =0x01;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_TimeBaseStructure.TIM_Prescaler = 0;   //不分頻
  TIM_TimeBaseStructure.TIM_Period = 65535;  //設定為最大
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up ; //向上計數  
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising   , TIM_ICPolarity_Rising   );//上升沿計數
  TIM_ICStructInit(&TIM_ICInitStructure);
  TIM_ICInitStructure.TIM_ICFilter = 10;//設定濾波係數
  TIM_ICInit(TIM3, &TIM_ICInitStructure);
    TIM_ClearFlag(TIM3, TIM_FLAG_Update);      //清除更新中斷
  TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //使能更新中斷
  TIM3->CNT = 0;//將計數值設為0
    TIM_Cmd(TIM3, ENABLE);//enable TIM3
    printf("Encoder_A initializztion is OK\n");
}

TIM4的編碼器模式配置與此相似,不再贅述。
小車的運動控制和轉速控制PID演算法,請見下節;