PWM驱动直流电机

目录

一、实验目的

二、实验要求

三、硬件部分

1. 主控模块

2. 电器驱动模块

3. 直流电机

 四、软件部分

1. 主程序

2.定时器输出PWM波原理

 五、实验总结


一、实验目的

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

二、实验要求

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

三、硬件部分

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

图3.1  系统总体框图

 图3.2 系统电路原理图

1. 主控模块

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

 图3.3 MSP432P401R引脚图

2. 电器驱动模块

        系统采用DRV8838 H桥电机驱动器作为电机驱动模块,以实现对本实验中直流电机的驱动。该输出驱动器由配置为H桥的N沟道功率MOSFET组成,以驱动电机绕组。DRV8838电机驱动器的引脚图如图3.4所示,且DRV883X电机驱动器应用电路图如图3.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。根据洛伦兹力定律,电机将电能转换为机械能,这种机械动力为车轮提供扭矩,使车轮转动以实现小车的移动。

 四、软件部分

1. 主程序

 图4.1 主程序流程图

2.定时器输出PWM波原理

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

   

图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波新占空比,以实现直线行驶。

### PWM信号控制直流电机的运行原理 PWM(Pulse Width Modulation,脉宽调制)是一种通过调节电信号占空比来控制设备的技术。对于直流电机而言,PWM信号能够改变施加在其上的平均电压,从而实现对电机转速的有效调控[^1]。 具体来说,当PWM信号作用于直流电机时,其工作过程如下: - **占空比定义**:PWM信号中的高电平持续时间与整个周期的比例称为占空比。如果占空比较高,则高电平的时间较长,电机两端获得的平均电压更高;反之亦然。 - **电压变化影响**:由于直流电机的速度与其所受电压成正比关系,因此通过调整PWM信号的占空比即可间接改变电机两端的平均电压,进而达到调节转速的目的[^3]。 --- ### 硬件连接方式 为了利用PWM信号驱动直流电机,通常需要以下硬件组件: - 微控制器(如Arduino、STM32等) - 功率放大模块(如L298N、TB6612FNG等H桥驱动器) 以下是典型的硬件连接方案: 1. 将微控制器的一个GPIO引脚配置为PWM输出端口,并将其连接至功率放大模块的使能(Enable)或PWM输入接口。 2. 把直流电机接入功率放大模块对应的电机接线端子上。 3. 提供合适的电源供电给功率放大模块和直流电机。 例如,在基于Arduino平台的情况下,可以使用`analogWrite()`函数生成PWM信号并发送到指定引脚。 --- ### 软件实现方法 #### Arduino代码示例 下面是一个简单的Arduino程序,用于演示如何通过PWM信号控制直流电机: ```cpp const int motorPin = 9; // 定义PWM输出引脚 int dutyCycle = 0; // 占空比初始值 void setup() { pinMode(motorPin, OUTPUT); // 设置引脚为输出模式 } void loop() { for (dutyCycle = 0; dutyCycle <= 255; dutyCycle++) { // 增大占空比 analogWrite(motorPin, dutyCycle); delay(20); // 每次增加等待一段时间 } for (dutyCycle = 255; dutyCycle >= 0; dutyCycle--) { // 减小占空比 analogWrite(motorPin, dutyCycle); delay(20); // 每次减少等待一段时间 } } ``` 上述代码展示了如何逐步增大再减小PWM信号的占空比,从而使直流电机加速后再减速。 --- ### PID闭环控制系统简介 除了基本的开环控制外,还可以引入PID算法构建闭环控制系统以提高精度。该系统会实时监测实际转速并与目标设定值比较,计算误差并通过比例(P)、积分(I)、微分(D)三个部分综合处理后调整PWM占空比[^2]。 这种做法特别适用于那些对外界干扰敏感或者追求稳定性能的应用场景,比如机器人移动底盘或是工业自动化生产线等领域。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值