PWM驱动直流电机

目录

一、实验目的

二、实验要求

三、硬件部分

1. 主控模块

2. 电器驱动模块

3. 直流电机

 四、软件部分

1. 主程序

2.定时器输出PWM波原理

 五、实验总结


一、实验目的

  1. 掌握MSP432 PWM波的输出配置
  2. 掌握直流电机驱动的方法

二、实验要求

  1. 按键控制小车的前进方向。
  2. 实现小车加速前进,减速后退运动。

三、硬件部分

        本实验系统总体框图如图3.1所示,系统的电路原理图如图3.2所示。系统采用MSP432P401R作为系统主控,通过改变输出PWM波的占空比以及GPIO引脚的输出高低电平,经H桥电路电机驱动模块提供电机转动所需的电压和电流,以实现对电机的驱动,进而控制TI-RSLK小车的前进方向以及行驶速度。

PWM驱动直流电机_第1张图片

图3.1  系统总体框图

PWM驱动直流电机_第2张图片

 图3.2 系统电路原理图

1. 主控模块

        系统采用MSP432P401R芯片作为系统主控,MSP432是在MSP430单片机基础上,以32位ARM Cortex-M4F为内核的MCU,具有浮点单元和存储器保护单元,频率最高达48MHz。具有低功耗,片上外设资源丰富的特点。其引脚图如图3.3所示。

PWM驱动直流电机_第3张图片

 图3.3 MSP432P401R引脚图

2. 电器驱动模块

        系统采用DRV8838 H桥电机驱动器作为电机驱动模块,以实现对本实验中直流电机的驱动。该输出驱动器由配置为H桥的N沟道功率MOSFET组成,以驱动电机绕组。DRV8838电机驱动器的引脚图如图3.4所示,且DRV883X电机驱动器应用电路图如图3.5所示。

PWM驱动直流电机_第4张图片

PWM驱动直流电机_第5张图片

        本实验中,主控芯片P1.6、P1.7引脚分别连接左右两个DRV8838模块的PH管脚,通过IO端口输出高低电平控制直流电机的转向,进而控制小车的前行方向;主控芯片的P3.6、P3.7引脚分别连接左右两个DRV8838模块的nSLEEP管脚,通过IO端口输出高低电平控制直流电机的休眠,进而控制小车的启停;主控芯片的P2.6、P2.7引脚分别连接左右两个DRV8838模块的EN管脚,通过输出不同占空比的PWM波控制输送到电机的电功率,进而控制直流电机的转速,最后实现控制小车的行驶速度。

3. 直流电机

        系统采用如图3.6所示的直流有刷电机,该电机自带AB双相增量式磁性霍尔编码器,且电机响应频率为1-10KHz。根据洛伦兹力定律,电机将电能转换为机械能,这种机械动力为车轮提供扭矩,使车轮转动以实现小车的移动。

PWM驱动直流电机_第6张图片

 四、软件部分

1. 主程序

PWM驱动直流电机_第7张图片

 图4.1 主程序流程图

2.定时器输出PWM波原理

        实验采用定时器输出占空比可变的PWM波来实现对直流电机的驱动。MSP432P041R的Timer_A模块可主要分为两个部分:主计数器和捕获/比较模块。主计数器负责定时、计时/计数,计数值(TAR寄存器的值)被送到各个捕获/比较模块中,它们可在无需CPU干预情况下根据触发触发条件和计数器值自动完成某些测量和输出功能。当捕获/比较模块工作在比较模式时,每个捕获/比较模块将不断地将自身的比较值寄存器与主计数器的计数值进行比较,一旦相等,就将自动改变定时器输出引脚的输出电平,从而可在无需CPU干预的情况下输出PWM波、可变单稳态脉冲、移向方波、相位调制等常用波形。在本实验中,定时器A采用增减计数工作模式,输出采用翻转/复位模式,定时器输出PWM波的原理图如图4.2所示。

   

PWM驱动直流电机_第8张图片

图4.2 定时器输出PWM波的原理图

        图4.2是小车右轮直流电机的PWM驱动原理图,分析该图可知,定时器输出PWM波的周期由TA0CCR0寄存器比较值决定,占空比由TA0CCR0寄存器比较值和TA0CRR3寄存器比较值的比值决定。因此通过改变TA0CCR3寄存器的值,即可改变右直流电机的转速,进而控制右轮的转速。

#include "msp.h"
#include "../inc/Clock.h"

void PWM_Init(uint16_t period, uint16_t Rduty, uint16_t Lduty);
void PWM_Rduty(uint16_t Rduty);
void PWM_Lduty(uint16_t Lduty);
void Motor_Init(void);
void Motor_Stop(void);
void Motor_Forward(uint16_t leftDuty, uint16_t rightDuty);
void Motor_Right(uint16_t leftDuty, uint16_t rightDuty);
void Motor_Left(uint16_t leftDuty, uint16_t rightDuty);
void Motor_Backward(uint16_t leftDuty, uint16_t rightDuty);
void route1(uint16_t leftDuty,uint16_t rightDuty);
void route2(void);
void circle_R(uint16_t leftDuty,uint16_t rightDuty);
void circle_L(uint16_t leftDuty,uint16_t rightDuty);

/**
 * main.c
 */
void main(void)
{
    uint8_t button;

	WDT_A->CTL = WDT_A_CTL_PW | WDT_A_CTL_HOLD;		// stop watchdog timer
    Clock_Init48MHz();
    LaunchPad_Init();//initialize the LEDS and buttons
    Motor_Init();
    Motor_Stop();
    while(1){
        while(LaunchPad_Input() == 0);  //wait for the input of the button(P1.0,P1.4)
        while(LaunchPad_Input()){       //wait until releasing
            button = LaunchPad_Input();
            if(button==0x03)            //when P1.0 and P1.4 both input,
                while(LaunchPad_Input());//reserve the status until releasing, to avoid mistakes
        }
        switch(button){
        case 0x01:                      //P1.1 input
            LaunchPad_Output(0x02);     //green
            Motor_Left(5000,5000);
            Clock_Delay1ms(2000);
            break;
        case 0x02:                      //P1.4 input
            LaunchPad_Output(0x04);     //blue
            Motor_Right(5000,5000);
            Clock_Delay1ms(2000);
            break;
        case 0x03:                      //P1.1 and P1.4 both input
            LaunchPad_Output(0x07);     //white
            route2();
            Clock_Delay1ms(2000);
            break;
        }
        Motor_Stop();
        LaunchPad_Output(0x01);         //red
        }

    }

//***************************PWM_Init*******************************
// PWM outputs on P2.6(TA0.3), P2.7(TA0.4)
// Inputs:  period : the value of TA0CCR0 //the Period of PWM is 2*period*8*(1/12M)
//          Rduty  : the value of TA0CCR3 //the duty cycle of PWM_P2.6 is Rduty/period
//          Lduty  : the value of TA0CCR4 //the duty cycle of PWM_P2.7 is Lduty/period
// Outputs: none
// SMCLK = 48MHz/4 = 12 MHz, 83.33ns
// Counter counts up to TA0CCR0 and back down
// Let Timerclock period T = 8/12MHz = 666.7ns
// period of P7.3 squarewave is 4*period*666.7ns
// P2.6=1 when timer equals TA0CCR3 on way down, P2.6=0 when timer equals TA0CCR3 on way up
// P2.7=1 when timer equals TA0CCR4 on way down, P2.7=0 when timer equals TA0CCR4 on way up
void PWM_Init(uint16_t period, uint16_t Rduty, uint16_t Lduty){
    if(Rduty >= period) return; // bad input
    if(Lduty >= period) return; // bad input
    P2->DIR |= 0xC0;          // P2.6, P2.7 output
    P2->SEL0 |= 0xC0;         // P2.6, P2.7 Timer0A functions
    P2->SEL1 &= ~0xC0;        // P2.6, P2.7 Timer0A functions
    TIMER_A0->CCTL[0] = 0x0080;      // CCI0 toggle
    TIMER_A0->CCR[0] = period;       // Period is 2*period*8*83.33ns is 1.333*period
    TIMER_A0->EX0 = 0x0000;          // divide by 1
    TIMER_A0->CCTL[3] = 0x0040;      // CCR3 toggle/reset
    TIMER_A0->CCR[3] = Rduty;        // CCR3 duty cycle is Rduty/period
    TIMER_A0->CCTL[4] = 0x0040;      // CCR4 toggle/reset
    TIMER_A0->CCR[4] = Lduty;        // CCR4 duty cycle is Lduty/period
    TIMER_A0->CTL = 0x02F0;        // SMCLK=12MHz, divide by 8, up-down mode
      //TA0CTL register
      // bit  mode
      // 9-8  10    TASSEL, SMCLK=12MHz
      // 7-6  11    ID, divide by 8
      // 5-4  11    MC, up-down mode
      // 2    0     TACLR, no clear
      // 1    0     TAIE, no interrupt
      // 0          TAIFG
}

//change the duty
void PWM_Rduty(uint16_t Rduty){
  if(Rduty >= TIMER_A0->CCR[0]) return; // bad input
  TIMER_A0->CCR[3] = Rduty;        // CCR13 duty cycle is Rduty/period
}

void PWM_Lduty(uint16_t Lduty){
  if(Lduty >= TIMER_A0->CCR[0]) return; // bad input
  TIMER_A0->CCR[4] = Lduty;        // CCR4 duty cycle is Lduty/period
}

//*******************Motor_Init*******************************
// Initialize GPIO pins for output
// P1.6 : R_DIR, to control the direction of the right motor(0-forward, 1-backward)
// P1.7 : L_DIR, to control the direction of the left motor(0-forward, 1-backward)
// P3.6 : R_nSLP, to enable or disable the right driver(0-disable, 1-enable)
// P3.7 : L_nSLP, to enable or disable the left  driver(0-disable, 1-enable)
// Input: none
// Output: none
void Motor_Init(void){
  // write this as part of Lab 13
    P1->SEL0 &= ~0xC0;
    P1->SEL1 &= ~0xC0;  // configure as GPIO
    P1->DIR |= 0xC0;    // make P1.6 & P1.7 out
    P1->OUT &= ~0xC0;

    P3->SEL0 &= ~0xC0;
    P3->SEL1 &= ~0xC0;  // configure as GPIO
    P3->DIR |= 0xC0;    // make P3.6 & P3.7 out
    P3->OUT &= ~0xC0;   // low current sleep mode
}

void Motor_Stop(void){
    P1->OUT &= ~0xC0;
    P2->OUT &= ~0xC0;   // off
    P3->OUT &= ~0xC0;   // low current sleep mode
}

void Motor_Forward(uint16_t leftDuty, uint16_t rightDuty){
    P1->OUT &= ~0xC0;   // set direction of motors
    PWM_Init(15000, rightDuty, leftDuty);     //Period of the PWM is 2*15000*8/12M=20ms
    P3->OUT |= 0xC0;    // activate motors
}

void Motor_Right(uint16_t leftDuty, uint16_t rightDuty){
    P1->OUT &= ~0x80;   // left wheel forward
    P1->OUT |= 0x40;    // right wheel backward
    PWM_Init(15000, rightDuty, leftDuty);
    P3->OUT |= 0xC0;    // activate motors
}

void Motor_Left(uint16_t leftDuty, uint16_t rightDuty){
    P1->OUT &= ~0x40;   // right wheel forward
    P1->OUT |= 0x80;    // left wheel backward
    PWM_Init(15000, rightDuty, leftDuty);
    P3->OUT |= 0xC0;    // activate motors
}

void Motor_Backward(uint16_t leftDuty, uint16_t rightDuty){
    P1->OUT |= 0xC0;   // set direction of motors
    PWM_Init(15000, rightDuty, leftDuty);
    P3->OUT |= 0xC0;    // activate motors
}
//*********************route************************************
/*
 * route 1:forward->backward->right->forward->left->forward->stop
 */
void route1(uint16_t leftDuty,uint16_t rightDuty){
    Motor_Forward(leftDuty, rightDuty);
    Clock_Delay1ms(1000);
    Motor_Backward(leftDuty, rightDuty);
    Clock_Delay1ms(1000);
    Motor_Right(leftDuty, rightDuty);
    Clock_Delay1ms(200);
    Motor_Forward(leftDuty, rightDuty);
    Clock_Delay1ms(1000);
    Motor_Left(leftDuty, rightDuty);
    Clock_Delay1ms(200);
    Motor_Forward(leftDuty, rightDuty);
    Clock_Delay1ms(1000);
    Motor_Stop();
}

/*
 * route2:run by diffent speed(20%->40%->80%)
 */
void route2(void){
    int rightDuty = 2000;
    int leftDuty = 2000;
    P1->OUT &= ~0xC0;   // direction of motors:forward
    PWM_Init34(10000, rightDuty, leftDuty);     //period=10000*(8/12M)s , duty=20%
    P3->OUT |= 0xC0;    // activate motors
    Clock_Delay1ms(1000);
    PWM_Duty3(rightDuty*2); //40%
    PWM_Duty4(leftDuty*2);
    Clock_Delay1ms(1000);
    PWM_Duty3(rightDuty*4); //80%
    PWM_Duty4(leftDuty*4);
    Clock_Delay1ms(1000);

    P1->OUT |= 0xC0;   // set direction of motors:backward
    Clock_Delay1ms(1000);
    PWM_Duty3(rightDuty*2); //40%
    PWM_Duty4(leftDuty*2);
    Clock_Delay1ms(1000);
    PWM_Duty3(rightDuty); //20%
    PWM_Duty4(leftDuty);
}

///*
// * spin in circle
// */
void circle_R(uint16_t leftDuty,uint16_t rightDuty){
    P1->OUT &= ~0x80;   // left wheel forward
    P1->OUT |= 0x40;    // right wheel backward
    PWM_Init(15000, rightDuty, leftDuty);
    //P3->OUT |= 0xC0;    // activate motors
    P3->OUT &= ~0x40;   //active left motor and close right motor
    P3->OUT |= 0x80;
    Clock_Delay1ms(3000);
    Motor_Stop();
}


 五、实验总结

        本实验中使用的智能小车是差分驱动的,即左右车轮分别使用两个电机驱动,然而由于电机自身的驱动差异性,以及运转过程中路况的影响,都会导致左右轮速度不同,从而在直线行驶过程中出现“偏移”现象。该问题很难通过开环控制消除,因此我们需要引入闭环控制,通过编码器监测左右两个电机转速,反馈并控制输出PWM波新占空比,以实现直线行驶。

你可能感兴趣的:(MSP432-RSLK小车,单片机,嵌入式硬件)