在开发树莓派小车时,需要使用树莓派来控制直流电机,树莓派无法直接驱动直流电机,需要使用直流电机驱动芯片,最常见的有L298N,比如一般的TT马达用这个驱动就足够,但是如果是大功率电机,它的发热量和死区都比较大,因为我买的小车是大功率直流电机,所以使用的是TB6612FNG驱动芯片,它是东芝的芯片,能驱动两个直流电机,或者并联四个直流电机(左前和左后并联,右前和右后并联)。本文介绍如何使用TB6612FNG来驱动直流电机。

TB6612FNG驱动芯片

   TB6612FNG驱动芯片结构如下图:

▲ TB6612FNG结构图

     我买回来,实物是这样的,体积很小。

▲ 装车图

   针脚很多, 对照TB6612FNG结构图,各针脚功能如下。

左边针脚 功能 右边针脚 功能
PWMA A电机控制信号输入 VM 电机驱动电压输入端(4.5-15V)
AIN2 A电机输入端2 VCC 逻辑电平输入端(2.7-5.5V)
AIN1 A电机输入端1 GND 接地
STBY 正常工作/待机 状态控制端 AO1 A电机输出端1
BIN1 B电机输入端1 AO2 A电机输出端2
BIN2 B电机输入端2 BO2 B电机输出端2
PWMB B电机控制信号输入 BO1 B电机输出端1
GND 接地 GND 接地

 

说明一下:

    左边PWMA,PWMB,是通过PWM来控制A,B电机的转速,0停止,1最大转速。左边的接口全部要接树莓派的GPIO接口,STBY,用来控制AB两个电机的待机和工作,输入高电压1表示工作,0表示待机。

    右边的VM接外接电源,用来直接给电机驱动电压,我这里买的是12V, 3A的18650锂电池,也不贵。这里需要注意的是,因为我买的是大功率电机,型号为:JGB37-520 DC12V200RPM 电机,所以最好用12V电压,这样转的快。

▲ 12V,18650充电电池,DC充电头可以用面包板电源充电,外接DC转接头可以引出正负极

   最好是电机跟树莓派的电源分开。VM接到树莓派的5V接口上,树莓派的GND接口跟电机的驱动电源一起接地,这里可以通过面包板实现共同接地。

  AIO1, AIO2, BIO1,BIO2,分别接电机的正负极,这里需要调试一下找到电机的正负极。方法为,直接将电机的正负极,接到电机驱动电源的正负极上,观察电机的转动方向。记下正负极,正极接到AIO1,BIO1上,负极接到AIO2,BIO2上。

    以上操作在树莓派断电下进行,同时在操作电机和驱动电源的时候,注意不到将杜邦线碰到树莓派电路板。

    控制电机的转速,可以通过PWMA和PWMB的值来控制A,B电机的转速。通过STBY的高低值,来控制待机和工作,通过AIO1,AIO2的高低值,来控制正反转。

  

▲ 通过树莓派GPIO接口,来输入不同的值来控制转速和运转方向

少废话,看代码

     写代码之前,先要布线,将电机驱动电源,树莓派,驱动电机,通过杜邦线连起来。

    

▲ 接线图及说明

   在.NET Core IoT的GitHub的源码里有DCMotor相关代码,我这里拿过来,首先是一个DCMotor抽象类,表示一个电机,因为电机类型有2针(只有IO1,IO2),3针的(除IO1和IO2之外,还有PWM控制转速):

/// <summary>
    /// Direct current (DC) motor
    /// </summary>
    public abstract class DCMotor : IDisposable
    {
        private const int DefaultPwmFrequency = 50;
        private bool _shouldDispose;

        /// <summary>
        /// Constructs generic <see cref="DCMotor"/> instance
        /// </summary>
        /// <param name="controller"><see cref="GpioController"/> related with operations on pins</param>
        /// <param name="shouldDispose">True to dispose the Gpio Controller</param>
        protected DCMotor(GpioController controller, bool shouldDispose)
        {
            _shouldDispose = shouldDispose;
            Controller = controller;
        }

        public abstract void Break();


        /// <summary>
        /// Gets or sets the speed of the motor. Range is -1..1 or 0..1 for 1-pin connection.
        /// 1 means maximum speed, 0 means no movement and -1 means movement in opposite direction.
        /// </summary>
        public abstract double Speed { get; set; }

        /// <summary>
        /// <see cref="GpioController"/> related with operations on pins
        /// </summary>
        protected GpioController Controller
        {
            get;
            set;
        }

        /// <summary>
        /// Disposes the <see cref="DCMotor"/> class
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases the resources used by the <see cref="DCMotor"/> instance.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_shouldDispose)
                {
                    Controller?.Dispose();
                    Controller = null;
                }
            }
        }



        /// <summary>
        /// Creates <see cref="DCMotor"/> instance which allows to control speed in both directions.
        /// </summary>
        /// <param name="speedControlChannel"><see cref="PwmChannel"/> used to control the speed of the motor</param>
        /// <param name="directionPin">First pin used to control the direction of the motor</param>
        /// <param name="otherDirectionPin">Second pin used to control the direction of the motor</param>
        /// <param name="controller"><see cref="GpioController"/> related to <paramref name="directionPin"/> and <paramref name="otherDirectionPin"/></param>
        /// <param name="shouldDispose">True to dispose the Gpio Controller</param>
        /// <returns><see cref="DCMotor"/> instance</returns>
        /// <remarks>
        /// When speed is non-zero the value of <paramref name="otherDirectionPin"/> will always be opposite to that of <paramref name="directionPin"/>.
        /// <paramref name="speedControlChannel"/> should be connected to enable pin of the H-bridge.
        /// <paramref name="pinIn1"/> should be connected to H-bridge input corresponding to one of the motor inputs.
        /// <paramref name="pinIn2"/> should be connected to H-bridge input corresponding to the remaining motor input.
        /// Connecting motor directly to GPIO pin is not recommended and may damage your board.
        /// </remarks>
        public static DCMotor Create(PwmChannel speedControlChannel, int pinIn1, int pinIn2, GpioController controller = null, bool shouldDispose = true)
        {
            if (speedControlChannel == null)
            {
                throw new ArgumentNullException(nameof(speedControlChannel));
            }

            if (pinIn1 == -1)
            {
                throw new ArgumentOutOfRangeException(nameof(pinIn1));
            }

            if (pinIn2 == -1)
            {
                throw new ArgumentOutOfRangeException(nameof(pinIn2));
            }

            return new DCMotor3Pin(
                speedControlChannel,
                pinIn1,
                pinIn2,
                controller,
                shouldDispose);
        }

        /// <summary>
        /// Creates <see cref="DCMotor"/> instance which allows to control speed in both directions.
        /// </summary>
        /// <param name="speedControlPin">Pin used to control the speed of the motor with software PWM (frequency will default to 50Hz)</param>
        /// <param name="directionPin">First pin used to control the direction of the motor</param>
        /// <param name="otherDirectionPin">Second pin used to control the direction of the motor</param>
        /// <param name="controller"><see cref="GpioController"/> related to <paramref name="speedControlPin"/>, <paramref name="directionPin"/> and <paramref name="otherDirectionPin"/></param>
        /// <param name="shouldDispose">True to dispose the Gpio Controller</param>
        /// <returns><see cref="DCMotor"/> instance</returns>
        /// <remarks>
        /// When speed is non-zero the value of <paramref name="pinIn2"/> will always be opposite to that of <paramref name="pinIn1"/>
        /// PWM pin <paramref name="speedControlPin"/> should be connected to enable pin of the H-bridge.
        /// <paramref name="pinIn1"/> should be connected to H-bridge input corresponding to one of the motor inputs.
        /// <paramref name="pinIn2"/> should be connected to H-bridge input corresponding to the remaining motor input.
        /// Connecting motor directly to GPIO pin is not recommended and may damage your board.
        /// </remarks>
        public static DCMotor Create(int speedControlPin, int pinIn1, int pinIn2, GpioController controller = null, bool shouldDispose = true)
        {
            if (speedControlPin == -1)
            {
                throw new ArgumentOutOfRangeException(nameof(speedControlPin));
            }

            if (pinIn1 == -1)
            {
                throw new ArgumentOutOfRangeException(nameof(pinIn1));
            }

            if (pinIn2 == -1)
            {
                throw new ArgumentOutOfRangeException(nameof(pinIn2));
            }

            controller = controller ?? new GpioController();
            return new DCMotor3Pin(
                new SoftwarePwmChannel(speedControlPin, DefaultPwmFrequency, 0.0, controller: controller),
                pinIn1,
                pinIn2,
                controller,
                shouldDispose);
        }
    }

    因为我们是三针电机,所以把DCMotor3Pin弄过来:

internal class DCMotor3Pin : DCMotor
    {
        private PwmChannel _pwm;
        private int _pinIN1;
        private int _pinIN2;
        private double _speed;

        public DCMotor3Pin(
            PwmChannel pwmChannel,
            int pinIn1,
            int pinIn2,
            GpioController controller,
            bool shouldDispose)
            : base(controller ?? new GpioController(), controller == null ? true : shouldDispose)
        {
            if (pwmChannel == null)
            {
                throw new ArgumentNullException(nameof(pwmChannel));
            }

            _pwm = pwmChannel;

            _pinIN1 = pinIn1;
            _pinIN2 = pinIn2;

            _speed = 0;

            _pwm.Start();

            Controller.OpenPin(_pinIN1, PinMode.Output);
            Controller.Write(_pinIN1, PinValue.Low);

            Controller.OpenPin(_pinIN2, PinMode.Output);
            Controller.Write(_pinIN2, PinValue.Low);
        }

        /// <summary>
        /// Gets or sets the speed of the motor.
        /// Speed is a value from -1 to 1.
        /// 1 means maximum speed, signed value changes the direction.
        /// </summary>
        public override double Speed
        {
            get
            {
                return _speed;
            }
            set
            {
                double val = Math.Clamp(value, -1.0, 1.0);

                if (_speed == val)
                {
                    return;
                }

                if (val == 0.0)
                {
                    Controller.Write(_pinIN1, PinValue.Low);
                    Controller.Write(_pinIN2, PinValue.Low);
                }
                else if (val > 0.0)
                {
                    Controller.Write(_pinIN1, PinValue.High);
                    Controller.Write(_pinIN2, PinValue.Low);
                }
                else
                {
                    Controller.Write(_pinIN1, PinValue.Low);
                    Controller.Write(_pinIN2, PinValue.High);
                }

                _pwm.DutyCycle = Math.Abs(val);

                _speed = val;
            }
        }

        public override void Break()
        {
            _pwm.DutyCycle = 0;
            Controller.Write(_pinIN1, PinValue.High);
            Controller.Write(_pinIN2, PinValue.High);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _speed = 0.0;
                _pwm?.Dispose();
                _pwm = null;
            }

            base.Dispose(disposing);
        }
    }

    可以看到,通过设置Speed大于小于等于0来,设置IO1和IO2的值,进而来控制正转反转和停止,通过pwm的大小,来控制速度。

    以上代表单个电机,要控制小车的前进后退,我们需要两个电机,所以,新建一个DCCar,表示一辆小车,代码如下:

  public class DCCar : IDisposable
    {
        private int standbyGpio;
        private DCMotor leftMotor;
        private DCMotor rightMotor;
        GpioController controller;
        public DCCar(int standBy, int pmwA, int aIn1, int aIn2, int pmwb, int bIn1, int bIn2)
        {
            standbyGpio = standBy;
            leftMotor = DCMotor.Create(pmwA, aIn1, aIn2);
            rightMotor = DCMotor.Create(pmwb, bIn1, bIn2);
            controller = new GpioController(PinNumberingScheme.Logical);
            //make stand by stop
            controller.OpenPin(standbyGpio, PinMode.Output);
        }

        public void MoveForward(double speed)
        {
            double s = Math.Clamp(Math.Abs(speed), 0, 1);
            leftMotor.Speed = s;
            rightMotor.Speed = s;
            controller.Write(standbyGpio, PinValue.High);
        }
        public void MoveBack(double speed)
        {
            double s = Math.Clamp(Math.Abs(speed), 0, 1);
            leftMotor.Speed = -s;
            rightMotor.Speed = -s;
            controller.Write(standbyGpio, PinValue.High);
        }

        public void TurnLeft(double speed)
        {
            double s = Math.Clamp(Math.Abs(speed), 0, 1);
            leftMotor.Speed = -s;
            rightMotor.Speed = s;
            controller.Write(standbyGpio, PinValue.High);
        }

        public void TurnRight(double speed)
        {
            double s = Math.Clamp(Math.Abs(speed), 0, 1);
            leftMotor.Speed = s;
            rightMotor.Speed = -s;
            controller.Write(standbyGpio, PinValue.High);
        }

        public void Stop()
        {
            leftMotor.Break();
            rightMotor.Break();
            controller.Write(standbyGpio, PinValue.Low);
        }

        public void Dispose()
        {
            Stop();
            leftMotor?.Dispose();
            leftMotor = null;
            rightMotor?.Dispose();
            rightMotor = null;
            controller?.Dispose();
            controller = null;
        }
    }

   可以看到,左右转是通过控制左右两个电机的正反转来实现的。

   现在,在Main函数里,来实例化DCCar,并通过一定的延时来控制前进,后退,左转右转。

  static void TestCar()
        {
            using (DCCar car = new DCCar(20, 23, 12, 16, 18, 13, 25))
            {
                while (true)
                {
                    Console.WriteLine("move forward");
                    DoAction(car.MoveForward);
                    Thread.Sleep(1250);
                    Console.WriteLine("move back");
                    DoAction(car.MoveBack);
                    Thread.Sleep(1250);
                    Console.WriteLine("turn left");
                    DoAction(car.TurnLeft);
                    Thread.Sleep(1250);
                    Console.WriteLine("turn right");
                    DoAction(car.TurnRight);
                    Thread.Sleep(1250);
                    car.Stop();
                    Thread.Sleep(1250);
                }
            }
        }

        static void DoAction(Action<double> a)
        {
            for (double i = 0.1; i <= 1; i += 0.1)
            {
                a(i);
                Thread.Sleep(1250);
            }
        }

   发布,部署,运行,如果一切正常,就会看到,小车会先前进,大约1250毫秒后后退,然后左转,然后右转,然后停止,然后又再次循环。

    能驱动DC马达,就是制作小车的第一步,本文讲解了如何使用TB6612FNG来驱动大功率电机,如果是TT马达,则可以用用L298N,这里就不展示了,希望给大家带来帮助。

 

参考

Arduino 和 TB6612FNG 驱动直流电机