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.
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.
@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