DC motor control with doubled outputs

Community guides and tutorials
Post Reply
User avatar
Lukas
Posts: 75
Joined: Tue Dec 18, 2018 3:44 pm

DC motor control with doubled outputs

Post by Lukas » Fri Sep 04, 2020 4:06 pm

CONTROLLINO MEGA offers 12 outputs driven by Half-bridges. These outputs may be used to run the DC motor directly. As all these outputs also support PWM, the speed of the motor can be controlled by standard Arduino function AnalogWrite.
But - what if the maximum current defined for one CONTROLLINO output is not enough? Then one wants to drive the motor from two interconnected outputs. This solution is possible from electrical side, but cannot be controlled by AnalogWrite from software side. It is necessary to use outputs which are connected to the same micrcontroller port to be able to control them really synchronously.

I have used TimerThree library to run the Timer3 to have interrupt once per 100 microseconds. All the rest is done in the interrupt service routine.

Please see my code below. Plus some photos and ocsilloscope pictures (pictures were taken when the D6 and D7 outputs were not interconnected - just to see if they are really synchronous).

The speed can be simply controlled from Serial Monitor (-20 ... 0 .. 20).

Enjoy!

Lukas

Code: Select all

#include <TimerThree.h>

#include <Controllino.h>  /* Usage of CONTROLLINO library allows you to use CONTROLLINO_xx aliases in your sketch. */

/*
  CONTROLLINO - DC motor control MAXI, Version 01P01
  The sketch is relevant only for CONTROLLINO variant MAXI!

  Please, see https://www.arduino.cc/en/Reference/PortManipulation for some theoretical background.

  IMPORTANT INFORMATION!
  Please, select proper target board in Tools->Board->Controllino MAXI before Upload to your CONTROLLINO.
  (Please, refer to https://github.com/CONTROLLINO-PLC/CONTROLLINO_Library if you do not see the CONTROLLINOs in the Arduino IDE menu Tools->Board.)

  https://controllino.biz/

  (Check https://github.com/CONTROLLINO-PLC/CONTROLLINO_Library for the latest CONTROLLINO related software stuff.)
*/

volatile int gTargetSpeed = 0;    /* may be set from 0 = stop, to 20 = full speed */
volatile int gCurrentSpeed = 0;
volatile int gTargetDirection = 0;      /* may be set to 0 or 1 */
volatile int gCurrentDirection = 0;      

String readString;

void setup() {

  /* DC motor is connect to 4 digital outputs. 
     To be able to source more current each pole is connected to two CONTROLLINO outputs D4 with D5 to (+) and D6 with D7 to (-) */
  /* Please note that there must be used Half-Bridge outpus (D0 to D11) and all outputs must be at the same microcontroller port to 
     keep precise timing. */
     
  PORTH = PORTH & B10000111;  /* sets Digital Outputs in one shot to LOW  Port H - pins 3,4,5,6 */
  DDRH = DDRH | B01111000;  /* logical "1" (HIGH) in the "direction" register configures the appropriate pin as an output */

  /* configure Timer3 to generate interrupt */
  Timer3.initialize(100);  /* period 100 microseconds - leads to frequency 10 kHz, with 20 speed steps we have PWM speed of 500 Hz */
  Timer3.attachInterrupt(pwmTickRoutine); /* attach interrupt service routine to this source of interrupts */

  Serial.begin(9600);
}

// the loop function runs over and over again forever
void loop() {

  while (Serial.available()) {
    char c = Serial.read();  //gets one byte from serial buffer
    readString += c; //makes the String readString
    delay(2);  //slow looping to allow buffer to fill with next character
  }

  if (readString.length() >0) {
    Serial.println(readString);  //so you can see the captured String
    int n = readString.toInt();  //convert readString into a number
    int dir = 0;
    if (n < 0) {dir = 1;} else {dir = 0;}
    n = abs(n);
    if (n > 20) {n = 20;}
    /* Critical section - BEGIN */  
    noInterrupts();
      /* gTargetSpeed and gDirection may be modified only inside the critical section */
      gTargetSpeed = n;
      gTargetDirection = dir;
    interrupts();
    /* Critical section - END */

    readString="";
  }
   
}

void pwmTickRoutine( void )
{
  static int mPWMCounter = 0; /* counts permanently from 0 to 255 */
  mPWMCounter ++;

  if (mPWMCounter > 20) 
  {
    mPWMCounter = 0;
    /* smoothly change the speed */
    if (gCurrentSpeed > gTargetSpeed) {gCurrentSpeed--;}
    if (gCurrentSpeed < gTargetSpeed) {gCurrentSpeed++;}
    if (gCurrentDirection != gTargetDirection)
      {
        /* switch off the power for both */
        PORTH = PORTH & B10000111;  /* sets Digital Outputs in one shot to LOW  */
        gCurrentDirection = gTargetDirection;
      }
  }

  if (mPWMCounter <= gCurrentSpeed && gCurrentSpeed != 0)
  {
    /* switch on the proper direction */
    if (gCurrentDirection == 0)
    {
      /* switch on one side */
      PORTH = PORTH | B01100000;  /* sets Digital Outputs in one shot to HIGH  */
    }
    else 
    {
      PORTH = PORTH | B00011000;  /* sets Digital Outputs in one shot to HIGH  */
    }
  }
  else 
  {
    /* switch off the power for the remaining part of the PWM pulse */
    PORTH = PORTH & B10000111;  /* sets Digital Outputs in one shot to LOW  */
  }

  return;
}

/* END OF THE SKETCH */
Image Image Image Image Image

Post Reply