PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : RNMotor-Board - Frage zur I2C-Ansteuerung



Johannes
08.10.2004, 15:15
Moin,
@Frank, ich wollte mich ja nochmal bei dir melden, wenn ich das Motor-Board ausprobiert habe. Leider hat eine andere Bestellung etwas auf sich warten lassen, aber jetzt habe ich die nötigen Kabel und ein Application Board für mein Mega8 und konnte heute mit dem Testen beginnen.

Ich programmiere das Mega8-Modul mit dem AVRco (Pascal), der hier wohl nicht so verbreitet ist. Ich habe noch nicht so viel Ahnung von dem Ding, aber das wird schon noch kommen :-) Ich habe mir die Anleitung zum Motor-Board angeschaut, bei der ja zu jedem Befehl auch ein Bascom-Beispielcode vorhanden ist. Die Sprache kenne ich leider überhaupt nicht, aber mich hat gewundert, dass die Daten in ein Array gesetzt werden und dann nur das erste Element versendet wird:

Dim I2cdaten(6) As Byte
'Motorstrom auf 200mA begrenzen
I2cdaten(1) = 10 'Kennung muss bei RN-Motor immer 10 sein
I2cdaten(2) = 10 'Befehlscode
I2cdaten(3) = 0 '1 Parameter
I2csend &H56, I2cdaten(1),5 'Befehl wird gesendet

Oder verstehe ich das nur falsch? Vielleich könnte mir jemand noch etwas genauer beschreiben, was man genau an die Platine senden soll.

Was muss man eigentlich alles senden, damit sich der Motor zunächst einmal dreht (zum Testen, ob es überhaupt geht)? Reicht da "Motor einschalten", also Befehlscode 10?

Und noch eine Frage zum Verändern der Slave ID: Wird die für immer gespeichert? Das wäre gut...

Gruß
Johannes

Frank
08.10.2004, 18:11
Hi Johannes,

ja AVR-Pascal ist hier kaum verbreitet. Das liegt unter anderem auch daran das es nur für den Mega8 einen günstigen Compiler gibt und der Mega8 etwas knapp für größerer Bots ausgelegt ist.
Die I2C-Befehle von Bascom dürften nicht so sehr verschieden sein. Der Befehl mit dem Array ist nur eine Erleichterung da man so nicht alle Beides einzeln absenden muß. Man kann aber auch einen einfachen Befehl nutzen um einzelne Bytes zu senden.

Jeder RN-Motor Befehl besteht aus einer Sequenz von 5 Bytes. Das erste Byte muß immer 10 sein, danach kommt der Befehlscode und danach noch 3 Parameter.
Es gibt auch Befehle die weniger Parameter benötigen, dennoch müssen 5 Bytes gesendet werden. Der Inhalt der unbedeutend Bytes ist völlig egal.
Diese Methode wurde gewählt weil Bytesequenzen mit fester Länge platzsparender und sicherer verarbeitet werden konnten.

Welchen Bedeutung die einzelnen Bytes haben kannst du ja in der Tabell in der Anleitung recht gut ersehen.
Du mußt nur immer daran denken, ein Befehl besteht aus folgeder Sequenz:
i2cstart
slave Adresse senden (meißt Hex 56)
byte 1 senden (immer 10)
byte 2 senden (Befehlscode)
byte 3 senden (1. Parameter)
byte 4 senden (2. Parameter)
byte 5 senden (3. Parameter)
i2cstop

Damit sich der Motor erst mal dreht, müssen mindestens zwei Befehle gegeben werden:

Motor einschalten
Endlos drehen einschalten

Vorausgesetzt die Standard Stromstärke von 100mA reicht, würde dann schon Motor laufen. Ansonsten vorher noch Strom einstellen.

Wenn du die Slave Adresse durch einen I2C Befehl verstellst, dann ist dies dauerhaft verstellt. Also auch ein Reset oder Stromausfall stellt das nicht zurück. Du kannst diese natürlich durch einen neuen i2C-Befehl jederzeit wieder verstellen.

Sollte noch was unklar sein, einfach fragen.

Gruß Frank

Johannes
09.10.2004, 14:10
Moin Frank,
ich habe es jetzt endlich hinbekommen, die Motoren drehen sich. Alles wunderbar, aber ein paar Probleme gibt es noch. Beispielsweise funktioniert bei mir der Endlos-Befehl nicht. Stattdessen wird der Motor in der Position gehalten. Ich kann also momentan nur über Einzelschritte drehen, aber auch immer nur einen Motor einzeln. Beide Motoren zusammen (also mit dem ensprechenden Parameter auf 2 gesetzt) passiert auch nichts.

Außerdem wird bei mir alles ziemlich heiß, auch die Motoren, und die LED leuchtet, auch wenn ich den Strom ganz niedrig gestellt habe. Warum ist eigentlich dieser eine dreipolige IC (Spannungsregler?) rechts nicht am Kühlkörper dran?

Aber sonst bin ich zufrieden!
Gruß
Johannes

Frank
09.10.2004, 16:19
Hi Johannes,

irgendwo muß bei deiner Ansteuerung noch was falsch sein. Vielleicht schickst du die Befehle zu schnell hintereinander. Einige Befehle wie das STrom einstellen benötigen ein paar Milliisekunden. Mach mal probehalber zwischen jeden RN-Motor Befehl (also jede 5er Sequenz) 200 ms Pause. Die Zeiten kannst du später immer noch reduzieren, nur erst mal zum testen.
Der Endlos Befehl ist schon einer der wichtigsten, ich nehme den nur!

Das die LED leuchtet und das es zu warm wird deutet darauf hin da sdu einen zu hohen Strom eingestellt hast. Kann das sein? Teste Motoren ruhig mit niedrigerem Strom, gewöhnlich laufen die Motoren auch schon mit einem Bruchteil des Nennstromes.

Der Spannungsregler für die Logik wird gewöhnlich nicht sehr heiß, dahe rwar es nicht unbedingt notwendig diesen zu kühlen. Zudem war es platzmäßig auch schlecht anders zu lösen, da das Board sich genau an die Standard-Größenvorgaben halten mußte (Roboternetz-Norm).
Mit einem kleinen Winkel kann man ihn aber immer noch am Kühlkörper befestigen, aber wie gesagt gewöhnlich ist dies nicht notwendig.

Solltest du nicht weite rkommen kannst du mal den Pascal Ansteuercode von dir posten. VIelleicht steig ich ja durch, obwohl meine Pascal Zeiten schon lange zurück liegen.

Gruß Frank

Johannes
09.10.2004, 18:06
Moin Frank,
ja, irgendwo ist da noch der Wurm drin. Ich habe beispielsweise einmal während dem betrieb einen Motor abgeklemmt. Danach ging gar nichts mehr. Aber nachdem ich dann ein paar Wait-Zeiten geändert hatte, ging es wieder. Im moment passiert wiederum gar nichts. Hier mal mein Code


// Maximalstrom

Var
[...]
data5 : array[1..4] of byte;
MotorNumber : byte;

[...]
data5[1] := 1;
data5[2] := MotorNumber;
data5[3] := 50;
data5[4] := 0;
mDelay( 100 );
TWIout( 43, 10, data5 );

// Drehrichtung
data5[1] := 4;
data5[2] := MotorNumber;
data5[3] := 1;
data5[4] := 0;
mDelay( 200 );
TWIout( 43, 10, data5 );

// Voll-/Halbschritt
data5[1] := 14;
data5[2] := 0;
data5[3] := 0;
data5[4] := 0;
mDelay( 10 );
TWIout( 43, 10, data5 );

// Geschwindigkeit
data5[1] := 8;
data5[2] := MotorNumber;
data5[3] := 10;
data5[4] := 0;
mDelay( 10 );
TWIout( 43, 10, data5 );

// Einschalten
data5[1] := 10;
data5[2] := MotorNumber;
data5[3] := 0;
data5[4] := 0;
mDelay( 10 );
TWIout( 43, 10, data5 );

// Endlos drehen
data5[1] := 6;
data5[2] := MotorNumber;
data5[3] := 0;
data5[4] := 0;
mDelay( 10 );
TWIout( 43, 10, data5 );

Aber ich werde morgen noch ein wenig rumexperimentieren.

Gruß
Johannes

Frank
09.10.2004, 21:23
Hi Johannnes,

wenn während des Betriebes Motor abgeklemmt wird, schaltet RN-Motor den Motor derzeit aus. Also nicht abklemmen wenn du experimentierst.

Ich kenn leider die Syntax von dem Pascal Befehl TWIOUT nicht, aber irgendwie scheint mir die merkwürdig. Können da Werte teils als Byte und teils als Arry übergeben werden? Vielleicht kannst du mir mal Beschreibung von dem Befehl posten.

Wenn das wirklich so geht sind die Befehle so weitgehend in Ordnung, allerdings die Reihenfolge ist etwas ungünstig.
Den Strom solltest du immer als erstes einstellen, das hast du ja gemacht. In deinem Fall hast du den Strom auf 500mA pro Strang eingestellt.

Dann solltest du Halbschritt einschalten wenn du magst, ich lass das immer weg da ich meißt nur Vollschritt nehm.

Danach sollte man immer erst mal den Motor einschalten. Durch einschalten bewegt sich ja noch nix, nur der Motor bekommt schon Spannung. Du merkst das wenn du versuchst den Motor mit der Hand zu drehen.

Danach kann man dann Drehrichtung, Endlosschritt, Einzelschritt, Bestimmte Schrittanzahl, Schrittabruf, Stop usw. beliebig verwenden.

Den Delay Befehl solltest du hinter TWIOut einsetzten, ist einfach logisch übersichtlicher. Vor allem hinter Stromeinstellen und Einschalten sollte ruhig mal 200ms Pause sein. Hinte rdie anderen kannst du ja probehalber geringeren Delay Wert nehmen.

Tja mit I2C ist das manchmal so eine Sache, nicht alle Compiler unterstützen das I2C Protokoll 100% korrekt. Daher muß man manchmal etwas experimentieren. Eigentlich sind Delay´s nämlich überflüssig wenn der Compiler das Clock Signal richtig überwacht und auf den Slave wartet. Aber viele schnell gebastelte I2C-Libarys machen das nicht, daher ist eben das experimentiern notwendig.

Frank
09.10.2004, 21:50
Was mir noch merkwürdig erscheint, du verwendest die Slave ID Dezimal 43. Eigentlich kann die nicht richtig sein, denn es sind nur Slave ID´s zwischen Hex 50 und Hex 70 möglich :-)

Johannes
10.10.2004, 13:17
Moin Frank,
schon komisch, ich habe die Reihenfolgen geändert, und jetzt geht es. Naja, mir ist das alles noch ein Rätsel. Ich glaube, beim ARVco gibt es noch eine andere I2C Lib, viellleicht probiere ich die mal aus.

Bei dem TWIout ist es so, dass man die Adresse, dann den Register und dann die Daten angibt. Aber der Register ist ja praktisch das erste Byte, das gesendet wird. Zu den Adressen: Beim Pascal unterscheidet man nicht zwischen Sende- und Empfängeradresse. Die letzte Stelle von der Binärzahl wird immer weggelassen, wodurch natürlich eine neue Zahl entsteht.

Gruß
Johannes

Frank
10.10.2004, 15:32
Na fein das es geht.
Die beschriebene Sytax von TWIout ist aber in der Tat schon recht ungewöhnlich und gewöhnungsbedürftig. Nicht alle I2C-Slaves haben Register. Aber letzlich gehts ja auch damit.

Gruß Frank

Johannes
10.10.2004, 17:30
Jup, funktioniert jetzt alles. Ich habe auch die SlaveID von dem einen Board ändern können und kann nun 4 Motoren getrennt ansteuern. Demnächst werde ich den Code mal auf meine Website packen. Falls es doch noch einmal jemanden geben sollte, der mit Pascal das Motorboard ansteuern will.

Gruß
Johannes

Frank
10.10.2004, 20:04
Na fein. Wäre toll wenn du mir kleines Beispiel in Pascal posten oder mailen könntest, ich könnt das dann auch gleich in die Anleitung aufnehmen. Beispiele kann man nie genug haben.

Gruß Frank

Johannes
11.10.2004, 19:09
Ok, den Code poste ich, vorher muss ich aber noch ein paar Sachen ändern/verschönern. Ich denke, morgen habe ich das dann fertig.

Gruß
Johannes

Johannes
12.10.2004, 11:32
Moin,
ich habe den Code soweit fertig. Noch ein Hinweis: Da ich zwei Boards ansteuere, habe ich das ganze so programmiert, dass ich als Nummer für die Motoren eine Zahl von 1 bis 4 übergebe. Die Funktionen pStepperAdress() und pStepperMotor() ermitteln daraus die Adresse und die Motornummer, die gesendet werden soll. Das soll nicht irritieren. Bei Bedarf poste ich den Code nochmal einfacher für nur ein Board.


program ApplicationBoardTest;

{ $BOOTRST $00C00} {Reset Jump to $00C00}
{$NOSHADOW}
{ $W+ Warnings} {Warnings off}

Device = mega8, VCC = 5;
Import SysTick, SerPort, PWMport1, PWMport2, ADCPort, I2Cexpand, TWImaster, LCDmultiPort;

From System Import;

Define
ProcClock = 16000000; {Hertz}
SysTick = 10; {msec}
StackSize = $003C, iData;
FrameSize = $003C, iData;
SerPort = 19200, Stop1; {Baud, StopBits|Parity}
RxBuffer = 8, iData;
TxBuffer = 8, iData;
ADCchans = 2, iData;
ADCpresc = 128;
PWMres = 9; {bits}
PWMpresc = 64;
TWIpresc = TWI_BR400;
LCDmultiPort = I2C_TWI;
LCDrows_M = 2; {rows}
LCDcolumns_M = 16; {columns per line}
LCDtype_M = 44780;
I2Cexpand = I2C_TWI, $38; {Port0 = PCA9554 an Adresse $38}
I2CexpPorts = Port0; // I2Cexpand ports
{Port0 ist die IO Port Erweiterung (I2Cexpand) über den PCA9554 Baustein.
Durch Anschluß weiterer PCA9554 kann man bis Port7 erweitern}

Implementation

{$IDATA}

{--------------------------------------------------------------}
{ Type Declarations }

type


{--------------------------------------------------------------}
{ Const Declarations }
const
STEPPER_ADRESS1:byte = 43;
STEPPER_ADRESS2:byte = 44;
STEPPER_MOTOR1:byte = 0;
STEPPER_MOTOR2:byte = 1;
STEPPER_MOTOR3:byte = 2;
STEPPER_MOTOR4:byte = 3;
STEPPER_DIRECTION_RIGHT:byte = 0;
STEPPER_DIRECTION_LEFT:byte = 1;
STEPPER_MODUS_VS:byte = 0;
STEPPER_MODUS_HS:byte = 1;

{--------------------------------------------------------------}
{ Var Declarations }
{$IDATA}

var
{Portdefinition für Taster und LED's. Kein Unterschied zwischen normalen IO-Ports und I2Cexpand Ports}
LED1[@Port0, 4] : bit;
LED2[@Port0, 5] : bit;
LED3[@Port0, 6] : bit;
LED4[@Port0, 7] : bit;
Taster1[@Pin0, 3] : bit;
Taster2[@Pin0, 2] : bit;
Taster3[@Pin0, 1] : bit;
Taster4[@Pin0, 0] : bit;
M1F[@PortD, 6] : bit;
M1R[@PortD, 7] : bit;
M2F[@PortD, 5] : bit;
M2R[@PortD, 4] : bit;
temp:word;

{--------------------------------------------------------------}
{ functions }

procedure InitPorts;
begin
PortD:= %00001100;
DDRD:= %11110000;
PWMPort1:= 0;
PWMPort2:= 0;
end InitPorts;

procedure Init_I2C;
begin
LCDsetup_M(LCD_m1); //Initialisiere I2C LCD an Adresse $20
LCDcursor_M(LCD_m1, false, false); //Setze Cursor Blink off und Cursor visible off
DDR0:= $F0; //Port0.4 bis 7 als Ausgänge (LED's)
Port0:= $F0; //Port0.4 bis 7 auf 1 (LED's aus)
INP_POL0:= $00; //Polarität für Taster positiv
end Init_I2C;

function pStepperAdress( Motor:byte ) : byte;
begin
if (Motor = 0) or (Motor = 1) then return(STEPPER_ADRESS1); endif;
if (Motor = 2) or (Motor = 3) then return(STEPPER_ADRESS2); endif;
end pStepperAdress;

function pStepperMotor( Motor:byte ) : byte;
begin
if (Motor = 2) then return(0);
elsif (Motor = 3) then return(1);
else return(Motor); endif;
end pStepperMotor;

procedure StepperCurrency( Motor : byte; Currency : integer );
var data : array[1..4] of byte;
begin
data[1] := 1;
data[2] := pStepperMotor( Motor );
data[3] := byte( Currency DIV 10 );
data[4] := 0;
mDelay( 100 );
TWIout( pStepperAdress( Motor ), 10, data );
mDelay( 100 );
end;

procedure StepperModus( Motor, Modus : byte );
var data : array[1..4] of byte;
begin
data[1] := 14;
data[2] := Modus;
data[3] := 0;
data[4] := 0;
mDelay( 100 );
TWIout( pStepperAdress( Motor ), 10, data );
mDelay( 100 );
end;

procedure StepperOn( Motor : byte );
var data : array[1..4] of byte;
begin
data[1] := 10;
data[2] := pStepperMotor( Motor );
data[3] := 0;
data[4] := 0;
mDelay( 200 );
TWIout( pStepperAdress( Motor ), 10, data );
mDelay( 200 );
end;

procedure StepperStop( Motor : byte );
var data : array[1..4] of byte;
begin
data[1] := 3;
data[2] := pStepperMotor( Motor );
data[3] := 0;
data[4] := 0;
mDelay( 200 );
TWIout( pStepperAdress( Motor ), 10, data );
mDelay( 200 );
end;

procedure StepperOff( Motor : byte );
var data : array[1..4] of byte;
begin
data[1] := 9;
data[2] := pStepperMotor( Motor );
data[3] := 0;
data[4] := 0;
mDelay( 10 );
TWIout( pStepperAdress( Motor ), 10, data );
mDelay( 10 );
end;

procedure StepperRotate( Motor, Speed, Direction : byte );
var data : array[1..4] of byte;
begin
data[1] := 4;
data[2] := pStepperMotor( Motor );
data[3] := Direction;
data[4] := 0;
TWIout( pStepperAdress( Motor ), 10, data );
mDelay( 100 );
data[1] := 8;
data[2] := pStepperMotor( Motor );
data[3] := Speed;
data[4] := 0;
TWIout( pStepperAdress( Motor ), 10, data );
mDelay( 100 );
data[1] := 6;
data[2] := pStepperMotor( Motor );
data[3] := 0;
data[4] := 0;
TWIout( pStepperAdress( Motor ), 10, data );
mDelay( 100 );
end;

procedure StepperStep( Motor, Direction : byte );
var data : array[1..4] of byte;
begin
data[1] := 4;
data[2] := pStepperMotor( Motor );
data[3] := Direction;
data[4] := 0;
TWIout( pStepperAdress( Motor ), 10, data );
mDelay( 30 );
data[1] := 7;
data[2] := pStepperMotor( Motor );
data[3] := 0;
data[4] := 0;
TWIout( pStepperAdress( Motor ), 10, data );
end;

procedure StepperSteps( Motor, Speed, Direction : byte; Steps : integer );
var data : array[1..4] of byte;
begin
data[1] := 4;
data[2] := pStepperMotor( Motor );
data[3] := Direction;
data[4] := 0;
TWIout( pStepperAdress( Motor ), 10, data );
mDelay( 30 );
data[1] := 8;
data[2] := pStepperMotor( Motor );
data[3] := Speed;
data[4] := 0;
TWIout( pStepperAdress( Motor ), 10, data );
mDelay( 100 );
data[1] := 5;
data[2] := pStepperMotor( Motor );
data[3] := LO( Steps );
data[4] := HI( Steps );
TWIout( pStepperAdress( Motor ), 10, data );
end;


{--------------------------------------------------------------}
{ Main Program }
{$IDATA}

begin
InitPorts;
Init_I2C;
EnableInts;
LCDclr_m(LCD_m1);
M1F:= true;
M1R:= false;
M2F:= true;
M2R:= false;
PWMport1:= 400;
PWMport2:= 400;
temp:=GetADC(1);
write(LCDout_m, ' mindrobots ');
LCDxy_m(LCD_m1, 0, 1);

//Einstellen des Motorstroms
StepperCurrency( STEPPER_MOTOR1, 200 );
//Vollschritt oder Halbschritt
StepperModus( STEPPER_MOTOR1, STEPPER_MODUS_VS );
//Einschalten
StepperOn( STEPPER_MOTOR1 );
//15 Schritte mit langsamer Geschwindigkeit, links herum
StepperSteps( STEPPER_MOTOR1, 200, STEPPER_DIRECTION_LEFT, 15 );
mDelay( 5000 );
//15 Schritte mit höherer Geschwindigkeit, links herum
StepperSteps( STEPPER_MOTOR1, 50, STEPPER_DIRECTION_LEFT, 15 );
mDelay( 1000 );

end ApplicationBoardTest.

Bei der StepperSteps()-Funktion muss man daran denken, dass die Funktion nicht so lange wartet, bis die Schritte ausgeführt wurden. Das muss man selbst mit einem Wait machen. Theoretisch könnte man noch aus der Geschwindigkeit errechnen, wie lange das Drehen dauert und dem entsprechend ein Wait setzen.

Gruß
Johannes

Frank
14.10.2004, 10:13
Danke Johannes,

ist perfekt so. Ich hab das Pascal-Beispiel gleich unter Angabe des Autors/Nickname in die Anleitung übernommen, hoffe das ist dir Recht.

Falls noch jemand ein Beispiel für anderen Controller hat, kann ich es gerne auch übernehmen. Wie gesagt, Beispiele kann man nie genug haben.

Gruß Frank

Johannes
14.10.2004, 10:55
Moin Frank,
wunderbar, allerdings habe ich mir eben die Anleitung nochmal runtergeladen (http://www.robotikhardware.de/download/rnmotor.pdf) und konnte kein Pascal-Beispiel finden. Bin ich blind? :-)

Gruß
Johannes

P.S. Es wäre schön, wenn du beim Autor noch meine Webadresse http://algorithms.mindrobots.de reinschreiben würdest. Wie ich schon geschrieben habe, werde ich dort demnächst was zu dem Board und der Ansteuerung schreiben.

Frank
14.10.2004, 11:06
Hatte es wohl falsch abgelegt. Nu ist es aber aktuallisiert. Webseite steht auch drin.

Gruß Frank

Johannes
14.10.2004, 11:14
Wunderbar!

Gruß
Johannes