hi botty,
danke, jetzt komme ich weiter...
die pinns 18-21 sind noch frei...
Druckbare Version
hi botty,
danke, jetzt komme ich weiter...
die pinns 18-21 sind noch frei...
Hallo inka,
ich hab mir Deinen Code mal angeguckt und da ich gerade selbst an
meinem ersten Projekt dran bin, versuch ich hier mal Dir Deine Zurückhaltung
gegenüber Interrupts zu nehmen und zeig Dir meinen Code.
Falls es zu ausführlich wird, dann fasse das bitte nicht als Überheblichkeit auf.
Ich weiß halt einfach nicht auf welchem Programmierstand Du bist.
Die Code Beispiele nicht zusammenkopieren, ganz unten ist der ganze Sketch.
Los geht's:
Zuerst hab ich mir einen Datentypen gemacht, der die Infos für einen Motor hält.
mot ist das Objekt für den PWM Kanal, enc_pin ist der Pin an den der EncoderCode:struct motor {
AF_DCMotor mot;
uint8_t enc_pin;
// volatile damit der Compiler keine 'dummen' Optimierung macht.
volatile uint32_t ticks;
unsigned long start_time;
unsigned long stop_time;
};
kommt und ticks ist die Variable in der wir die Flankenwechsel zählen.
Das "volatile" davor ist wichtig. Ohne könnte der Compiler auf die Idee kommen
was wegzuoptimieren was er nicht soll.
Faustregel: Variablen die Interrupt Service Routinen und Hauptprogramm
benutzt werden sind immer "volatile".
Da Du vier Motoren hast (ich hab nur zwei) nehmen wir ein Array vom Typ
"struct motor" und initialisieren es.
Die Spalten entsprechen den obigen Zeilen. Sprich die "1" ist der PWM Kanal undCode:struct motor motoren[M_MAX] = {
{ AF_DCMotor(1), 18, 0, 0, 0 }, // M_VL
{ AF_DCMotor(2), 19, 0, 0, 0 }, // M_HL
{ AF_DCMotor(3), 20, 0, 0, 0 }, // M_VR
{ AF_DCMotor(4), 21, 0, 0, 0 } // M_HR
};
die "18" der Pin an den der Endocder vom Motor der mit dem ersten PWM Kanal
gesteuert wird angeschlossen ist.
Da ich lieber mit Namen als mit Zahlen arbeite habe ich noch einen Aufzählungstypen
deklariert:
Die Namen werden auf fortlaufende unsigned integer abgebildet M_MAX hatCode:enum motoren_e {
M_VL = 0, // Motor vorne links
M_HL,
M_VR,
M_HR,
M_MAX
};
den Wert "4".
Wenn Du die Zahlen bei der Initialisierung so anpaßt das dein erster PWM Kanal
den vorderen linken Motor antreibt und der Encoder am entsprechenden Pin liegt, läßt
sich der Motor mit
anschalten.Code:motoren[M_VL].mot.setSpeed(255/2);
motoren[M_VL].mot.run(FORWARD);
Wollte man das mit allen machen geht das mit:
Jetzt brauchen wir für jedes Element eine Interrupt Service Routine, die aufgerufen wird wennCode:for(uint8_t idx = M_VL; idx < M_MAX; idx++) {
motoren[idx].mot.setSpeed(255/2);
motoren[idx].mot.run(FORWARD);
}
sich der Zustand am Pin ändert.
Grundsätzlich haben dies Funktionen diese Signatur:
Faustregeln für den Inhalt so einer ISR sind:Code:void isr_name(void);
Sie muß ihre Funktion erfüllen.
Sie soll so kurz wie möglich dabei sein.
Wir wollen unseren "ticks" Zähler raufzählen und zwar für jedes Element in unserem Array.
So brauchen wir vier Stück:
Der nächste Schritt ist den Pin zu initialisieren und dem System zu erklären, welcher PinCode:void motor_isr_m_vl(void) { motoren[M_VL].ticks++; }
void motor_isr_m_vr(void) { motoren[M_VR].ticks++; }
void motor_isr_m_hl(void) { motoren[M_HL].ticks++; }
void motor_isr_m_hr(void) { motoren[M_HR].ticks++; }
zu welcher ISR gehört:
"digitalPinToInterrupt()" mapped die Pin Nummer auf das entsprechende Register undCode:void setup() {
Serial.begin(9600);
for(uint8_t idx = M_VL; idx < M_MAX; idx++) {
pinMode(motoren[idx].enc_pin, INPUT_PULLUP);
}
attachInterrupt(digitalPinToInterrupt(motoren[M_VL].enc_pin), motor_isr_m_vl, CHANGE);
attachInterrupt(digitalPinToInterrupt(motoren[M_HL].enc_pin), motor_isr_m_hl, CHANGE);
attachInterrupt(digitalPinToInterrupt(motoren[M_VR].enc_pin), motor_isr_m_vr, CHANGE);
attachInterrupt(digitalPinToInterrupt(motoren[M_HR].enc_pin), motor_isr_m_hr, CHANGE);
}
"attachInterrupt()" setzt dafür die ISR als zweiten Parameter. Der letzte Parameter kann
die Werte RISING, FALLING oder CHANGE haben. CHANGE bedeutet bei jedem Wechsel
von HIGH nach LOW und vice versa wird die entsprechende ISR aufgerufen und in unserem Fall
der richtige "ticks" inkrementiert.
Was ist jetzt kritisch? Nun alle Elemente aus der Struktur außer "ticks" sind unkritisch.
Wenn Du auf "ticks" lesend zugreifst kann es schlimmstenfalls passieren das der Interrupt das
Lesen unterbricht und du mit einem Wert um eins verschoben weiterarbeitest.
Beim schreiben kann es passieren, daß wenn Du "ticks" einen neuen Wert zuweißt, der
Interrupt Dich unterbricht und dadurch der alte Wert verwendet wird.
Mag ersteres noch unkritisch sein,müssen wir uns vor Letzterem schützen.
Dafür gibt's im Arduino Framework die Funktionen "noInterrupts()", die alle Interrupts ausschaltet
und "interrupts()", die sie wieder einschaltet. Hier am Beispiel "motor_start()"
letzten Endes muß man jeden Zugriff auf "ticks" so schützen und damit man das beim LesenCode:void motor_start(uint8_t idx, uint8_t pwm, uint8_t dir) {
if(idx >= M_MAX)
return;
noInterrupts();
motoren[idx].ticks = 0;
interrupts();
motoren[idx].start_time = motoren[idx].stop_time = millis();
motoren[idx].mot.setSpeed(pwm);
motoren[idx].mot.run(dir);
}
wie beim Schreiben nicht vergißt gibt's die Funktionen
die Du anstelle direkter Zuweisungen benutzt, so kann nichts passieren.Code:inline uint32_t motor_hole_ticks(uint8_t idx) {
if(idx >= M_MAX)
return 0;
noInterrupts();
uint32_t tmp = motoren[idx].ticks;
interrupts();
return tmp;
}
inline void motor_setze_ticks(uint8_t idx, uint32_t val) {
if(idx >= M_MAX)
return;
noInterrupts();
motoren[idx].ticks = val;
interrupts();
}
Zu guter Letzt der ganze Sketch in dem Du die PWM Kanäle und die
Encoder Pins anpassen müßtest. Dann kann's losgehen ob einer eine Umdrehung
macht und wenn das funktionert sind da "setupMulti()" und "loopMulti()" für alle
Motoren.
Der nächste Schritt wäre jetzt die Datenstruktur für einen Regler zu erweitern und diesenCode:#include <AFMotor.h>
/*
* Benamte Indexe. Wichtig: Alle Schleifen basieren auf der
* Annahme, dass M_VL das erste Element und mit 0 initialisiert
* ist. M_MAX muss ausserdem _immer_ das letzte Element sein da
* es die Groesse des Arrays s.u. bestimmt.
*/
enum motoren_e {
M_VL = 0, // Motor vorne links
M_HL,
M_VR,
M_HR,
M_MAX
};
/*
* Infos zu einem Motor zusammenfassen.
*/
struct motor {
AF_DCMotor mot;
uint8_t enc_pin;
// volatile damit der Compiler keine 'dummen' Optimierung macht.
volatile uint32_t ticks;
unsigned long start_time;
unsigned long stop_time;
};
/*
* Hier werden die PWM Channel - Encoder Pin Paare gesetzt.
* Restliche Elemente muessen ebenfalls initialisiert werden.
* Werte pruefen ob sie zu den Namen oben passen.
* Fuer die externen Interrupts sind die Pins 2,5,18-21
* erlaubt. Hardware anpassen.
*/
struct motor motoren[M_MAX] = {
{ AF_DCMotor(1), 18, 0, 0, 0 }, // M_VL
{ AF_DCMotor(2), 19, 0, 0, 0 }, // M_HL
{ AF_DCMotor(3), 20, 0, 0, 0 }, // M_VR
{ AF_DCMotor(4), 21, 0, 0, 0 } // M_HR
};
// Anzahl der Encoderstriche fuer eine Umdrehung
const static double ticks_per_rev = 40;
const static double durchmesser = 6.5; // in cm;
const static double u_per_tick = (3.1415926535897932384626433832795 * durchmesser) / ticks_per_rev;
/*
* Interruptroutinen mit der gleichen Namenskonvention wie
* oben.
*/
void motor_isr_m_vl(void) { motoren[M_VL].ticks++; }
void motor_isr_m_vr(void) { motoren[M_VR].ticks++; }
void motor_isr_m_hl(void) { motoren[M_HL].ticks++; }
void motor_isr_m_hr(void) { motoren[M_HR].ticks++; }
void setup() {
//Serial.begin(115200);
Serial.begin(9600);
pinMode(motoren[M_VL].enc_pin, INPUT_PULLUP);
// motor_init_pos(M_VL);
/*
* Hier wird die Verbindung hergestellt zwischen Pin und Interruptroutine.
* Die Namen muessen zueinander passen.
* Durch CHANGE werden beide Uebergaenge beruecksichtigt.
*/
attachInterrupt(digitalPinToInterrupt(motoren[M_VL].enc_pin), motor_isr_m_vl, CHANGE);
}
void loop() {
unsigned long log_time;
String out_s = "";
motor_start(M_VL, 255/2+3, FORWARD);
log_time = millis();
while(motor_laeuft(M_VL)) {
if(motor_hole_ticks(M_VL) >= ticks_per_rev)
motor_stop(M_VL);
// Ticks und Strecke ausgeben
static const int wtime = 250;
if(log_time + wtime <= millis() ) {
log_time = wtime + millis();
out_s = "";
out_s += millis() + ": ";
out_s += M_VL + " ";
out_s += motoren[M_VL].ticks + " "; // Hier koennte jetzt ein Interrupt unterbrechen!
out_s += motor_strecke_gefahren(M_VL);
Serial.println(out_s);
}
}
while(1);
// oder
// delay(2000);
}
void setupMulti() {
//Serial.begin(115200);
Serial.begin(9600);
for(uint8_t idx = M_VL; idx < M_MAX; idx++) {
pinMode(motoren[idx].enc_pin, INPUT_PULLUP);
// motor_init_pos(idx);
}
/*
* Hier wird die Verbindung hergestellt zwischen Pin und Interruptroutine.
* Die Namen muessen zueinander passen.
* Durch CHANGE werden beide Uebergaenge beruecksichtigt.
*/
attachInterrupt(digitalPinToInterrupt(motoren[M_VL].enc_pin), motor_isr_m_vl, CHANGE);
attachInterrupt(digitalPinToInterrupt(motoren[M_HL].enc_pin), motor_isr_m_hl, CHANGE);
attachInterrupt(digitalPinToInterrupt(motoren[M_VR].enc_pin), motor_isr_m_vr, CHANGE);
attachInterrupt(digitalPinToInterrupt(motoren[M_HR].enc_pin), motor_isr_m_hr, CHANGE);
}
/*
* Alle Motoren an, bis eine Umdrehung erreicht ist. Es wird alle
* halbe Sekunde der Zustand der Ticks pro Motor ausgeben.
*/
void loopMulti() {
unsigned long log_time = 0;
unsigned long ticks_tmp = 0;
unsigned long all_ticks_tmp[M_MAX] = { 0, 0, 0, 0 };
String out_s = "";
bool alle_aus = false;
// Alle Motoren an.
for(uint8_t idx = M_VL; idx < M_MAX; idx++) {
if( ! motor_laeuft(idx) )
motor_start(idx, 255/2+3, FORWARD);
}
while( ! alle_aus ) {
// Wenn die Anzahl der Ticks fuer eine Umdrehung
// erreicht ist - Motor aus.
for(uint8_t idx = M_VL; idx < M_MAX; idx++) {
if(motor_laeuft(idx) && motor_hole_ticks(idx) >= ticks_per_rev) {
motor_stop(idx);
}
}
// Ticks und Strecke ausgeben
static const int wtime = 250;
if(log_time + wtime <= millis() ) {
out_s = "";
out_s += millis() + ": ";
for(uint8_t idx = M_VL; idx < M_MAX; idx++) {
out_s += idx + " ";
out_s += motoren[idx].ticks + " "; // Hier koennte jetzt ein Interrupt unterbrechen!
out_s += motor_strecke_gefahren(idx);
ticks_tmp = 0;
motor_ticks_per_milli(idx, &ticks_tmp);
out_s += " " + ticks_tmp;
if(idx == M_MAX - 1)
out_s += "\n";
else
out_s += ", ";
}
for(uint8_t idx = 0; idx < M_MAX; idx++)
all_ticks_tmp[idx] = 0;
motor_ticks_per_milli_fuer_alle(all_ticks_tmp, M_MAX);
for(uint8_t idx; idx < M_MAX; idx++)
out_s += all_ticks_tmp[idx] + " ";
Serial.println(out_s);
log_time = wtime + millis();
}
// pruefen ob alle Motoren aus sind.
for(uint8_t idx = M_VL, alle_aus = true; alle_aus && idx < M_MAX; idx++) {
alle_aus = alle_aus && ( ! motor_laeuft(idx) );
}
}
while(1);
// Oder nach 2sec geht's von vorne los.
// delay(2000);
}
/*
* Faehrt den Motor auf den ersten HIGH Level des Encoderpins.
* Falls er schon HIGH ist wird erst in ein LOW gefahren und
* beim naechsten HIGH gestoppt.
*/
void motor_init_pos(uint8_t idx) {
if(idx >= M_MAX)
return;
if(motor_laeuft(idx))
motor_stop(idx);
// Wenn wir bereits eine 1 haben, dann fahren wir erstmal bis zur
// naechsten 0.
if(digitalRead(motoren[idx].enc_pin)) {
motoren[idx].mot.setSpeed(255/3);
motoren[idx].mot.run(FORWARD);
while(digitalRead(motoren[idx].enc_pin));
}
// Wir sind in ner 0, wissen aber nicht ob sich der Motor schon dreht.
if( ! digitalRead(motoren[idx].enc_pin) ) {
motoren[idx].mot.setSpeed(255/4);
motoren[idx].mot.run(FORWARD);
while( ! digitalRead(motoren[idx].enc_pin) );
motoren[idx].mot.setSpeed(0);
motoren[idx].mot.run(RELEASE);
// Bremsen evtl. nicht noetig oder muss angepasst werden.
/*
delayMicroseconds(75);
motoren[idx].mot.setSpeed(255/5);
motoren[idx].mot.run(BACKWARD);
delayMicroseconds(100);
motoren[idx].mot.setSpeed(0);
motoren[idx].mot.run(RELEASE);
*/
}
}
/*
*
*/
bool motor_laeuft(uint8_t idx) {
if(idx >= M_MAX)
return false;
return motoren[idx].start_time > 0 && motoren[idx].start_time == motoren[idx].stop_time;
}
/*
* idx - der Motor Index
* pwm und dir wie in AF_DCMotor.
*/
void motor_start(uint8_t idx, uint8_t pwm, uint8_t dir) {
if(idx >= M_MAX)
return;
motor_setze_ticks(idx, 0);
motoren[idx].start_time = motoren[idx].stop_time = millis();
motoren[idx].mot.setSpeed(pwm);
motoren[idx].mot.run(dir);
}
/*
* Fuer den Fall das zwischen motor_start und motor_stop die pwm nicht
* geaendert wird liesse sich aus der Zeit und der Strecke s.u. grob die
* Geschwindigkeit ermitteln (Be- und Entschleunigung vernachlaessgt).
*/
void motor_stop(uint8_t idx) {
if(idx >= M_MAX)
return;
motoren[idx].mot.setSpeed(0);
motoren[idx].mot.run(RELEASE);
motoren[idx].stop_time = millis();
}
/*
* Liesst den ticks Wert interrupt safe.
*/
inline uint32_t motor_hole_ticks(uint8_t idx) {
if(idx >= M_MAX)
return 0;
noInterrupts();
uint32_t tmp = motoren[idx].ticks;
interrupts();
return tmp;
}
/*
* Setzt den ticks Wert interrupt safe.
*/
inline void motor_setze_ticks(uint8_t idx, uint32_t val) {
if(idx >= M_MAX)
return;
noInterrupts();
motoren[idx].ticks = val;
interrupts();
}
/*
*
*/
void motor_ticks_per_milli(uint8_t idx, unsigned long *res) {
if(idx >= M_MAX) {
*res = 0;
return;
}
*res = motor_hole_ticks(idx) / (millis() - motoren[idx].start_time);
}
/*
* Zu _einem_ Zeitpunkt werden alle Ticks/Milli ausgerechnet.
*/
void motor_ticks_per_milli_fuer_alle(unsigned long *res, uint8_t n) {
unsigned long *b = 0;
unsigned long tstamp = 0;
if(n != M_MAX)
return;
b = res;
noInterrupts();
tstamp = millis();
while(b < res + n) {
*b = motoren[b - res].ticks;
b++;
}
interrupts();
for(b = res; b < res + n; b++) {
*b = *b / (tstamp - motoren[b - res].start_time);
}
}
/*
* Rechnet die gefahrenen Zentimeter anhand der Ticks aus.
*/
double motor_strecke_gefahren(uint8_t idx) {
if(idx >= M_MAX)
return 0.0;
return motor_hole_ticks(idx) * u_per_tick;
}
/*
* Rechnet fuer alle Motoren zu _einem_ Zeitpunkt die Strecke
* aus.
*/
void motor_strecke_fuer_alle(double *res, int n) {
if(n != M_MAX)
return;
double *b = 0;
unsigned long tmps[M_MAX];
uint8_t idx = 0;
noInterrupts();
while(idx < M_MAX) {
tmps[idx] = motoren[idx].ticks;
idx++;
}
interrupts();
for(b = res; b < res + n; b++) {
*b = tmps[b-res] * u_per_tick;
}
}
zu implementieren.
Soweit bin hier noch nicht, da die Dinger Neuland für mich sind und ich jetzt erstmal
schaue ob mir eine gute Fee einen Drucker auf den heutigen Sperrmüll gestellt hat, den
für meinen Robby ausschlachten kann.
Viele Grüße
Chris
vielen dank, botty für den code...
mache Dir bitte keine gedanken über "zu viel erklärender text" und so, lieber mehr als weniger. Und auch wenn mein englisch ausreichend ist um beim lesen technischer texte halbwegs zu verstehen um was es geht - die deutschen kommentare und erklärungen sind richtig wohltuend...
für meinen "programmierstand" ist der code recht ambitioniert, ich brauche zeit um alles zu verstehen. Ich versuche so zu lernen, dass ich fremden code zuerst zu erfassen versuche, dann starte und durch veränderungen mir verborgen gebliebenes zu verstehen versuche. Dazu brauche ich - wie schon gesagt - zeit und einen funktionierenden sketch :-( ---- ist da evtl. beim copy und paste etwas verloren gegangen? Oder muss ich irgendwo etwas ergänzen/anpassen?
schon hier:die reiehnfolge meiner motoren ist durch die lage des mega und des darauf gesteckten motorshilds eine andere (die zahlen entsprechen den motoren 1 bis 4 des motorshields...):Code:enum motoren_e {
M_VL = 0, // Motor vorne links
M_HL,
M_VR,
M_HR,
M_MAX
};
die eigentliche zuordnung zu den motorbezeichnungen aus der lib findet aber - wenn ich es richtig verstehe - erst hier statt:Code:M_Hl = 1
M_Hr = 2
M_Vr = 3
M_Vl = 4
Code:/*
* Hier werden die PWM Channel - Encoder Pin Paare gesetzt.
* Restliche Elemente muessen ebenfalls initialisiert werden.
* Werte pruefen ob sie zu den Namen oben passen.
* Fuer die externen Interrupts sind die Pins 2,5,18-21
* erlaubt. Hardware anpassen.
*/
struct motor motoren[M_MAX] = {
{ AF_DCMotor(1), 18, 0, 0, 0 }, // M_VL
{ AF_DCMotor(2), 19, 0, 0, 0 }, // M_HL
{ AF_DCMotor(3), 20, 0, 0, 0 }, // M_VR
{ AF_DCMotor(4), 21, 0, 0, 0 } // M_HR
};
Beim versuch den sketch zu kompilieren bekomme ich folgende fehlermeldung:
an der verstehe ich nur, dass ‘digitalPinToInterrupt’ nicht deklariert wurde, deshalb die frage ob es beim kopieren passiert sein könnte, oder ob ich woanders suchen muss?Code:botty_4_motoren_1.ino: In function ‘void setup()’:
botty_4_motoren_1.ino:70:62: error: ‘digitalPinToInterrupt’ was not declared in this scope
botty_4_motoren_1.ino: In function ‘void loop()’:
botty_4_motoren_1.ino:93:13: error: ambiguous overload for ‘operator+=’ (operand types are ‘String’ and ‘double’)
botty_4_motoren_1.ino:93:13: note: candidates are:
In file included from /usr/share/arduino/hardware/arduino/cores/arduino/Arduino.h:192:0,
from botty_4_motoren_1.ino:9:
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:108:11: note: String& String::operator+=(char)
String & operator += (char c) {concat(c); return (*this);}
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:109:11: note: String& String::operator+=(unsigned char)
String & operator += (unsigned char num) {concat(num); return (*this);}
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:110:11: note: String& String::operator+=(int)
String & operator += (int num) {concat(num); return (*this);}
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:111:11: note: String& String::operator+=(unsigned int)
String & operator += (unsigned int num) {concat(num); return (*this);}
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:112:11: note: String& String::operator+=(long int)
String & operator += (long num) {concat(num); return (*this);}
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:113:11: note: String& String::operator+=(long unsigned int)
String & operator += (unsigned long num) {concat(num); return (*this);}
^
botty_4_motoren_1.ino: In function ‘void setupMulti()’:
botty_4_motoren_1.ino:116:62: error: ‘digitalPinToInterrupt’ was not declared in this scope
botty_4_motoren_1.ino: In function ‘void loopMulti()’:
botty_4_motoren_1.ino:157:15: error: ambiguous overload for ‘operator+=’ (operand types are ‘String’ and ‘double’)
botty_4_motoren_1.ino:157:15: note: candidates are:
In file included from /usr/share/arduino/hardware/arduino/cores/arduino/Arduino.h:192:0,
from botty_4_motoren_1.ino:9:
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:108:11: note: String& String::operator+=(char)
String & operator += (char c) {concat(c); return (*this);}
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:109:11: note: String& String::operator+=(unsigned char)
String & operator += (unsigned char num) {concat(num); return (*this);}
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:110:11: note: String& String::operator+=(int)
String & operator += (int num) {concat(num); return (*this);}
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:111:11: note: String& String::operator+=(unsigned int)
String & operator += (unsigned int num) {concat(num); return (*this);}
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:112:11: note: String& String::operator+=(long int)
String & operator += (long num) {concat(num); return (*this);}
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:113:11: note: String& String::operator+=(long unsigned int)
String & operator += (unsigned long num) {concat(num); return (*this);}
^
Hallo inka,
es tut mir leid, dass sich das bei Dir nicht kompilieren läßt und Dich unnötig Zeit kostet.
Die einzige Erklärung die ich habe ist, das wir unterschiedliche Arduino IDE Versionen benutzen.
Ich habe das Ganze unter Version 1.6.5 sowohl in meinen Nano hineinbekommen als auch gegen das Mega 2560 er Board fehlerfrei kompilieren können.
Ich meine das mit dem String ließe sich mit direkter Ausgabe auf Serial umgehen. Was aber an "attachInterrupt()" als Parameter übegeben werden muß wenn "digitalPinToInterrupt()" nicht da ist müßte ich nachschlagen (oder weiß daß jemand anderes hier aus dem Kopf?).
Die andere Variante, wenn Du 'ne ältere IDE Version benutzt, wäre die neueste Version zu installieren. Das würde ich aber nur machen, wenn andere Projekte von Dir dadurch nicht gefährdet werden, was nur Du abschätzen kannst.
Zu den Einstellungen:
Wenn das
Deine PWM Kanäle sind, sieht die Initialisierung so aus:Code:M_Hl = 1
M_Hr = 2
M_Vr = 3
M_Vl = 4
testen kannst Du das indem Du erstmal die Funktionen loopMulti, setupMulti, loop und setup auskommentierst oder löscht.Code:struct motor motoren[M_MAX] = {
{ AF_DCMotor(4), 18, 0, 0, 0 }, // M_VL
{ AF_DCMotor(1), 19, 0, 0, 0 }, // M_HL
{ AF_DCMotor(3), 20, 0, 0, 0 }, // M_VR
{ AF_DCMotor(2), 21, 0, 0, 0 } // M_HR
};
der Test ob die Zuordnung stimmt ohne Encoder/Interrupt geraffel sähe dann so aus:
und synonym mit den anderen Symbolen aus "enum motor_e" für "M_VL".Code:void setup() {
}
void loop() {
motoren[M_VL].mot.setSpeed(128);
motoren[M_VL].mot.run(FORWARD);
delay(1000);
motoren[M_VL].mot.setSpeed(0);
motoren[M_VL].mot.run(RELEASE);
}
Grüße
Chris
hallo botty,
es war die ältere version der IDE, die ich verwendet habe, unter ubuntu ist ja nicht immer das aktuellste installiert. Jetzt habe ich die 1.6.6 und konnte Deinen code bereits kompilieren, kann also weiter experimentieren...
was meine älteren projekte betrifft, da verlasse ich mich auf die abwärtskompatibilität der software:-), notfalls ist ja die alte version auch noch installiert...
zum sperrmüll und drucker: bei uns muss man nicht warten bis jemand einen drucker rausstellt, man kann es im wertstoffhof versuchen...- so habe ich mir auch schon teile besorgt...
hallo botty,
kann es sein, dass wir hier beide von etwas anderen voraussetzungen ausgehen? Ich habe keine PWM kanäle/pins wie es sie direkt auf dem mega gibt, sondern schliesse meine vier motoren am motorshield (M1 bis M4) an...
Ich bin grad auf dem Handy und deshalb in aller Kürze: ohne Interrupts wird das nicht gut gehen.
Ich hab was ähnliches gebaut und es geht ganz gut (mit IRQs). Wenn Du keinen Mega2560 nutzt dann gibt es noch die PinChangeInterrupts, diese sind etwas langsamer und umständlicher aber funktionieren gut.
Hallo inka,
aus Deinem Beispiel mit den mehreren Motoren hast folgende Variablen definiert gehabt:
die Zahl n, die Du hier in motor_x(n) benutzt hast, muß in der ersten Spalte des Arrays in AF_DCMotor(n) eingetragen werden.Code:AF_DCMotor motor_1(1);
AF_DCMotor motor_2(2);
AF_DCMotor motor_3(3);
AF_DCMotor motor_4(4);
Probier doch bitte mal aus,wenn Du die Initialisierung von oben nimmst, ob
den linken vorderen Motor alle drei Sekunden an bzw aus schaltet.Code:void loop() {
motoren[M_VL].mot.setSpeed(130);
motoren[M_VL].mot.run(FORWARD);
delay(3000);
motoren[M_VL].mot.setSpeed(0);
motoren[M_VL].mot.run(RELEASE);
delay(3000);
}
hallo botty,
so?
der - so wie ich Dich verstanden habe geänderte code sieht so aus:Code:struct motor motoren[M_MAX] =
{
{ AF_DCMotor(1), 4, 18, 0, 0 }, // M_VL
{ AF_DCMotor(2), 1, 19, 0, 0 }, // M_HL
{ AF_DCMotor(3), 3, 20, 0, 0 }, // M_VR
{ AF_DCMotor(4), 2, 21, 0, 0 } // M_HR
};
und lässt sich nicht kompilieren (ich sagte es schon, sehr sportlich der code für meine programmierkünste :-()Code:#include <AFMotor.h>
/*
* Benamte Indexe. Wichtig: Alle Schleifen basieren auf der
* Annahme, dass M_VL das erste Element und mit 0 initialisiert
* ist. M_MAX muss ausserdem _immer_ das letzte Element sein da
* es die Groesse des Arrays s.u. bestimmt.
*/
enum motoren_e
{
M_VL = 0, // Motor vorne links
M_HL,
M_VR,
M_HR,
M_MAX
};
/*
* Infos zu einem Motor zusammenfassen.
*/
struct motor
{
AF_DCMotor mot;
uint8_t enc_pin;
// volatile damit der Compiler keine 'dummen' Optimierung macht.
volatile uint32_t ticks;
unsigned long start_time;
unsigned long stop_time;
};
/*
* Hier werden die PWM Channel - Encoder Pin Paare gesetzt.
* Restliche Elemente muessen ebenfalls initialisiert werden.
* Werte pruefen ob sie zu den Namen oben passen.
* Fuer die externen Interrupts sind die Pins 2,5,18-21
* erlaubt. Hardware anpassen.
*/
struct motor motoren[M_MAX] =
{
{ AF_DCMotor(1), 4, 18, 0, 0 }, // M_VL
{ AF_DCMotor(2), 1, 19, 0, 0 }, // M_HL
{ AF_DCMotor(3), 3, 20, 0, 0 }, // M_VR
{ AF_DCMotor(4), 2, 21, 0, 0 } // M_HR
};
// Anzahl der Encoderstriche fuer eine Umdrehung
const static double ticks_per_rev = 40;
const static double durchmesser = 6.5; // in cm;
const static double u_per_tick = (3.1415926535897932384626433832795 * durchmesser) / ticks_per_rev;
/*
* Interruptroutinen mit der gleichen Namenskonvention wie
* oben.
*/
void motor_isr_m_vl(void) { motoren[M_VL].ticks++; }
void motor_isr_m_vr(void) { motoren[M_VR].ticks++; }
void motor_isr_m_hl(void) { motoren[M_HL].ticks++; }
void motor_isr_m_hr(void) { motoren[M_HR].ticks++; }
void setup()
{
//Serial.begin(115200);
Serial.begin(9600);
for(uint8_t idx = M_VL; idx < M_MAX; idx++)
{
pinMode(motoren[idx].enc_pin, INPUT_PULLUP);
// motor_init_pos(idx);
}
/*
* Hier wird die Verbindung hergestellt zwischen Pin und Interruptroutine.
* Die Namen muessen zueinander passen.
* Durch CHANGE werden beide Uebergaenge beruecksichtigt.
*/
attachInterrupt(digitalPinToInterrupt(motoren[M_VL].enc_pin), motor_isr_m_vl, CHANGE);
attachInterrupt(digitalPinToInterrupt(motoren[M_HL].enc_pin), motor_isr_m_hl, CHANGE);
attachInterrupt(digitalPinToInterrupt(motoren[M_VR].enc_pin), motor_isr_m_vr, CHANGE);
attachInterrupt(digitalPinToInterrupt(motoren[M_HR].enc_pin), motor_isr_m_hr, CHANGE);
}
/*
* Alle Motoren an, bis eine Umdrehung erreicht ist. Es wird alle
* halbe Sekunde der Zustand der Ticks pro Motor ausgeben.
*/
void loop() {
motoren[M_VL].mot.setSpeed(130);
motoren[M_VL].mot.run(FORWARD);
delay(3000);
motoren[M_VL].run(RELEASE);
delay(3000);
}
/*
void loop()
{
unsigned long log_time = 0;
unsigned long ticks_tmp = 0;
unsigned long all_ticks_tmp[M_MAX] = { 0, 0, 0, 0 };
String out_s = "";
bool alle_aus = false;
// Alle Motoren an.
for(uint8_t idx = M_VL; idx < M_MAX; idx++)
{
if( ! motor_laeuft(idx) )
motor_start(idx, 200/2+3, FORWARD);
}
while( ! alle_aus )
{
// Wenn die Anzahl der Ticks fuer eine Umdrehung
// erreicht ist - Motor aus.
for(uint8_t idx = M_VL; idx < M_MAX; idx++) {
if(motor_laeuft(idx) && motor_hole_ticks(idx) >= ticks_per_rev)
{
motor_stop(idx);
}
}
// Ticks und Strecke ausgeben
static const int wtime = 250;
if(log_time + wtime <= millis() )
{
out_s = "";
out_s += millis() + ": ";
for(uint8_t idx = M_VL; idx < M_MAX; idx++)
{
out_s += idx + " ";
out_s += motoren[idx].ticks + " "; // Hier koennte jetzt ein Interrupt unterbrechen!
out_s += motor_strecke_gefahren(idx);
ticks_tmp = 0;
motor_ticks_per_milli(idx, &ticks_tmp);
out_s += " " + ticks_tmp;
if(idx == M_MAX - 1)
out_s += "\n";
else
out_s += ", ";
}
for(uint8_t idx = 0; idx < M_MAX; idx++)
all_ticks_tmp[idx] = 0;
motor_ticks_per_milli_fuer_alle(all_ticks_tmp, M_MAX);
for(uint8_t idx; idx < M_MAX; idx++)
out_s += all_ticks_tmp[idx] + " ";
Serial.println(out_s);
log_time = wtime + millis();
}
// pruefen ob alle Motoren aus sind.
for(uint8_t idx = M_VL, alle_aus = true; alle_aus && idx < M_MAX; idx++)
{
alle_aus = alle_aus && ( ! motor_laeuft(idx) );
}
}
while(1);
// Oder nach 2sec geht's von vorne los.
// delay(2000);
}*/
/*
* Faehrt den Motor auf den ersten HIGH Level des Encoderpins.
* Falls er schon HIGH ist wird erst in ein LOW gefahren und
* beim naechsten HIGH gestoppt.
*/
void motor_init_pos(uint8_t idx)
{
if(idx >= M_MAX)
return;
if(motor_laeuft(idx))
motor_stop(idx);
// Wenn wir bereits eine 1 haben, dann fahren wir erstmal bis zur
// naechsten 0.
if(digitalRead(motoren[idx].enc_pin))
{
motoren[idx].mot.setSpeed(255/3);
motoren[idx].mot.run(FORWARD);
while(digitalRead(motoren[idx].enc_pin));
}
// Wir sind in ner 0, wissen aber nicht ob sich der Motor schon dreht.
if( ! digitalRead(motoren[idx].enc_pin) )
{
motoren[idx].mot.setSpeed(255/4);
motoren[idx].mot.run(FORWARD);
while( ! digitalRead(motoren[idx].enc_pin) );
motoren[idx].mot.setSpeed(0);
motoren[idx].mot.run(RELEASE);
// Bremsen evtl. nicht noetig oder muss angepasst werden.
/*
delayMicroseconds(75);
motoren[idx].mot.setSpeed(255/5);
motoren[idx].mot.run(BACKWARD);
delayMicroseconds(100);
motoren[idx].mot.setSpeed(0);
motoren[idx].mot.run(RELEASE);
*/
}
}
/*
*
*/
bool motor_laeuft(uint8_t idx)
{
if(idx >= M_MAX)
return false;
return motoren[idx].start_time > 0 && motoren[idx].start_time == motoren[idx].stop_time;
}
/*
* idx - der Motor Index
* pwm und dir wie in AF_DCMotor.
*/
void motor_start(uint8_t idx, uint8_t pwm, uint8_t dir)
{
if(idx >= M_MAX)
return;
motor_setze_ticks(idx, 0);
motoren[idx].start_time = motoren[idx].stop_time = millis();
motoren[idx].mot.setSpeed(pwm);
motoren[idx].mot.run(dir);
}
/*
* Fuer den Fall das zwischen motor_start und motor_stop die pwm nicht
* geaendert wird liesse sich aus der Zeit und der Strecke s.u. grob die
* Geschwindigkeit ermitteln (Be- und Entschleunigung vernachlaessgt).
*/
void motor_stop(uint8_t idx)
{
if(idx >= M_MAX)
return;
motoren[idx].mot.setSpeed(0);
motoren[idx].mot.run(RELEASE);
motoren[idx].stop_time = millis();
}
/*
* Liesst den ticks Wert interrupt safe.
*/
inline uint32_t motor_hole_ticks(uint8_t idx)
{
if(idx >= M_MAX)
return 0;
noInterrupts();
uint32_t tmp = motoren[idx].ticks;
interrupts();
return tmp;
}
/*
* Setzt den ticks Wert interrupt safe.
*/
inline void motor_setze_ticks(uint8_t idx, uint32_t val)
{
if(idx >= M_MAX)
return;
noInterrupts();
motoren[idx].ticks = val;
interrupts();
}
/*
*
*/
void motor_ticks_per_milli(uint8_t idx, unsigned long *res)
{
if(idx >= M_MAX)
{
*res = 0;
return;
}
*res = motor_hole_ticks(idx) / (millis() - motoren[idx].start_time);
}
/*
* Zu _einem_ Zeitpunkt werden alle Ticks/Milli ausgerechnet.
*/
void motor_ticks_per_milli_fuer_alle(unsigned long *res, uint8_t n)
{
unsigned long *b = 0;
unsigned long tstamp = 0;
if(n != M_MAX)
return;
b = res;
noInterrupts();
tstamp = millis();
while(b < res + n)
{
*b = motoren[b - res].ticks;
b++;
}
interrupts();
for(b = res; b < res + n; b++) {
*b = *b / (tstamp - motoren[b - res].start_time);
}
}
/*
* Rechnet die gefahrenen Zentimeter anhand der Ticks aus.
*/
double motor_strecke_gefahren(uint8_t idx)
{
if(idx >= M_MAX)
return 0.0;
return motor_hole_ticks(idx) * u_per_tick;
}
/*
* Rechnet fuer alle Motoren zu _einem_ Zeitpunkt die Strecke
* aus.
*/
void motor_strecke_fuer_alle(double *res, int n)
{
if(n != M_MAX)
return;
double *b = 0;
unsigned long tmps[M_MAX];
uint8_t idx = 0;
noInterrupts();
while(idx < M_MAX) {
tmps[idx] = motoren[idx].ticks;
idx++;
}
interrupts();
for(b = res; b < res + n; b++)
{
*b = tmps[b-res] * u_per_tick;
}
}
Arduino: 1.6.6 (Linux), Board: "Arduino Mega ADK"
Code:/home/georg/Arduino/sainsmart_car/fahren_mit timer/botty_4_motoren_1/botty_4_motoren_1.ino: In function 'void loop()':
botty_4_motoren_1:95: error: 'struct motor' has no member named 'run'
motoren[M_VL].run(RELEASE);
^
exit status 1
'struct motor' has no member named 'run'
Dieser Report hätte mehr Informationen mit
"Ausführliche Ausgabe während der Kompilierung"
aktiviert in Datei > Einstellungen.
Sorry ich habe das nicht gut erklärt. Du hast ein Feld mehr eingeführt dadurch das du die Zahlen eingefügt hast. Die Anzahl der Zeilen der "struct motor" muß mit der Anzahl der Spalten bei der Initialisierung übereinstimmen.
Code:#include <AFMotor.h>
/*
* Benamte Indexe. Wichtig: Alle Schleifen basieren auf der
* Annahme, dass M_VL das erste Element und mit 0 initialisiert
* ist. M_MAX muss ausserdem _immer_ das letzte Element sein da
* es die Groesse des Arrays s.u. bestimmt.
*/
enum motoren_e
{
M_VL = 0, // Motor vorne links
M_HL,
M_VR,
M_HR,
M_MAX
};
/*
* Infos zu einem Motor zusammenfassen.
*/
struct motor
{
AF_DCMotor mot;
uint8_t enc_pin;
// volatile damit der Compiler keine 'dummen' Optimierung macht.
volatile uint32_t ticks;
unsigned long start_time;
unsigned long stop_time;
};
/*
* Hier werden die PWM Channel - Encoder Pin Paare gesetzt.
* Restliche Elemente muessen ebenfalls initialisiert werden.
* Werte pruefen ob sie zu den Namen oben passen.
* Fuer die externen Interrupts sind die Pins 2,5,18-21
* erlaubt. Hardware anpassen.
*/
struct motor motoren[M_MAX] =
{
{ AF_DCMotor(4), 18, 0, 0, 0 }, // M_VL
{ AF_DCMotor(1), 19, 0, 0, 0 }, // M_HL
{ AF_DCMotor(3), 20, 0, 0, 0 }, // M_VR
{ AF_DCMotor(2), 21, 0, 0, 0 } // M_HR
};
// Anzahl der Encoderstriche fuer eine Umdrehung
const static double ticks_per_rev = 40;
const static double durchmesser = 6.5; // in cm;
const static double u_per_tick = (3.1415926535897932384626433832795 * durchmesser) / ticks_per_rev;
/*
* Interruptroutinen mit der gleichen Namenskonvention wie
* oben.
*/
void motor_isr_m_vl(void) { motoren[M_VL].ticks++; }
void motor_isr_m_vr(void) { motoren[M_VR].ticks++; }
void motor_isr_m_hl(void) { motoren[M_HL].ticks++; }
void motor_isr_m_hr(void) { motoren[M_HR].ticks++; }
void setup()
{
//Serial.begin(115200);
Serial.begin(9600);
for(uint8_t idx = M_VL; idx < M_MAX; idx++)
{
pinMode(motoren[idx].enc_pin, INPUT_PULLUP);
// motor_init_pos(idx);
}
/*
* Hier wird die Verbindung hergestellt zwischen Pin und Interruptroutine.
* Die Namen muessen zueinander passen.
* Durch CHANGE werden beide Uebergaenge beruecksichtigt.
*/
attachInterrupt(digitalPinToInterrupt(motoren[M_VL].enc_pin), motor_isr_m_vl, CHANGE);
attachInterrupt(digitalPinToInterrupt(motoren[M_HL].enc_pin), motor_isr_m_hl, CHANGE);
attachInterrupt(digitalPinToInterrupt(motoren[M_VR].enc_pin), motor_isr_m_vr, CHANGE);
attachInterrupt(digitalPinToInterrupt(motoren[M_HR].enc_pin), motor_isr_m_hr, CHANGE);
}
/*
* Alle Motoren an, bis eine Umdrehung erreicht ist. Es wird alle
* halbe Sekunde der Zustand der Ticks pro Motor ausgeben.
*/
void loop() {
unsigned long time;
motoren[M_VL].mot.setSpeed(130);
motoren[M_VL].mot.run(FORWARD);
for(time = millis(); time + 3000 >= millis(); ) {
delay(10);
Serial.print(motoren[M_VL].ticks);
Serial.print(" ");
}
Serial.println("");
motoren[M_VL].mot.setSpeed(0);
motoren[M_VL].mot.run(RELEASE);
delay(3000);
}