Motor drivers and arduino

Arduino projects on the go
User avatar
Mike_K
Posts: 669
Joined: 16 Feb 2018, 06:35
Location: Hertfordshire

Re: Motor drivers and arduino

Post by Mike_K »

Hi Max

I've used quite a few TB6612FNG and I was initially the same as you, I assumed that when using PWM you needed to freewheel in the "off" state, but having read the datasheet for the TI DRV8838 (where there is no freewheel, only "brake") it explains why, so as long as you are using a PWM frequency above a few hundred Hz, then braking is correct. If you are trying to "unstick" a small dc motor, like on a model train, with a pwm frequency of say 75Hz, then freewheeling may be the better option.

And I don't use the PWM inputs on the TB6612FNG, just the normal AIN1/AIN2 and BIN1/BIN2 inputs to save Arduino outputs (I just leave PWMA/PWMB unconnected), why use 6 connections when 4 will do? The Toshiba datasheet recommends this method if you can generate PWM for both AIN1/AIN2 and BIN1/BIN2, which an Arduino can do, but some older microcontrollers can't. The pwm inputs are also used if you have the pwm generated by external circuitry, not the microcontroller. So I don't think you will have to change your code at all, just ensure when reversing or stopped, take both AIN1/AIN2 HIGH (or BIN1/BIN2).

I've not used any other protective circuitry on any of Tobe's Rand LR2 and HR1 galloping ghost actuators that I use and I've had no problems. The TB6612FNG is rated to 15V max, so you are OK directly on a 12V lead-acid battery, I notice that the L9110 is only rated to 12V max. A fully charged lead-acid battery goes to 13.6V, I wonder if this could be some of your problem with the L9110 getting damaged? I see on some sellers websites they recommend 9V maximum and 12V not to be exceeded for the L9110.

I've only spent 5 minutes looking at your Arduino sketch and while neatly written, it will run relatively slowly especially if you are running the serial debugging code. In my experience using the serial port at 9600 baud, is virtually useless for debugging when doing accurate timings with ppm pulses as it adds such a delay to the code - each character takes just over 1mS to transmit, all the time stopping anything else running. In fact, nearly all of the functions you've used in your code are "blocking" which really slows things down. What do I mean by blocking? Well, code in the loop() runs one after the other to the bottom and then repeat, but things like the pulseIn() wait for the ppm pulse to arrive and program execution only start again once the ppm pulse finishes. If you call pulseIn() while the ppm pulse is high, it has to wait for a complete 20mS for the start of the next ppm pulse to start timing. I know an extra 20mS doesn't sound much, but it all adds up. When I first started programming Arduino, I never found it very accurate as any interrupts in the background will stop it counting (such as the millis(), micros(), analogWrite() etc) and used other timing methods. For an idea, look at Phil_G or my galloping ghost recoder sketches, we use an interrupt based method. viewtopic.php?f=86&t=1102

Also, I know analogWrite() is easy to use, but it limits the pwm frequency to 490Hz (if I remember correctly), if you learnt how to use the TCNT0/1/2 you can choose pwm frequencies in the KHz, I use above 20KHz pmw on most motors so that any noise/sound the motor makes is above the frequency we can hear. There are many tutorials on the web/YouTube, so there is little point in repeating it here.

Cheers

Mike
MaxZ
Posts: 330
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Motor drivers and arduino

Post by MaxZ »

Hi Mike,
Thanks for your extensive reply. Good to know that my code will be suitable to run the TB6612FNG without major changes, and I will not need extra protective measures.

Yes, I realise that there's stuff in my code that will slow things down, but then again the spindle system that it operates is very slow, so no real issues there.
The serial communication is still in there to get some feedback from my friend if the system misbehaves, which I need to support him as he is only just starting with Arduino.

Anyway, I have asked him to comment the serials out when all is set. As for the pulseIn() for both spindles, I already made that alternating so I get only one updated per run, but still can read the limit switch positions every run to stop the motor when needed.
On the Nano, pwm frequency is 490 Hz (not kHz ;) ), pins 5 and 6 are double that (980 Hz), but they are affected by the millis() timer.



Cheers,
Max.
MaxZ
Posts: 330
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Motor drivers and arduino

Post by MaxZ »

Hi guys,

Just to round this off, the system is working as it should by now, with the TB6612FNG driver. But it took a lot of head scratching before it did.
To begin with, I did use digitalWrites for AIN1/AIN2, and analogWrite for the PWMA pins, contrary to Mike's suggestion, for various reasons.

Basically, the code is simple, like:
Read the Rx output
Split it up into forward, stop, reverse
map forward and reverse to the speed variable as 0 (stick mid) - 255 (stick end)
Read the Rx output and the speed variable for debugging
analogWrite speed to the PWMA pin
Digital inputs to match the direction

Then the head scratching started, as the RX output was as expected, but the speed would only vary as intended when reversing, forward showed a persisting zero.
Long story short, it turned out that I had mistakingly swapped the pin nr./value arguments of the analogWrite function, correcting that made me happy again.

But what bugs me since is that I cannot think of an explanation why it would all function as intended when reversing, but not when moving forward.
Note that the speed value was read directly after mapping (Serial.print), and only written to the output pin further down the loop.

Effectively, I was mistakingly trying to write a fixed value (6 in this case) to a pin nr. varying from 0 to 255 (the speed variable). How could that affect the speed value and set it to zero? And why did I see this zero value in my serial monitor, since the Serial.print was located in between the map and analogWrite statements? And why was it only happening during stick forward (between 1550 and 1900 microsecs.)?

As I reported, I found the error and all is working fine now, but if anybody feels like solving this mystery, here is my code ( the corrected version):

Code: Select all

/*
Bedienen van twee schroefspindels met Arduino en dual motor driver TB6612FNG.
Voor arduino's met seriële monitor (Uno, Nano, etc.)
Slagbeperking d.m.v. eindschakelaars.
Vertraagde wisseling van vooruit of stop naar achteruit of vice versa.
Mei 2021, door Max Zuijdendorp.
*/
// Nummers van de input pinnen.
const int RxA = A1, RxB = A2, ISA = 3, ISB = 12, USA = 2, USB = 4, PWMA = 6, PWMB = 5, STBY = 9;

// Nummers van de output pinnen.
// 3, 5, 6, 9, 10 en 11 zijn PWM output capabele pinnen.
const int MotpinA1 = 8, MotpinA2 = 7, MotpinB1 = 10, MotpinB2 = 11;

// Maximum en mimimum ontvanger signaal om het bereik van het binnenkomende ontvangersignaal te beperken
// en daarmee te voorkomen dat de input voor de PWM's (0 tot 255) buiten bereik raakt met onverwachte gevolgen.
const int rxmin = 1100, rxmax = 1900;

// minimum vertraging (milliseconden) tussen richtingswisselingen.
const int reversedelay = 250; 


// Variabelen met beginwaarde.
int pulseA = 1500, pulseB = 1500, oldpulseA, oldpulseB, snelA = 0, snelB = 0;
bool motA1 = 0, motA2 = 0, motB1 = 0, motB2 = 0;
bool inswitchA = 1, inswitchB = 1, uitswitchA = 1, uitswitchB = 1, instopvlagA, instopvlagB, uitstopvlagA, uitstopvlagB;
int alt = 0; // "alt" is een hulpteller, waarmee afwisselend maar één ontvangeruitgang uitgelezen wordt.
long lasttimeFA = 0, lasttimeRA = 0, lasttimeFB = 0, lasttimeRB = 0; // laatste tijdstip vooruit of achetruit.

void setup() { // alles in dit deel wordt éénmalig doorlopen.
  pinMode (USA, INPUT_PULLUP);
  pinMode (USB, INPUT_PULLUP);
  pinMode (RxA, INPUT);
  pinMode (RxB, INPUT);
  pinMode (ISA, INPUT_PULLUP);
  pinMode (ISB, INPUT_PULLUP);
  pinMode (MotpinA1, OUTPUT);
  pinMode (MotpinA2, OUTPUT);
  pinMode (MotpinB1, OUTPUT);
  pinMode (MotpinB2, OUTPUT);
  // Het type van de PWM-pinnen (PWMA, PWMB) hoeft niet gedefinieerd te worden (referentie: de analogWrite() omschrijving).
  
  digitalWrite (STBY, HIGH); // pull standby pin HIGH
  digitalWrite (MotpinA1, motA1);
  digitalWrite (MotpinA2, motA2);
  digitalWrite (MotpinB1, motB1);
  digitalWrite (MotpinB2, motB2);
  analogWrite (PWMA, snelA);
  analogWrite (PWMB, snelB);
  
  Serial.begin(9600);

  

  // Het programma moet pas starten als beide servopulsen aanwezig en in het neutrale gebied zijn.
  while ((pulseIn (RxA, HIGH, 30000) < 1450) || (pulseIn (RxA, HIGH, 30000) > 1550));
  while ((pulseIn (RxB, HIGH, 30000) < 1450) || (pulseIn (RxB, HIGH, 30000) > 1550));
  

}

void loop() { // alles in dit deel wordt continu herhaald.
  alt = alt+1;

  // SPINDEL A

  if (alt == 1) {
    oldpulseA = pulseA;
    pulseA = pulseIn( RxA, HIGH, 30000); // lees de ontvangeruitgang van spindel 1 uit, timeout is 30 millisecondes.
    if ((pulseA < 950)||(pulseA > 2050)) pulseA = oldpulseA; // Als de nieuwe puls buiten de range 950 - 2050 milliseconden valt wordt de oude puls gebruikt.
    pulseA = constrain (pulseA, rxmin, rxmax); // beperk het bereik van de inkomende puls uit de ontvanger.
  }

  if (pulseA > 1550) {
    if ((millis() - reversedelay) > lasttimeRA) {
      snelA = map ( pulseA, 1550, rxmax, 0, 255); // vertaal de input (zenderknuppel voorwaarts) naar het output PWM bereik
      motA1 = 1;
      lasttimeFA = millis();
    }
    else motA1 = 0;
    motA2 = 0; // output is LOW.
    instopvlagA = false;
  }
  else if (pulseA < 1450) {
    motA1 = 0; // output is LOW.
    if ((millis() - reversedelay) > lasttimeFA) {
      snelA = map ( pulseA, 1450, rxmin, 0, 255); // vertaal de input (zenderknuppel achterwaarts) naar het output PWM bereik
      motA2 = 1;
      lasttimeRA = millis();
    }
    else motA2 = 0;
    uitstopvlagA = false;
  }
  else {
    motA1 = 0; // output is LOW.
    motA2 = 0; // output is LOW.
    lasttimeFA = millis();
    lasttimeRA = millis();
  }

  inswitchA = digitalRead (ISA); // lees de stand van de inschakelaar uit.
  if (inswitchA == 0) { // inschakelaar is ingeschakeld, pin is 0.
    instopvlagA = true; // voorkomt doorschieten
  }
  uitswitchA = digitalRead (USA); // lees de stand van de uitschakelaar uit.
  if (uitswitchA == 0) { // uitschakelaar is ingeschakeld, pin is 0.
    uitstopvlagA = true; // voorkomt doorschieten
  }

  if (instopvlagA) motA2 = 0; // spindel 1 kan alleen nog maar vooruit.
  if (uitstopvlagA) motA1 = 0; // spindel 1 kan alleen nog maar achteruit.
/*
  // Begin monitoring.
  Serial.print ("RxA in: ");
  Serial.print (pulseA);
  Serial.print ("    beginschakelaar: ");
  Serial.print (instopvlagA);
  Serial.print ("    snelheid ");
  Serial.print (snelA);
  Serial.print ("    AI1: ");
  Serial.print (motA1);
  Serial.print ("    AI2: ");
  Serial.print (motA2);
  Serial.print ("    eindschakelaar: ");
  Serial.println (uitstopvlagA); // inclusief nieuwe regel.
  // Einde monitoring, maak de monitoring regels inactief bij het definitive programmeren van de arduino
  // om vertraging in de loop te voorkomen.
*/
    
  // Stuursignalen voor de motordriver wegsturen.
  // Elke variabele motxx kan variëren tussen 0 en 255,
  // dus de PWM-output van pin Motpinxx varieert navenant van 0% tot 100%.

  analogWrite (PWMA, snelA);
  digitalWrite (MotpinA1, motA1);
  digitalWrite (MotpinA2, motA2);

  //SPINDEL B
  
  if (alt == 2) {
    oldpulseB = pulseB;
    pulseB = pulseIn( RxB, HIGH, 30000);
    if ((pulseB < 950)||(pulseB > 2050)) pulseB = oldpulseB;
    pulseB = constrain (pulseB, rxmin, rxmax);
    alt = 0; // reset de hulpteller.
  }

  
  if (pulseB > 1550) {
    if ((millis() - reversedelay) > lasttimeRB) {
      snelB = map ( pulseB, 1550, rxmax, 0, 255);
      motB1 = 1;
      lasttimeFB = millis();
    }
    else motB1 = 0;
    motB2 = 0; // output is LOW.
    instopvlagB = false;
  }
  else if (pulseB < 1450) {
    motB1 = 0; // output is LOW.
    if ((millis() - reversedelay) > lasttimeFB) {
      snelB = map ( pulseB, 1450, rxmin, 0, 255);
      motB2 = 1;
      lasttimeRB = millis();
    }
    else motB2 = 0;
    uitstopvlagB = false;
  }
  else {
    snelB = 0;
    motB1 = 0; // output is LOW.
    motB2 = 0; // output is LOW.
    snelB = 0;
    lasttimeFB = millis();
    lasttimeRB = millis();
  }

  inswitchB = digitalRead (ISB);
  if (inswitchB == 0) {
    instopvlagB = true;
  }

  uitswitchB = digitalRead (USB);
  if (uitswitchB == 0) {
    uitstopvlagB = true;
  }

  if (instopvlagB) motB2 = 0;
  if (uitstopvlagB) motB1 = 0;
/*
  Serial.print ("RxB in: ");
  Serial.print (pulseB);
  Serial.print ("    beginschakelaar: ");
  Serial.print (instopvlagB);
  Serial.print ("    snelheid ");
  Serial.print (snelB);
  Serial.print ("    BI1: ");
  Serial.print (motB1);
  Serial.print ("    BI2: ");
  Serial.print (motB2);
  Serial.print ("    eindschakelaar: ");
  Serial.println (uitstopvlagB);
*/
  analogWrite (PWMB, snelB);
  digitalWrite (MotpinB1, motB1);
  digitalWrite (MotpinB2, motB2);
A lot of Dutch in there (for educative reasons), sorry. Note that the "snel" variable is used to set the speed.

If you want to see the code in action, here is my friend's boat and working crane: https://youtu.be/XgoBTfoLupw

Cheers,
Max.
Post Reply