es geht mit der Simultanausführung auch schon viel einfacher, mit einem Mega2560 z.B. oder einem Due, und mit Rotationsencoder-Motoren, die man eh braucht, wenn man keine Servos hat:

PD-Steuerung (PID muss nicht unbedingt sein) pro Motor,
globale flags für jeden Motor (true für Ziel noch anzusteuern, false für "idle")
große while () Schleife für jede Simultanausführung:
wenn Zielencoder nicht erreicht ist, neuen Regelwert eingeben,
und wenn erreicht ist auf brake oder coast stellen und flag für diesen Motor auf "false" (Ziel erreicht).
while() Schleife laufen lassen, bis alle flags für alle Motoren auf false sind (alle Ziele erreicht, keine Steuerung mehr).
Das kann man in eine Funktion packen, der man alle Flags und Zielwerte übergibt, und die zurückmeldet (globaler Flag oder Rückgabewert), wenn alles fertig ist.

Alternative, ebenfalls erfolgreich probiert:
Multithreading mit Scheduler libs (gibt es von 2 verschiedenen Autoren (cmaglie oder mpatil) für ARM-MCUs und auch für beliebige ARMs+AVRs), pro Motor ein Scheduler thread,
threads laufen leer wenn Motor flag auf false steht, ansonsten wie oben.

Dass Motoren bei 2-Pin Steuerung unbeabsichtigt anlaufen passiert aber doch, allerdings nicht immer für alle Pins sondern nur für einige spezielle beim Bootvorgang, wenn die dann auf HIGH oder LOW gezogen werden.
Daher 3-Pin-Steuerung bei L298/L293-Steuerlogik, und die gibt es auch für andere H-Brücken mit anderen Chips von anderen Herstellern.

Aber wie gesagt: Servos sind viel einfacher, weil man
a) keine Encoder auslesen muss und man
b) keine H-Brücken braucht.