Hallo liebe µC Gemeinde,
in einem Projekt auf einem ATmega644PA bin ich gerade dabei 9 DC Motoren
auf verschiedenen Ports/Pins anzusteuern. Mit Hilfe von 9 Software-PWMs
für die Motoren, soll über eine berechnete Bitmaske die Ansteuerung
erfolgen (zu sehen im Beitrag: Soft-PWM).
Nun gilt es, die 9 IO-Pins zusammen zufassen, so dass die entstandene
Bitmaske der 9 Soft-PWMs auf die Motor-Pins angewendet werden kann. In
einem Artikel über "verschiedene Ports nach Bitmaske setzen"
(http://www.roboternetz.de/community/threads/59212-Ports-nach-Bitmaske-setzen)
fand ich eine Lösung, die funktioniert:
1
#include<avr/io.h>
2
#define ARRAY_LENGTH 9 //wieviele Pins sollen angesteuert werden
Nun wurde für das einfachere 'Handling' der verschiedenen Motoren-Pins
ein 'struct' eingeführt (wie im
Beitrag "#define in C", 3. letzer Eintrag, zu
lesen).
1
//auf einen Port-Pin wie auf eine Variable zugreifen:
Damit kann ich nun die verschiedenen Motoren in der 'main()'
initalisieren und aufgrund der obigen Methoden mit einer Bitmaske die
entsprechenden Pins in den Port auf 'on'(1) setzen.
1
intmain(void){
2
MOTOR1_DDR=OUTPUT;
3
MOTOR2_DDR=OUTPUT;
4
MOTOR3_DDR=OUTPUT;
5
MOTOR4_DDR=OUTPUT;
6
MOTOR5_DDR=OUTPUT;
7
MOTOR6_DDR=OUTPUT;
8
MOTOR7_DDR=OUTPUT;
9
MOTOR8_DDR=OUTPUT;
10
MOTOR9_DDR=OUTPUT;
11
12
uint16_tneueBitmaske=0b010001010;//dez:138 - setzt bit 2,7 und 11 also PORTC.5, PORTC.4 und PORTA.4
13
setPorts(neueBitmaske);
14
15
while(1){/*do nothing*/}
16
}
Vielleicht kann es sich der eine oder andere denken, aber nun sollten
die beiden Arrays 'Port_Mask' und 'Pin_Mask' ersetzt werden. Dazu wird
ein Array mit den eigenen definiertes MOTORx Makros benutzt. Glein zu
Beginn eingebaut, doch schon bei der Initialisierung kam es zu Problemen
=> "initializer element is not constant" (anscheinend geht das nicht vor
Ausführung der 'main()'-Methode).
Nach dem Umschreiben der 'setPort'-Methode auf das neue Motoren-Array
wurde alles compiliert und über den Simulator gestartet.
1
voidsetPorts(uint16_tbitMaske){
2
for(uint8_ti=0;i<ARRAY_LENGTH;i++){
3
if((bitMaske&Potenz_Zahlen[i])>0){
4
Motor_Mask[i]=1;
5
}else{
6
Motor_Mask[i]=0;
7
}
8
}
9
}
Und was soll ich sagen: Das Compilieren hat funktioniert, nur es kommt
überhaupt nichts dabei heraus. Bedeutet: kein einziger Pin der drei
Ports wird gesetzt. Beim recherchieren bin ich auf Aussagen gestoßen die
besagen, dass die selbsterstellten Bits nicht in Arrays genutzt werden
können. Ist das richtig? Vielleicht bin ich mit meinem Vorhaben ja
komplett auf dem Holzweg und jemand von euch hat gute Vorschläge. Freue
mich über jede Reaktion. Danke.
Beste Grüße Uli
Also ich mach das immer folgendermaßen. Die zu setzenden Bits definiere
ich mir mit einer sauberen Beschreibung anhand ihres Stellenwertes
(Umwandlung Binär/Hex), zB, würde das Datenblatt so aussehen:
BIT Funktion
7 xx
6 yy
..
1 Motor an
1
#define ENGINE_ON 0x01
2
#define 0x02
3
#define 0x04
4
#define 0x08
5
#define 0x10
6
#define YY 0x0020
7
#define XX 0x0040
Wenn ich nun das Bit 7 (xx) setzen will, reicht ein
1
REGISTER|=XX;
bzw. zum löschen:
1
REGISTER&=~(ENGINE_ON)
Also ähnlich wie bei dir, nur würde ich den ganzen Overhead mit den
Structs und diesen unleserlichen Makros nicht so machen.
Hallo Uli,
kannst du deinen Code schrittweise debuggen?
Sind deine Funktionen in einem Header nochmal bekannt gemacht?
BitboolSet wird erst verwendet, und dann beschreibst du, was Sie machen
soll.
Hey bitschupser,
zunächst vielen Dank für deine Antwort. Ja das mit den Structs bzw.
unleserlichen Makros war eine frühere Idee den Code "leserlicher" zu
machen. Und was leserlich ist, das definiert ja jeder für sich auf
andere Weise ;-) wie ich schon häufig hier im Forum lesen konnte.
Der jetzige Ansatz, der ja auch funktioniert, bezieht sich direkt auf
die PORT-Register (z.B. PORTC) bzw. Pin-Nummern (PC6). Und hinter PC6
steckt im Prinzip nichts anderes wie 0x20. Heißt, wenn ich eine
Bitmaske bekomme, soll diese auf die entsprechenden Pins der
verschiedenen Port-Register angewendet werden. Also gehe ich diese
Bitmaske Schritt für Schritt durch (in der setPorts-Methode) und setze
aufgrund der Reihenfolge in den vordefierten Arrays die Pins. In Deinem
Vorschlag würde ich das dann auch so machen. Ich benutze eine
FOR-Schleife, iteriere über die Bitmaske und rufe entsprechend einer
vordefinierten Reihenfolge die Funktion setzten oder löschen auf.
D.h. Du würdest das Maskieren mit der eingehenden Bitmaske ebenfalls mit
einer Schleife lösen!?
-----------------------------------
Hey Stromverdichter,
ebenfalls Danke für deine Antwort. Jepp, hab den Code tatsächlich
schrittweise debugged. Nur irgendwie macht der Debugger von Atmel Studio
(v7.0) irgendwie was er will. Im C-Code überspringt er meist
irgendwelche Zeilen oder zeigt Variablen-Inhalte nicht an. Im
Disassembler passieren sehr viele Dinge! Allerdings fehlt mir dabei die
Routine zu wissen, was in Assembler passiert.
Für dieses kleine Test-Projekt habe ich keinen Header angelegt, das
liegt alles in einer C-Datei. Ist das ein Problem? Du meintest die
Funktion BIT_BOOL_SET würde erst verwendet und anschließend erst
beschrieben. Das verstehe ich jetzt nicht ganz.
Die Inline Methoden: BIT_BOOL_SET, BIT_SET, BIT_CLEAR sind alle
ein Teil der Methode setPorts. Das ist im Prinzip die einzige Methode,
die ich im Code habe. Die anderen sind ja nur via inline rausgezogen.
Und dann ist da noch die main -Methode. Darin führe ich die setPorts
-Methode aus. Wie an 'bitschupser' geschrieben, wird darin über die
Bitmaske iteriert und dann ein Motor an und aus geschalten. Der soll
aber (im 2. Schritt, siehe oben) eben nicht über die Register und Pin
Bezeichnungen angesprochen werden, sondern über das SBIT, welches (aus
meiner Sicht) Gleiches impliziert. Also:
1
PORTC|=(1<<PC6);
ist das gleiche wie:
1
//...
2
#define MOTOR2 SBIT( PORTC, 6 )
3
//...
4
MOTOR2=1;
Nur aus irgendeinem Grund möchte das der Compiler aber nicht so wie ich
:-) und wäre die Frage, ob es einen Grund dafür gibt, den ich vielleicht
übersehen habe.
Danke nochmals für eure Hinweise.
Schöne Ostergrüße
Uli
Uli S. schrieb:> //auf einen Port-Pin wie auf eine Variable zugreifen:> struct bits {> uint8_t
Bitfields sind für Registermapping verführerisch, aber grundfalsch. Der
C-Standard gibt nicht vor, wie Bitfields zu implementieren sind. Das
erste deklarierte Bit kann oben sein oder auch unten.
Der Basisdatentyp könnte auch durchaus nach Gusto des Compilers schonmal
wechseln. Beispielsweise, weil uint8_t letztlich ein char ist, Bitfields
aber nur auf int-Datentypen definiert sind.
Bitfields sind dafür gedacht, Speicher zu sparen - nicht für IO. Kurzum,
laß es einfach.
Hey Nop,
das ist mal eine Aussage und zudem noch untermauert - vielen Dank dafür.
Das Hilft mir in diesem und auch für zukünftige Projekte weiter.
Ich hab noch etwas in 'quelloffenen' HowTos nach den Bitfeldern gesucht,
um mir dann noch ein Bild darüber zu machen, wo der Einsatz Sinn macht.
Und tatsächlich gibt es einige Dinge zu beachten:
[[http://www.c-howto.de/tutorial-strukturierte-datentypen-bitfelder.html]]
wie hier zu sehen.
Gleichzeitig habe ich nun meinen Fehler entdeckt und warum der oben
beschriebene Versuch, ein selbst erstelltes IO-Bitfield zu maskieren,
nicht gelungen ist. Nicht nur, wie Du Nop schon schriebst, dass es für
die Bitfields keine C-Standard Vorgabe gibt, sondern wie in diesem
Beitrag zu lesen:
[[http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/015_c_strukturen_013.htm]]
- Bitfelder belegen keine adressierbare Speicherstelle und können daher
auch den Adressoperator *&* nicht nutzen.
Vielen Dank nochmal für die Unterstützung. Manchmal braucht man eben nur
einen Wink mit dem ...
Beste Grüße Uli
Uli S. schrieb:> Also:>> PORTC |= (1<<PC6);>> ist das gleiche wie:>> //...> #define MOTOR2 SBIT( PORTC, 6 )> //...> MOTOR2 = 1;
Nöh. Es gibt keinerlei Definition von SBIT die aus der Ausdruck
"MOTOR2 = 1" irgendwie "PORTC |= (1<<PC6)" zaubern kann. Das kann also
unmöglich das gleiche sein.