Forum: Mikrocontroller und Digitale Elektronik ATmega 16: Zwei Ports zusammenfassen, geht das ?


von Ingo (Gast)


Lesenswert?

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

von Benedikt K. (benedikt)


Lesenswert?

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;

von Ingo (Gast)


Lesenswert?

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

von Benedikt K. (benedikt)


Lesenswert?

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.

von Ingo (Gast)


Lesenswert?

Hi Benedikt,

super, Danke für die Erklärung.

Gruss Ingo

von Peter D. (peda)


Lesenswert?

Nimm ein fach ne 16Bit Variable und gib sie dann auf beide Ports aus 
(PORTA = val; PORTB = val/256).


Peter

von Axel R. (Gast)


Lesenswert?

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.

von Willi W. (williwacker)


Lesenswert?

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

von Benedikt K. (benedikt)


Lesenswert?

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.

von Willi W. (williwacker)


Lesenswert?

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

von Uhu U. (uhu)


Lesenswert?

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.

von Benedikt K. (benedikt)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Uhu U. (uhu)


Lesenswert?

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


von Peter D. (peda)


Lesenswert?

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

von Uhu U. (uhu)


Lesenswert?

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.

von Thomas (kosmos)


Lesenswert?

@Ingo: Willst du 2 Ports gleichzeitig umschalten oder einfach nur 2 
Register als Word nutzen?

von Jox (Gast)


Lesenswert?

mal ne Frage ...

Warum macht ihr das nicht mit nem Pointer? Das ist doch ein 8 Bit 
Rechner.

von Ingo (Gast)


Lesenswert?

@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
Noch kein Account? Hier anmelden.