Forum: Mikrocontroller und Digitale Elektronik Verständnisprobleme bei der integer Zahlenübergabe an Port


von Andre M. (averagez)


Lesenswert?

Hallo miteinander,

ich hab mich im Forum angemeldet um den Rat der community zu suchen. mir 
stellt sich ein Problem für das ich schon die Suchfunktion genutzt habe. 
Allerdings glaube ich, dass mein Problem von dermaßen grundlegender 
Natur ist, dass ich es nicht hinbekomme eine Lösung zu finden. :D

Folgende Situation: Ich möchte mit einem Atmega 328P ein Lauflicht an 8 
LED's an Port D realisieren. Das ganze fängt bei LED 1 an PIND0 an und 
endet an PIND7 mit LED 8, wobei immer nur eine LED leuchten soll. Am 
Ende angekommen sollen die LEDs wieder absteigend leuchten, usw. usw. 
Hier mein Code:

[c]
#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <util/delay.h>
#include <math.h>

int main(void) {

    DDRD = 0xFF;
    int tmpLED = 0;
    int led = 0;
    int portCount = 0;

  while (1) {

      if(led>0 && led<7 && led>tmpLED){
        portCount=(int) pow(2,led);
        PORTD =portCount;
        tmpLED = led;
        led++;
        _delay_ms(2000);
      }
      if(led>0&&led<7 && led<tmpLED){
        portCount=(int) pow(2,led);
        PORTD = portCount;
        tmpLED = led;
        led--;
        _delay_ms(2000);
      }
      if(led==0){
        portCount= (int)pow(2,led);
        PORTD = portCount;
        tmpLED=led;
        led++;
        _delay_ms(2000);
      }
      if(led==7){
        portCount=(int) pow(2,led);
        PORTD = portCount;
        tmpLED=led;
        led--;
        _delay_ms(2000);
      }
    }
}
[c]

Jetzt mal unabhängig davon, dass das auch eleganter geht und ich 
Bitmasken benutzen könnte, liegt mein Verständnisproblem in den Zeilen, 
in denen ich portCount = (int) pow(2, led) berechne und dies dann an den 
Port übergebe. (int) steht hier noch zusätzlich. Auch ohne diesen 
Typecast läuft das Programm falsch.
Eigentlich müssten doch die Werte, die ich auf diese Weise an Port D 
übergebe 1, 2, 4, 8, 16, 32, 64, 128, 64, 32, etc. sein (so jedenfalls 
wenn ich den Code einfach nur in einer IDE für C Programme ausgeben 
lasse). Was mein Lauflicht allerdings daraus zu machen scheint ist 
folgendes (ich gebe hier jetzt die Dezimalzahl an, die sich aus der 
Binärdarstellung der LEDs ablesen lässt): 1, 2, 3, 7, 15, 31, 63, 128, 
63, 31, etc.
Warum läuft das nun falsch? Ich dachte immer, dass wenn ich eine integer 
Zahl an einen Port übergebe, dass dann das Register mit den 
entsprechenden Bits gefüllt wird. Wenn ich direkt 128 an PORTD übergebe 
leuchtet nur die letzte LED, wenn ich 4 übergebe, dann nur die 3te usw.
Könnt ihr mir vielleicht helfen und sagen, warum die pow() funktion die 
Werte falsch berechnet? oder liegt das Problem vielleicht sogar ganz 
woanders?

von Sebastian R. (sebastian_r569)


Lesenswert?

pow() gibt einen Double (Gleitkommazahl) zurück, keinen Integer. Deshalb 
musst du ihn noch auf int casten.

von Einer K. (Gast)


Lesenswert?

Sebastian R. schrieb:
> Deshalb
> musst du ihn noch auf int casten.
Tut er/sie/es.

Zudem ist der Cast als impliziter Cast gleichwertig zum expliziten.
(mal abgesehen von dem dokumentierenden Faktor.)

von Andre M. (averagez)


Lesenswert?

Hallo und danke für die Antwort,

das hab ich auch vermutet, aber das typecasten scheint keine Veränderung 
zu bringen. Aus irgendeinem Grund z.b. wird (int) pow(2, 2) als 3 
ausgegeben, nicht als 4.

von Joachim B. (jar)


Lesenswert?

Andre M. schrieb:
> Aus irgendeinem Grund z.b. wird (int) pow(2, 2) als 3
> ausgegeben, nicht als 4.

wegen float 3,9999
wie wäre es mit runden +0,5 vor int?

von HildeK (Gast)


Lesenswert?

Ich tippe mal auf ein Rundungsproblem.
Wenn du das tatsächlich über die Potenzfunktion machen willst, dann 
probiere mal:
   portCount=(int) (pow(2,led)+0.1);
Ich würde Shiftoperationen verwenden und kein 'int' sondern 'uint8_t',
Alternativ zu Shift: Multiplikation oder Division mit 2.

von Joachim B. (jar)


Lesenswert?

HildeK schrieb:
> Ich tippe mal auf ein Rundungsproblem.

:-)

HildeK schrieb:
> Ich würde Shiftoperationen verwenden und kein 'int' sondern 'uint8_t',
> Alternativ zu Shift: Multiplikation oder Division mit 2.

+1

von hacker-tobi (Gast)


Lesenswert?

Hi,

statt pow könntest du auch einfach
portcount = 2^led verwenden und das dann ausgeben oder mit dem BV Makro 
arbeiten.
Auch das umschalten hoch/runterzählen kannst du vereinfachen, z.b. in 
dem du tmpled einfach nur auf 0 oder 1 setzt und damit die Laufrichtung 
vorgibst.

Gruss tobi

von Andre M. (averagez)


Lesenswert?

Ihr habt alle recht, ich hatte echt ein Brett vorm Kopf o.O

was mein Problem mit obigem Code löst ist, dass ich portCount als 
round(portCount) runden muss, dann wird zur nächst höheren Integer Zahl 
aufgerundet.
Seltsam finde ich das ganze dennoch, in Codeblocks generiert mir (int) 
portCount den aufgerundeten Wert, entsprechend habe ich einen 
Rundungsfehler als Ursache verworfen. AtmelStudio scheint das aber 
vollkommen egal zu sein, dass ich eigentlich bei 3.999999 liege.

Vielen lieben Dank jedenfalls! Jetzt wo mir das einleuchtet will ich das 
mal lieber mit Shift Operationen erledigen :)

VG

von Sebastian (Gast)


Lesenswert?

Andre M. schrieb:
> auch eleganter geht

Code is evil. In den vier Zweigen deines Programms passiert mehr oder 
weniger das gleiche, und dieses gleiche sollte nur einmal beschtrieben 
werden.

Völlig unabhängig davon dass (int) pow(2,led) besser als (1<<led) 
ausgedrückt wird ...

LG, Sebastian

von hacker-tobi (Gast)


Lesenswert?

Hi nochmal,

eine einfache Multiplikation / Division mit 2 funktioniert nicht ganz, 
da 2*0 = 0 und 2*1 = 2, portd.1 wird damit nicht angesprochen. Daher 
2^led

Gruss tobi

von my2ct (Gast)


Lesenswert?

Wie kommt man auf die Idee, eine eigentlich simple Bit-Operation (shift) 
per float-Operation umzusetzen. Das ist wie ein Schuss von hinten durch 
die Brust ins Auge.

von Sebastian (Gast)


Lesenswert?

hacker-tobi schrieb:
> Daher 2^led

???

von Mario M. (thelonging)


Lesenswert?

Ich fasse zusammen:
1
#ifndef F_CPU
2
#define F_CPU 16000000UL
3
#endif
4
5
#include <avr/io.h>
6
#include <util/delay.h>
7
#include <math.h>
8
9
int main(void) {
10
11
    DDRD = 0xFF;
12
    int led = 0;
13
    int dir;
14
15
    while (1) {
16
        PORTD = (1 << led);
17
        _delay_ms(2000);
18
19
        if (led == 0) dir = 1;
20
        else if (led == 7) dir = -1;
21
        led += dir;
22
    }
23
}

von Peter D. (peda)


Lesenswert?

Will man verschiedene Muster darstellen, eignet sich auch gut ein Array.
Das läßt sich dann besonders leicht ändern und man muß keine Formel 
dafür entwickeln.
Hier mal ein Beispiel:
Beitrag "Re: AVR Sleep Mode / Knight Rider"
Das Array steht in pattern.c.

von HildeK (Gast)


Lesenswert?

hacker-tobi schrieb:
> eine einfache Multiplikation / Division mit 2 funktioniert nicht ganz,
> da 2*0 = 0 und 2*1 = 2, portd.1 wird damit nicht angesprochen. Daher
> 2^led

Ja, die Division und Multiplikation ist ein wenig umständlicher. Man 
muss für die erste LED halt die 1 setzten und dann kann man mit einer 
Multiplikation mit 2 die weiteren ansprechen.
Das zu erkennen hatte ich dem TO eigentlich zugetraut.

von Sebastian W. (wangnick)


Lesenswert?

hacker-tobi schrieb:
> 2^led

Es geht um Sourcecode in C. Was hat die bitweise exklusive 
Oder-Operation mit dem Thema zu tun?

LG, Sebastian

von Nosnibor (Gast)


Lesenswert?

my2ct schrieb:
> Wie kommt man auf die Idee, eine eigentlich simple Bit-Operation (shift)
> per float-Operation umzusetzen. Das ist wie ein Schuss von hinten durch
> die Brust ins Auge.
Indem man darauf hereinfällt, dass überall von Zweierpotenzen die Rede 
ist, und dann keine andere Potenzfunktion in C findet. Zwei historisch 
bedingte Fehlkonstruktionen.

von Einer K. (Gast)


Lesenswert?

Nosnibor schrieb:
> Indem man darauf hereinfällt, dass überall von Zweierpotenzen die Rede
> ist, und dann keine andere Potenzfunktion in C findet. Zwei historisch
> bedingte Fehlkonstruktionen.

Interessant!
Wie ist es denn .....
Bist du ein Gegner des Binärsystems, oder hast es nur nicht verstanden?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Nosnibor schrieb:
> my2ct schrieb:
>> Wie kommt man auf die Idee, eine eigentlich simple Bit-Operation (shift)
>> per float-Operation umzusetzen.
> Indem man darauf hereinfällt, dass überall von Zweierpotenzen die Rede
> ist, und dann keine andere Potenzfunktion in C findet. Zwei historisch
> bedingte Fehlkonstruktionen.
Ich sehe es eher so, dass man keinen Plan hat, was da tatsächlich auf 
der Hardware passieren muss, damit aus einer Zahl ein binäres Muster 
wird.

> my2ct schrieb:
>> Das ist wie ein Schuss von hinten durch die Brust ins Auge.
Besonders, wenn man dann nicht mal weiß, welche Datentypen man da 
eigentlich verwendet und welche eklatanten Einschränkungen 
(Rundungsfehler) sie haben. Der nächste logische Schritt wäre, wenn 
jetzt noch ein float-Wert auf Gleichheit abgefragt wird.

Andre M. schrieb:
> _delay_ms(2000);
Wenn man in jedem Zweig der if-Abfrage diesen Programmfehler einbaut 
(ein 2-Sekunden-Delay IST vergeudete Rechenzeit und damit ein 
Programmfehler), dann könnte man ihn dort rausnehmen und statt der 
Copy-Paste-Orgie nur 1 einziges Mal hinten anfügen.

: Bearbeitet durch Moderator
von Wolfgang (Gast)


Lesenswert?

HildeK schrieb:
> Ja, die Division und Multiplikation ist ein wenig umständlicher. Man
> muss für die erste LED halt die 1 setzten und dann kann man mit einer
> Multiplikation mit 2 die weiteren ansprechen.

Auch wenn man statt der eigentlich erforderliche Schiebeoperation eine 
Multiplikation verwendet, dokumentiert man damit nur, dass ein heftiges 
Defizit bei Grundlagen der Bitoperationen besteht und man nie auf 
Assemblerebene ohne HW-Multiplizierer programmiert hat. Die "wahren" 
Programmierer werden jetzt argumentieren, dass der Compiler diesen 
Schwachsinn schon wieder weg optimieren wird - Recht haben sie 
wahrscheinlich. Blindes Vertrauen in die Fähigkeiten der Leute, die den 
Compiler gebaut haben, statt gleich zu schreiben, was man eigentlich 
möchte.

von HildeK (Gast)


Lesenswert?

Wolfgang schrieb:
> Auch wenn man statt der eigentlich erforderliche Schiebeoperation eine
> Multiplikation verwendet, dokumentiert man damit nur, dass ein heftiges
> Defizit bei Grundlagen der Bitoperationen besteht und man nie auf
> Assemblerebene ohne HW-Multiplizierer programmiert hat.

Ich habe die alternative Variante im Wissen genannt, dass eine 
Integer-Mulitplikation oder Division mit Faktor 2 vom Compiler sicher in 
eine Schiebeoperation umgewandelt wird. In der Hoffnung, dass es der TO 
leichter versteht, der ja von der Potenzfunktion ausgegangen ist und dem 
Schiebeoperationen weniger geläufig sind. Wie gesagt, es war als 
Alternative genannt; nachdem ich auf die Schiebeoperation hingewiesen 
hatte!

von Gerald K. (geku)


Angehängte Dateien:

Lesenswert?

Viele Wege führen bekanntlich nach Rom:
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <unistd.h>
4
5
unsigned char mask[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
6
unsigned char *pmask[]={".......*","......*.",".....*..","....*...","...*....","..*.....",".*......","*......."};
7
8
unsigned char port;
9
unsigned char i;
10
11
int main()
12
{
13
14
        while (1)
15
        {
16
                printf("%s\r\n",pmask[i&7]); // portsimulation
17
18
                port=mask[i++&7];
19
                sleep(1);
20
        }
21
}

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.