Arduino Due - Digital Pins

Hello,

Working on a project that would use 60 GPIO pins (i.e. 60 digital pins). I am planning to use the Due and am working on a PCB which will connect to the 60 digital pins.

Looking at the Due pinout, it looks like it has quite a bit more than 60 digital pins. Some appear to have other functionality and may be difficult to use out-of-the-box as digital pins. Additionally, when I search in google "arduino due digital pins" it say that the total digital pins on the due is 54.

Below I have included a image of the due pinout, with the digital pins that I have currently selected for my project. I was wondering if anyone had some advice or suggestions, if the digital pins I selected will work easily as digital pins or if I am likely to run into some trouble.

Thanks for the help!

Arduino has an odd way of counting digital pins on some (but not all to make matters even more confusing) of the tech specs tables for their boards. Even though the pins with analog capabilities (ADC) can also be used as digital pins, they don't include them in the count. They count pins that have only digital capabilities.

Hi
I don't know if DUE is possible, but on ATmega328 based arduinos analog pins can be used as digital.

I did a quick search, with the exceptions of the nano and mini, analog pins can be used as digital pins if referenced correctly. (So hopefully I will be good.)

The four pins on the bottom left side below the analog pins look like they could be used as digital pins, but as your have shown, they are neither digital or analog pins. My graphic labels them as "D66" - "D69", so I just assumed they were digital pins. Anyone have any experience with using these four pins?

Hi,
Can you tell us what your project is please?
Why so many I/O and what are they going to/from?

We may be able to help minimise the number of I/O required?
Are you using SPI or I2C or UART comms?

Thanks.. Tom.. :grinning: :+1: :coffee: :australia:

I just did a quick blink test on them and it worked as expected.

Technically my project only uses 6 digital pins for 2 limit switches (NO/NC) and a stepper motor. However I am creating a mult-threaded python script which I am using to have 10 parallel systems all running at once. (Thus 6*10 = 60 digital pins)

I was originally going to do this with 10 nano's via I2C, but had some I2C complications, so now going with one board via USB/serial.

But thank you ino for testing that out! Appreciate the help!

Hi,
Are you aiming to use any of them for PWM, or is it all digital?

I assume 2 NO/NC for each limit switch and 2 for stepper control.
What driver are you aiming to use?

Thanks.. Tom... :grinning: :+1: :coffee: :australia:

At this point I think I can get away without PWM. I'm using a driver from stepperonline. I just toggling the stepper PUL from HIGH to LOW (with a short delay), and that has been working just fine for my purposes. (Might integrate ramping in the future, but not a major concern at the moment.)

But yeh each limit switch has a NO & NC connection for a total of 4 pin for the 2 limit switches, and then its just the PUL and DIR for the stepper, for a total of 6 digital pins (per setup.)

And like I said, I want to make ten parallel systems running at the same time, so working on a multi-threaded python script, and just need the right micro-controller setup to adequately support it.

Hi

The Mega cannot multi-task, when you are sending control data to one stepper, checking limit switches the other 10 steppers will be inactive.
The more more code becomes involved with comms with the python environment the slower will be the output activity.

Tom... :grinning: :+1: :coffee: :australia:

I have a queueing thread built into my python script, so that only one command is sent at a time. However yes I will still have a problem...(Which is why I probably disregarded USB/Serial originally. ) I can quickly check the limit switch states, but each motor call will occupy the due for multiple seconds, making it impossible to run the systems in parallel. Back to square one... (Thanks for calling that out, Tom.)

It seems like there is no good option. I2C is good for multiple devices, but is incredibly difficult to make reliable. And serial is reliable, but the Pi only has a small number of USB ports.

The only other option that I can think of is daisy chaining 10 arduinos via serial...Unfortunately from what I have seem, the documentation on it is not as robust as other methods.

Its the bad, the worse, and the ugly.

Well, it sort-of can. You just need non-blocking stepper code. Which is tough to come by, tricky to write, and might not be fast enough with ten motors running. :frowning:
(How many steps per second do you need each stepper to run?)

I don't have the code or the micro-stepping setting right infront of me at the moment, but I would say it makes about one-two turn per second. If I don't have any microstepping its probably about 200-400 steps per second. Each motor will make about ten turns per move. So If each motor would have to wait for the previous to move, a single move across all ten setups would take about a minute. This would not work for my needs.

I am not familiar with "non-blocking" is that similar to multi-threading?

Well, 200 to 400 steps per second isn't really fast. My MobaTools library can run up to 6 steppers with up to 2500 steps/sec each ( on an Arduino with ATMega processor ). It's fairly easy to extend to 10 Steppers if the speed is slower. I didn't try so far, but it should work. MobaTools can run all these steppers in parallel without blocking the sketch. But the Due isn't supported, only the Mega is possible.

Yes, the effect is similar. You must not use delay() oder long running loops within the loop() function. Then you can program several tasks one after the other. It's some kind of 'cooperative multitasking'.

Oh alright I would certainly like to give it a try.

Before I try to implement, could I just explain a little bit more about my project to ensure that were on the same page about required/available functionality:

So basically, my main program is located on the raspberry pi, and that is where I will be creating a thread for each of the 10 setups (a setup = 2 limit switches & 1 stepper) and one additional queue thread to ensure only one command is sent to the Arduino at a time.

So the RPi checks the state of a limit switch via the Arduino, and based on the response tells a motor to move. For the program to work correctly, when the motor command is called (via your Arduino library), it has to go off and execute the motor move, but the main code will continue to run. So when the motor is still finishing its rotation, other threads can be checking the state of their limit switches and have the Arduino sending back data to the RPi, and those threads can then have their motor start to move in random fashion, sometimes overlapping and sometimes not.

So while a motor move may take 5 seconds to complete, it acts as an instantaneous command, moves off to the side and executes, as the next portion of code continues to run without delay.

Based on what you described, it sounds like this is what your library offers, but I just wanted to confirm before I jump in.

All Methods of the MoToStepper class are non blocking. They tell the motor what to do and then instantaneously return. Creating the necessary steps pulses to turn the motor is done by timer interrupts ( on the Mega timer 3 is used ).
To check your limit switches you could use the MoToButtons class of the library. Up to 32 switches can be processed with one instance of the class. The switches are debounced and various switch states can be queried - e.g. the switch state or the moment at which the switch is actuated.
But depending on your mechanics it may be needed to stop the motor in the arduino sketch when a limit switch is reached. The time delay if this is done by RPi must be considerd. Of course the RPi can be informed in parallel when a limit switch is actuated.
It would be nice if you could tell us still a little more about your project so that we can get a better idea.

Cool. Maybe a non-blocking stepper library is not so "hard to find" after all!

It's like the "blink without delay" sketch (perhaps the simplest example of non-blocking code.) Stepping a motor is like blinking an LED, except you have four pins that need to change state, and it's a bit faster, and you want to "schedule" an operation like "go 200 steps in X direction." So instead of doing all 200 steps in a loop with delayMicroseconds() between each step, you'd do something like this pseudo-code:

moveMotor1() {
  if (micros() - lastMicros > stepTime1) {
    if (stepCount1 > 0) {
      stepcount1 -= 1;
      outputNewPhases();
      lastMicros1 = micros();
    }
  }
}

(repeat N times, wrapping in a nice datastructure of some kind.)
Probably takes a couple of microseconds if the motor isn't moving, and maybe ~200us if it actually has to step (even with Arduino's slow digitalWrite. Faster if carefully optimized!) So it should easily do a total of a couple thousand steps per second...

Yeh so I have a sign that is mounted in a wormgear that turns a sign. I have one limit switch for homing, and the second is just a out-of-bounds switch, incase something goes awry and it turns farther than intended. In any case my program control where the sign goes, and uses the homing switch to know it location when starting up. So basically the program selects a location. Based on this selection, the program first checks that 1) all limit switches are connected, then 2) that no limit switch is pressed, then 3) sends a command for the motor to move clockwise or counter clockwise. It continues to repeat this until the correct angle is reached.

So basically a motor call would not need to be interrupted as the limit switch is not checked again until the motor move that has been called is finished moving and the RPi determined if another movement is required. (The motor move only turns the output shaft on the wormgear a few degrees so it is done in very small increments.)

Here is the ardunio code I made to work via I2C. The RPi sends a command to check various limit switches. Based on the response, it sends a command to change a variable on the arduino, and sends a find command which is used in conjunction with the changed variable from the previous state. (I did it like this to minimize the data transferred, as I was concerned with the reliability of the I2C bus...unfortunately even that was too much.)

Anyways here is the Arduino code for the transfer of one setup (one thread). I haven't written the code for the 10 setups yet, but it will probably be just an extension of what I have already written, and changed to serial communication:

#include <Wire.h>

//Limit switch initialization
int h_no = 7;
int h_nc = 8;
int a_no = 10;
int a_nc = 11;

int arrival = 0;
int clicker = 0;

char h_no_h[] = "1";
char h_no_h_f[] = "2";
char a_no_h[] = "3";
char a_no_h_f[] = "4";
char a_no_l[] = "5";
char a_no_l_f[] = "6";
char h_no_l[] = "7";
char h_no_l_f[] = "8";
char all_ls[] = "9";
char all_ls_f[] = "a10";
char error[] = "b11";
 
int driverPUL = 13;    // PUL- pin
int driverDIR = 12;    // DIR- pin
 
int pd = 1000;       // Pulse Delay period

void setup() {
  
  Serial.begin(9600);
  Wire.begin(0x8);              
  Wire.onReceive(receiveEvent);
  Wire.onRequest(sendData);

  pinMode(h_no, INPUT_PULLUP);
  pinMode(h_nc, INPUT_PULLUP);
  pinMode(a_no, INPUT_PULLUP);
  pinMode(a_nc, INPUT_PULLUP);

  pinMode (driverPUL, OUTPUT);
  pinMode (driverDIR, OUTPUT);
}

void loop() {}

void motor_move_clockwise(){

   for (int i = 0; i <= 1000; i++) {
    digitalWrite(driverDIR,LOW);
    digitalWrite(driverPUL,HIGH);
    delayMicroseconds(pd);
    digitalWrite(driverPUL,LOW);
    delayMicroseconds(pd);
  }
}

void motor_move_counterclockwise(){

  for (int i = 0; i <= 1000; i++) {
    digitalWrite(driverDIR,HIGH);
    digitalWrite(driverPUL,HIGH);
    delayMicroseconds(pd);
    digitalWrite(driverPUL,LOW);
    delayMicroseconds(pd);
  }
}

bool check_switches(){

  if((digitalRead(h_no) == LOW && digitalRead(h_nc) == LOW || digitalRead(h_no) == HIGH && digitalRead(h_nc) == HIGH) || (digitalRead(a_no) == LOW && digitalRead(a_nc) == LOW || digitalRead(a_no) == HIGH && digitalRead(a_nc) == HIGH))
    return false;
  else
    return true;
}

void receiveEvent(int howMany) {
  while (Wire.available()) { 
    arrival = Wire.read(); 

    Serial.println("recieveEvent Function Called");

    if(arrival == 1){
      motor_move_clockwise();
      Serial.println("Motor clockwise called");
    }else if(arrival == 2){
      motor_move_counterclockwise();
      Serial.println("Motor counterclockwise called");
    }else if(arrival == 3){
      clicker = 1;
      Serial.println("Clicker = 1, checking h_no_h");
    }else if(arrival == 4){
      clicker = 2;
      Serial.println("Clicker = 2, checking a_no_h");
    }else if(arrival == 5){
      clicker = 3;
      Serial.println("Clicker = 3, checking a_no_l");
    }else if(arrival == 6){
      clicker = 4;
      Serial.println("Clicker = 4, checking h_no_l");
    }else if(arrival == 7){
      clicker = 5;
      Serial.println("Clicker = 5, checking all_ls");
    }else{
      clicker = 6;
      Serial.println("Clicker = 6, Error");}
  }
}

void sendData() { 
  
  Serial.println("sendData Function Called");

  if (clicker == 1){

    if(digitalRead(h_no) == HIGH){
      Wire.write(h_no_h[0]); // writing 1
      Serial.println(" wiring - home NO high");
    }else{
      Wire.write(h_no_h_f[0]); // writing 2
      Serial.println(" wiring - home NO not high");
    }
    
  }else if(clicker == 2){

    if(digitalRead(a_no) == HIGH){
      Wire.write(a_no_h[0]); // writing 3
      Serial.println(" wiring - away NO high");
    }else{
      Wire.write(a_no_h_f[0]); // writing 4
      Serial.println(" wiring - away NO not high");
    }

  }else if(clicker == 3){

    if(digitalRead(a_no) == LOW){
      Wire.write(a_no_l[0]); //writing 5
      Serial.println(" wiring - away NO low");
    }else{
      Wire.write(a_no_l_f[0]); // writing 6
      Serial.println(" wiring - away NO not low");
    }

  }else if(clicker == 4){

    if(digitalRead(h_no) == LOW){
      Wire.write(h_no_l[0]); // writing 7
      Serial.println(" wiring - home NO low");
      Serial.println(h_no_l[0]);
    }else{
      Wire.write(h_no_l_f[0]); // writing 8
      Serial.println(" wiring - home NO not low");
    }

  }else if(clicker == 5){

    if(check_switches()){
      Wire.write(all_ls[0]); // writing 9 - swithes good
      Serial.println(" wiring - limit switches connected");
    }else{
      Wire.write(all_ls_f[0]);   // writing (a) 10 - switches bad
      Serial.println(" wiring - limit switches Connection FAILURE");
    }
    
  }else if(clicker == 6){

    Wire.write(error[0]); // writing (b) 11
    Serial.println(" wiring - ERROR");
    
  }
}

Just curious, would I just be able to use your library for just the moving of the stepper motors? Or does it require that all code is implemented under your library for the non-blocking property to work effectively? (Ideally, I would prefer to make small changes if possible, but whatever is required is required.)

That should work for me!

No, you can use any of the classes in MobaTools independently from each other. There are some parts that are used in common by some classes. But that's done automatically. Only the code needed for the used classes is included in your sketch. The MoToButtons class ( and the MoToTimer class ) is completely independent from the other classes and need only the .h files to be included. But you can safely include MobaTools.h only.

What is meant with 'program'? Its the program on the RPi? I would do all this in the Arduino Sketch. Homing when starting. Then the RPi tells where the sign should go, and the Arduino moves the sign to that position. If there is an error, the RPi is informed about that. Why should the RPi do all this low level work?
After homing you can position the stepper absolutely with MobaTools. Only tell where it should go, and the lib does the rest.
Why don't you trust your limit switches so that you have to check them so often?

Great, I was think of just keeping it simple and just using it to replace my "motor_move_clockwise()" and motor_move_counterclockwise()" functions (like shown below). I going to download it shortly and talk a look at implementation of your library.

void motor_move_clockwise(){

   for (int i = 0; i <= 1000; i++) {
    digitalWrite(driverDIR,LOW);
    digitalWrite(driverPUL,HIGH);
    delayMicroseconds(pd);
    digitalWrite(driverPUL,LOW);
    delayMicroseconds(pd);
  }
}

Originally my project was completely contained on the RPi. This includes the GUI, the limit switches and motor were are all connected and controlled by the RPi. (Used waves to bypass real-time motor control issues.) Anyways as I have been wanted to expand, the RPi is limiting in the amount of available GPIO. That is why I have since expanded to the Arduino. To minimize changes, I only exported functionality related to the GPIO to the Arduino, and left everything else done on the Pi in python. (I imagine it looks odd how I have it set up, and had I considered this expansion when I started, I probably would have done it differently.)