Hallo ihr Lieben, habe eine Frage bzgl. einer Zusammenfassung von 2 Ports in C. Ich fange gerade an, mich mit dem ATmega16 anzufreunden. Eigentlich geht alles ganz gut, dennoch ergibt sich die eine oder andere Frage dazu, wo mir bisher die SUFU auch nicht helfen konnte. Lange Rede, kurzer Sinn, hier meine Frage. Ist es möglich, beim Mega16 z.B. Port A und Port B zu einem 16 Bit Ausgangsregister zusammen zu fassen ? Im Instruction Set Summary des Datenblatts lese ich zwar was von Wortverarbeitung, aber ist es möglich ein Wort direkt auf Port A und Port B auszugeben ? Da sich meine Erfahrungen im Umgang mit Microcontrollern eher auf 8051, Assembler und reine 8 Bit Anwendungen beschränken, würde ich zum Start der Programmierung in C, gerne mal ein 16 Bit Lauflicht programmieren. In Assembler, kommt mir das Carry zur Hilfe, den Übergang zwischen zwei Ports zu finden. Wie jedoch geht das in C ? Eine Idee meinerseits, ist eine Struktur zu deklarieren, bin mir jedoch nicht sicher ob sich das in der Praxis bewährt, daher würde ich mich über reichlich INPUT freuen. Beste Grüsse Ingo
Lass es lieber. Es geht zwar mit einigen Tricks, ist aber nicht ganz einfach und vor allem nicht ganz ungefährlich. Mach es doch einfach so: PORTA=wert&255; PORTB=wert/256;
Hi Benedikt, Erst mal Danke für die Info, mach ich gerne so, wenn Du mir kurz erläuterst, was da passiert. Bei der ersten Zeile, maskiere ich das Register mit 255, damit schalte ich die letzte LED im Port aus ok. Was passiert dann ? ich teile einen Wert x durch 256 und schreibe das in den anderen Port ? 255 / 256 = 0,996.... Versteht der Mega16 das als eine 1, um den ersten Pin zu setzen ?? Beste Grüsse Ingo
Ingo wrote: > Bei der ersten Zeile, maskiere ich das Register mit 255, damit schalte > ich die letzte LED im Port aus ok. Ich verstehe nicht ganz was du meinst, daher erkläre ich das ganze mal: wert ist ein 16 bit Wert. Dieser Wert wird auf 2 Bytes (also 2x 8bit) aufgeteilt, und jedes Byte wird an einen Port geschrieben. > 255 / 256 = 0,996.... Nein: Eine Division mit Ganzzahlen wird immer abgerundet. In diesem Fall kommt also 0 raus. Um jeweils einen Pin nacheinander einzuschalten, musst du in wert am Anfang eine 1 schreiben (dann ist PortA0 an) und in jedem Schritt den Wert mit 2 multiplizieren: 1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768 Dadurch werden nacheinander immer der nächste Pins auf 1 geschaltet.
Hi Benedikt, super, Danke für die Erklärung. Gruss Ingo
Nimm ein fach ne 16Bit Variable und gib sie dann auf beide Ports aus (PORTA = val; PORTB = val/256). Peter
Habe mitgelesen. Eine Frage dazu: Peter Dannegger wrote: > Nimm ein fach ne 16Bit Variable und gib sie dann auf beide Ports aus > (PORTA = val; PORTB = val/256). > > > Peter Muss man zwingend nach char casten, oder werden automatisch die niederwertigen 8Bit von 'val' am PORTA ausgegeben? PORTA = (unsigned char)val; Danke AxelR.
Wenn Du "nur bastelst", dann ist es egal ob Du castest. Wenn es ein berufliches Werk ist, dann sollte Software-Qualität eine Rolle spielen und dann ist casten Pflicht. Es zeigt Dir in einem Jahr oder einem Kollegen, dass Du wusstest was Du tust und das das Programm nicht nur zufällig läuft. Das mit dem Dividieren würde ich auf alle Fälle lassen. Mein Compiler (CodeVisionAVR) z.B. macht daraus eine echte Division (da mein Controller ein mega 8 keine Division kann wird eine 100 byte teure Unterroutine erzeugt und eingebunden). 8 mal nach rechts shiften macht dasselbe und geht zumeist schneller. Das ist wichtig, damit zwischen den Ausgaben auf die Ports die Pause möglichst kurz ist (für ein simples Lauflicht ist das auch wurscht, aber vielleicht ist das ja die Vorstufe zu einem "richtigen Programm". Alternative: unsigned int w; // 16 bit w = lauflicht(); // schreibt was rein PORTA = (w>>8)&oxff; // die oberen 8 bits nach unten schieben // und ausmaskieren PORTB = w&oxff; So sollte es eigentlich schnell auf allen Compilern laufen. Merke: Lauflicht ist ein gutes Hello-World-Programm für Mikrocontroller. "Ältere" mögen lästern, aber gute Programmierer erinnern sich, dass sie auch mal klein angefangen haben. Und auch die müssen immer noch was neues lernen und dann sehen die auch wieder alt aus. Ciao Willi Wacker - lernt gerade wieder etwas neues
Willi Wacker wrote: > Das mit dem Dividieren würde ich auf alle Fälle lassen. Mein Compiler > (CodeVisionAVR) z.B. macht daraus eine echte Division (da mein > Controller ein mega 8 keine Division kann wird eine 100 byte teure > Unterroutine erzeugt und eingebunden). Dann besorg dir mal ein besseren Compiler... Ein gutes Programm beschreibt nicht das was der Compiler machen soll, sondern das was das Programm machen soll. Alles andere sind nur Bastellösungen. Wenn du schon mit gutem Programmierstil anfängst, dann gehört sowas auch dazu. Jeder andere Compiler den ich kenne, macht aus einer Division durch eine 2er Potenz bei eingeschalteter Optimierung eine Schiebeoperation, falls kein schneller Divisionsbefehl vorhanden ist.
Aber wir arbeiten hier immerhin auf Bitebene und wenn ich irgendwelche Bits irgendwohin haben will, dann bastel ich nicht mit einer Division und einer Multiplikation rum, auch wenn hier nach langem Hin-und-Her letztlich dasselbe passiert, sondern ich mache es direkt. Damit wird dann auch deutlich, was ich wirklich will, nämlich Bits verschieben und keine Arithmetikaufgabe lösen. Das hab ich oben nicht so deutlich gemacht, aber es ist ja noch nicht zu spät. Zum Thema Compiler: Schade dass ich keinen guten Compiler habe. Aber das will der Kunde so, und der ist König. Warum er das will, hat er mir nicht gesagt. Zum Thema Programmierstil: Kann jeder so halten wie er will oder der Scheffe es will oder der Kunde Zum Thema "Dann besorg Dir mal...": Ich habe eine freundliche Antwort gegeben und erwarte, dass man mich ebenfalls freundlich anspricht / schreibt. Ciao Willi Wacker
Willi Wacker wrote: > Wenn es ein berufliches Werk ist, dann sollte Software-Qualität eine > Rolle spielen und dann ist casten Pflicht. Schüttel, brrrrr. Typecasts sind so mit das Fürchterlichste, was C zu bieten hat. Sie machen Software unportabel und unpflegbar, weil Typinformation über den gesamten Quelltext verstreut wird und jeder Typ damit gegen den Strich gebürstet wird, bei gleichzeitiger Maulsperre für den Compiler. Man sollte union benutzen, um dieser Sammlung von Fallen aus dem Weg zu gehen:
1 | typedef union { |
2 | unsigned int b16; |
3 | struct _bit8 { |
4 | unsigned char l; |
5 | unsigned char h; |
6 | } b8; |
7 | } bit_8_16; |
8 | |
9 | ... |
10 | bit_8_16 wert; |
11 | ... |
12 | wert.b16 = 1; |
13 | |
14 | ... |
15 | wert.b16 <<= 1; |
16 | PORTA = wert.b8.l; |
17 | PORTB = wert.b8.h; |
Dann ist die Typinformation zentral in dem typedef verpackt, kann leicht an neue Gegebenheiten angepaßt werden und der Compiler kann seinen Senf dazu geben, wenn er feststellt, daß da was suspekt oder falsch ist.
Willi Wacker wrote: > Ich habe eine freundliche Antwort > gegeben und erwarte, dass man mich ebenfalls freundlich anspricht / > schreibt. So freundlich war das aber nicht: Willi Wacker wrote: > Das mit dem Dividieren würde ich auf alle Fälle lassen. Wiso ich einen Anfänger erstmal von der Schiebeoperation abrate: Es gibt einige Compiler (zu denen glaube ich auch der Codevison gehört), bei denen folgendes nicht funktioniert, da die Compiler entgegen dem was ANSI C machen sollte die rechte Seite als char und nicht als int rechnen. unsigned int c; unsigned char a,b; ... c=a|(b<<8); c=a|(b*256); funktioniert dagegen, da der Compiler die 256 als int erkennt und daher mit int rechnet. Nachdem ich ein paarmal drauf reingefallen bin, habe ich mir die Schieberei abgewöhnt, da der erzeugte Code bei fast allen Compilern gleich ist. @Uhu Uhuhu Bist du sicher, dass die Lösung überall gleich funktioniert ? Soweit ich weiß, ist ein struct nicht eindeutig definiert. Die Reihenfolge der Variablen ist dem Compiler überlassen. (Ich nutze diese Variante auch, aber auch nur weil meine Software nur auf einem AVR läuft, und ziemlich sicher nicht auf einen anderen Controller portiert wird.)
Uhu Uhuhu wrote: > und der Compiler kann seinen Senf > dazu geben, wenn er feststellt, daß da was suspekt oder falsch ist. Er wird einen Teufel tun und einfach die Bytes entsprechend seiner internen Byteorder zusammenpappen. Du hast also mit der Union die Gewähr, daß Dein Programm zu 50% läuft oder auch nicht. Mit dem Schieben und Maskieren hast Du aber zu 100% ein richtiges Programm. Eine Union nimmt man nur dann, wenn einem die Portabilität egal ist. Peter
@ Peter Dannegger: > Er wird einen Teufel tun und einfach die Bytes entsprechend seiner > internen Byteorder zusammenpappen. Das wird er in der Tat nicht. Nur, wenn man einen bestehenden Code auf ein anderes System portiert, dann ist bekannt, ob sich die Byteorder ändert, oder nicht - man muß dann eben eine Stelle anpassen, statt viele... > Eine Union nimmt man nur dann, wenn einem die Portabilität egal ist. Angenommen, Du willst einen für 8 Bit in cast-Technik geschriebenen Code auf eine 16-Bit-Maschine bringen: Bei der Union-Methode paßt man die Datenstruktur an und der caster, bzw. dessen armer Nachfolger kann sich damit amüsieren, die betreffenden Stellen in sämtlichen Quellmodulen zu suchen und anzupassen.
Uhu Uhuhu wrote: > Das wird er in der Tat nicht. Nur, wenn man einen bestehenden Code auf > ein anderes System portiert Muß nicht mal ein anderes Target sein. Es kann schon reichen, nen anderen Compiler oder andere Compilerversion zu nehmen. > Angenommen, Du willst einen für 8 Bit in cast-Technik geschriebenen Code > auf eine 16-Bit-Maschine bringen: Na und ? Dann läuft er dort genauso fehlerfrei. Wenn dort die Ports 16 Bit breit sind, ist natürlich die Aufteilung Blödsinn, die muß dann komplett weg. Wäre aber mit der Union nicht anders. Peter
Und wenn das ganze Problem mit skaliert wird? Statt 16 32 Bit ausgegeben werden sollen? Generell ist es eine sehr schlechte Idee, Typinformation von der Typdefinition abzutrennen und dann womöglich noch über hunderte Quelltextmodule zu streuen. Nicht umsonst wurde bei C++ eine neue Cast-Syntax neben der alten, von C geerbten eingeführt, die auffälliger und vor allem mit Semantik angereichert ist, um diesen neuralgischen Punkt etwas zu entschärfen.
@Ingo: Willst du 2 Ports gleichzeitig umschalten oder einfach nur 2 Register als Word nutzen?
mal ne Frage ... Warum macht ihr das nicht mit nem Pointer? Das ist doch ein 8 Bit Rechner.
@Thomas O, Hi Thomas und der Rest natürlich auch, was heißt umschalten ? Ich möchte ein Lauflicht uber zwei ports jagen, also LED technisch 1, 2, 4...65535 einschalten. Hauptsächlich geht es mir um den Übergang von Port A nach Port B innerhalb einer Schleife, ohne Unterbrechung. Dazu gab es hier schon einige Vorschläge, die ich aufgenommen habe. Hatte leider noch keine Zeit alles zu testen, werde dies aber im Laufe der Woche tun. In Assembler, auf 8051 Controllern, habe ich dies über das Carryflag, den RRC und RLC realisiert. Ich wusste einfach nur nicht wie ich das in C realisieren könnte, was sich jedoch mittlerweile geändert hat. Ich bin sehr erfreut über das rege Interesse an diesem Thread und möchte noch mal allen danken, die sich hier beteiligt haben, um mir auf die Sprünge zu helfen... Best Regards Ingo
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.