Kleines C-Prog geht nicht

Gast #5441522
Lesenswert?

Guten Morgen,
brauche mal eure Hilfe bei einem kleinen Syntax-Fehler. Hier mal der 
Code:

int main(void)
{
   DDRA = 0x0F;
   while(1)
  {
    PORTA = 3;
    _delay_ms(500);
    PORTA &= (0<<PA0);
    _delay_ms(500);
  }
}

LED an Port A.0 blinkt ordnungsgemäß wie erwartet nur die LED A.1 blinkt 
gleichzeitig mit. Sie dürfte eigentlich mit der LED an A.0 an gehen und 
dann nicht mehr ausgehen.
Verwende gerade das Studio 6.2 mit dem internen C-Compiler. Warum wird 
überhaupt ein Schiebebefehl verwendet um Ports zu schalten? Bei BASCOM 
ist es doch so einfach. Habe aber gerade eine zeitkritische Anwendung.
Danke HH
Gast #5441533
Lesenswert?

ICh weiß ja nicht welchen Controller du verwendest. Eigentlich sollte
1
    PORTA = 3;
2
    _delay_ms(500);
3
    PORTA &= (0<<PA0);
4
    _delay_ms(500);

funktionieren. Möglicherweise liegt so etwas vorkonfiguriertes wie JTAG 
auf dem Pin?
Gast #5441536
Lesenswert?

1
int main(void)
2
{
3
   DDRA  |= (1<<PA1)|(1<<PA0);
4
   PORTA |= (1<<PA1)|(1<<PA0);
5

6
   while(1)
7
   {
8
    PINA = (1<<PA0);
9
    _delay_ms(500);
10
   }
11
}


Funktioniert mit allen etwas moderneren AVR.
z.B. ATTiny85 ATMega328p
Gast #5441537
Lesenswert?

Hallo Curby,
dachte auch dass PORTA &= (0<<PA0); funktioniert und nur A.0 
ausschaltet.
Aber eben nur der Befehl PORTA &= ~(1<<PA0); funktioniert. Dann mache 
ich es eben so. ;-(
Habe einen Tiny461A dran.
#5441546
Lesenswert?

Curby23523 N. schrieb:
> ICh weiß ja nicht welchen Controller du verwendest. Eigentlich sollte
>
1
>     PORTA = 3;
2
>     _delay_ms(500);
3
>     PORTA &= (0<<PA0);
4
>     _delay_ms(500);
5
>
>
> funktionieren. Möglicherweise liegt so etwas vorkonfiguriertes wie JTAG
> auf dem Pin?

Klar funktioniert das, deshalb Compiliert das auch ohne Fehler. Aber 
auch für dich, überlege mal was
1
PORTA &= (0<<PA0)

Bewirken wird. Grade ein Anfänger wird das falsch interpretieren ;)
#5441584
Lesenswert?

Ich gehe mal davon aus, dass du als Anfänger etwas auf dem Schlauch 
stehst.
Hier mal die Hausaufgabe fürs Wochenende:
1
PORTA &= (0<<PA0)

Überleg mal was die Einzelschritte im folgenden ergeben. Ob die 
Datentypen stimmen, kann ich nicht sagen, aber es geht ums Prinzip:
1
uint x = (0<<PA0);
2
PORTA = PORTA & x;

Ich denke es hilft dir viel, wenn du deinen Denkfehler selber erkennst.
Mir hat zum Einstieg das gcc Tutorial hier gut geholfen (Verändern von 
Registerinhalten).
https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Ver.C3.A4ndern_von_Registerinhalten
#5441685
Lesenswert?

HH schrieb:
> Irgendwie bewirkt der Befehl:
> PORTA &= (0<<PA0);
> dass der gesamte PORTA mit Null überschrieben wird und nicht nur PA.0!

schreibe dir es doch einfach auf:

PA0 ist Bit 0

also setzt du die 8 Portsbits auf 0000 0000
Es ist Bit 0 auf 0 gesetzt richtig aber auch Bit 1 bis 7

und nun undierst du alle Bits auf Port A mit 0
was soll da rauskommen ausser 8x NULL?
Maske 0000 0000
PortA 1010 xxxx
---------------
gibt 0000 0000

weswegen wird es wohl richtig mit
PORTA &= ~(1<<PA0)
Maske 0000 0001
negiert 1111 1110
PortA 1010 xxxx
---------------
gibt 1010 xxx0

gemacht?

alle bleiben wie sie sind ausser BIT 0 das wird auf 0 gesetzt gelöscht
Gast #5441789
Lesenswert?

HH schrieb:
> LED an Port A.0 blinkt ordnungsgemäß wie erwartet nur die LED A.1 blinkt
> gleichzeitig mit. Sie dürfte eigentlich mit der LED an A.0 an gehen und
> dann nicht mehr ausgehen.

MaWin schrieb:
> Schon HH schrieb, dass es nicht funktioniert. Das gibt dir nicht zu
> Denken ?

Ja, wieder zu schnell gelesen, er möchte, dass der PORT nicht angeht. 
Dann bitte in C-Lektüre nachschauen, wie man Bits manipuliert.
Gast #5441860
Lesenswert?

HH schrieb:

> Trinke erst mal einen Kräuter!

Entweder Programmieren oder Saufen. Beides gleichzeitig funktioniert 
eher nicht, da wirst du nur das Saufziel erreichen...

Geringe Mengen Alkohol können allerdings wirklich auch die Kreativität 
beim Programmieren erhöhen, so meine Beobachtung. Sie lösen etwas die 
Verhaftung in eingefahrene Bahnen.

> Warum wird
> überhaupt ein Schiebebefehl verwendet um Ports zu schalten? Bei BASCOM
> ist es doch so einfach.

Zum Beispiel solche Veklemmungen... Nein, hier wird kein Schiebebefehl 
verwendet. Geschoben wird hier nur zur Compile-Zeit, letztlich ergeben 
die Ausdrücke zur Laufzeit eine konstante Zahl.

Du solltest dich mit dem grundsätzlichen Unterschied zwischen 
Kompilezeit und Laufzeit auseinandersetzen. Wenn du den begriffen hast, 
wird das klarer.
Gast #5443047
Lesenswert?

Oh c-hater. Auch schon getrunken?

Mir ist jetzt schon klar, wie es funktioniert. Werde aber wohl nicht 
erfahren, warum Mann einen schlüssigen Befehl, um ein Bit zusetzen (also 
mit Oder maskiert - mit oder ohne senkrechten Strich) nicht eine 
ähnliche Syntax verwendet, um ein Bit zu löschen. In Bascom und in 
Pascal für PICs geht es doch. Aber hier wird erst das Einer-Komplement 
mit der Tilde gebildet.
Oder seht euch mal CodeVision an.
Aber die Variante von A (Gast) ganz oben gefällt mir ganz gut.

Danke und schönen Sonntag noch.
Gast #5443064
Lesenswert?

HH schrieb:
> Mir ist jetzt schon klar, wie es funktioniert. Werde aber wohl nicht
> erfahren, warum Mann einen schlüssigen Befehl, um ein Bit zusetzen (also
> mit Oder maskiert - mit oder ohne senkrechten Strich) nicht eine
> ähnliche Syntax verwendet, um ein Bit zu löschen. In Bascom und in
> Pascal für PICs geht es doch. Aber hier wird erst das Einer-Komplement
> mit der Tilde gebildet.
> Oder seht euch mal CodeVision an.


Ich bin mir nicht sicher, ob du mit dieser Einstellung in der C und C++ 
Welt gut aufgehoben bist.

Denn da wird nicht alles mundgerecht vorgekaut, wie es deiner Meinung 
nach sein sollte. Hier muss man die die Dinge selber machen.

HH schrieb:
> Aber die Variante von A (Gast) ganz oben gefällt mir ganz gut.
Passt ins Bild.
Gast #5443325
Lesenswert?

Es ist fast wie immer. Einige sachliche Antworten und viele am Kern 
vobei.
Anfängern oder besser hier > Umsteigern < wird unterstellt, dass sie es 
besser lassen sollten.

Ja, Arduino F, vielleicht bist Du in diesem Forum falsch mit Deiner 
Einstellung. Deine Beiträge an anderer Stelle "passen ins Bild"!

Also weiter so .....
#5443360
Lesenswert?

HH schrieb:
> Oh c-hater. Auch schon getrunken?
>
> Mir ist jetzt schon klar, wie es funktioniert.

Das glaube ich nicht.

> Werde aber wohl nicht erfahren, warum Mann einen schlüssigen Befehl, um
> ein Bit zusetzen (also mit Oder maskiert - mit oder ohne senkrechten
> Strich) nicht eine ähnliche Syntax verwendet, um ein Bit zu löschen.

Weil das nicht primär die "Befehle" sind, um ein Bit zu setzen oder zu 
löschen. Sie werden nur unter anderem auch dafür verwendet.
#5443366
Lesenswert?

M. K. schrieb:
> Dass du das "^" vergessen hast vor dem "=" ;)

Nein, hat er nicht. Bei halbwegs aktuellen AVRs kann man ein Bit in 
einem PORT-Register toggeln, indem man eine 1 in das entsprechende Bit 
des dazugehörigen PIN-Registers schreibt.

Sven A. schrieb:
> was spricht denn dagegen?

Mir würde nur einfallen, dass es bei Portierung auf einen älteren AVR zu 
fiesen, schwer zu findenden Fehlern kommen könnte.
#5443370
Lesenswert?

Arduino F. schrieb:
> HH schrieb:
>> Mir ist jetzt schon klar, wie es funktioniert. Werde aber wohl nicht
>> erfahren, warum Mann einen schlüssigen Befehl, um ein Bit zusetzen (also
>> mit Oder maskiert - mit oder ohne senkrechten Strich) nicht eine
>> ähnliche Syntax verwendet, um ein Bit zu löschen. In Bascom und in
>> Pascal für PICs geht es doch. Aber hier wird erst das Einer-Komplement
>> mit der Tilde gebildet.
>> Oder seht euch mal CodeVision an.
>
>
> Ich bin mir nicht sicher, ob du mit dieser Einstellung in der C und C++
> Welt gut aufgehoben bist.

Die eigentliche Frage ist doch (und die finde ich berechtigt):

Warum benötige ich zur Manipulation einzelner Bits einen Datentyp 
uint8_t, der ja vorzeichenlose Ganzzahlen repräsentieren soll?

Was er möchte ist ein Datentyp, der einfach nur eine Ansammlung von Bits 
darstellt, und der dann Operationen zum Setzen / Löschen einzelner Bits 
anbietet.

Und da ist er doch in der C++-Welt sehr gut aufgehoben ;-) Damit könnte 
man auch die Problematik des RMW lösen ...
Gast #5443376
Lesenswert?

Wilhelm M. schrieb:
> Warum benötige ich zur Manipulation einzelner Bits einen Datentyp
> uint8_t, der ja vorzeichenlose Ganzzahlen repräsentieren soll?
> Was er möchte ist ein Datentyp, der einfach nur eine Ansammlung von Bits
> darstellt, und der dann Operationen zum Setzen / Löschen einzelner Bits
> anbietet.
> Und da ist er doch in der C++-Welt sehr gut aufgehoben ;-) Damit könnte
> man auch die Problematik des RMW lösen ...

Dir steht es natürlich frei eine Bitstruct darüber zu legen. Der 
Compiler muss trotzdem einen Zugriff auf das Register machen, das bei 
diesem Controller funktioniert. Das wird hier eben 8 Bit sein.
Wobei es bei manchen uC auch Bereiche gibt die Bit adressiert sind.
Gast #5443380
Lesenswert?

Hallo Wilhelm M. schrieb:
> Warum benötige ich zur Manipulation einzelner Bits einen Datentyp
> uint8_t, der ja vorzeichenlose Ganzzahlen repräsentieren soll?

Die Frage ist doch schnell beantwortet, da ein Port eines AVR µC 8 Bit 
Breit ist.
Das "soll" ist doch eher ein "ist".
Und wenn in "C" programmiert wird, dann kann man sich seine eigenen 
Zugriffe auf Pins basierend auf einem Port programmieren, so dass einem 
die Syntax zusagt.

Hier geht es wirklich nur um das Aussehen, um mehr nicht.
Also warum der ganze Stress?
#5443381
Lesenswert?

Anderer Frank schrieb:
> Wilhelm M. schrieb:
>> Warum benötige ich zur Manipulation einzelner Bits einen Datentyp
>> uint8_t, der ja vorzeichenlose Ganzzahlen repräsentieren soll?
>> Was er möchte ist ein Datentyp, der einfach nur eine Ansammlung von Bits
>> darstellt, und der dann Operationen zum Setzen / Löschen einzelner Bits
>> anbietet.
>> Und da ist er doch in der C++-Welt sehr gut aufgehoben ;-) Damit könnte
>> man auch die Problematik des RMW lösen ...
>
> Dir steht es natürlich frei eine Bitstruct darüber zu legen.

Ja, das mache ich auch schon sehr lange so ... und mittlerweile ist so 
etwas in rudimentärer Form (eine Ansammlung von Bits) auch in der 
libstdc++ enthalten ;-)
#5443388
Lesenswert?

Karl M. schrieb:
> Hallo Wilhelm M. schrieb:
>> Warum benötige ich zur Manipulation einzelner Bits einen Datentyp
>> uint8_t, der ja vorzeichenlose Ganzzahlen repräsentieren soll?
>
> Die Frage ist doch schnell beantwortet, da ein Port eines AVR µC 8 Bit
> Breit ist.

8-Bit als ein Byte zusammengefasst: ja.

Aber diese 8-Bit immer als Ganzzahl interpretieren: klares nein.

> Hier geht es wirklich nur um das Aussehen, um mehr nicht.

Nein. Es geht darum, den Code ausdrücken zu lassen, was er bedeutet. Und 
da ist es eben unsinnig, 8-Bits eines Ports zu einer Ganzzahl zusammen 
zu fassen. Es sind einfach nur 8 (zusammenhanglose) Bits.

> Also warum der ganze Stress?

Welcher Stress?
#5443395
Lesenswert?

Wilhelm M. schrieb:
> Karl M. schrieb:
>> Hallo Wilhelm M. schrieb:
>>> Warum benötige ich zur Manipulation einzelner Bits einen Datentyp
>>> uint8_t, der ja vorzeichenlose Ganzzahlen repräsentieren soll?
>>
>> Die Frage ist doch schnell beantwortet, da ein Port eines AVR µC 8 Bit
>> Breit ist.
>
> 8-Bit als ein Byte zusammengefasst: ja.
>
> Aber diese 8-Bit immer als Ganzzahl interpretieren: klares nein.

Tut man ja auch weder bei dem &, noch bei dem <<. Man shiftet ja Bits 
und nicht eine Ganzzahl. Man interpretiert sie in dem Fall lediglich als 
einen zusammengehörigen Block aus 8 Bits, aber nicht als Ganzzahl.
#5443398
Lesenswert?

Rolf M. schrieb:
> Wilhelm M. schrieb:
>> Karl M. schrieb:
>>> Hallo Wilhelm M. schrieb:
>>>> Warum benötige ich zur Manipulation einzelner Bits einen Datentyp
>>>> uint8_t, der ja vorzeichenlose Ganzzahlen repräsentieren soll?
>>>
>>> Die Frage ist doch schnell beantwortet, da ein Port eines AVR µC 8 Bit
>>> Breit ist.
>>
>> 8-Bit als ein Byte zusammengefasst: ja.
>>
>> Aber diese 8-Bit immer als Ganzzahl interpretieren: klares nein.
>
> Tut man ja auch weder bei dem &, noch bei dem <<. Man shiftet ja Bits
> und nicht eine Ganzzahl. Man interpretiert sie in dem Fall lediglich als
> einen zusammengehörigen Block aus 8 Bits, aber nicht als Ganzzahl.

Dann lies mal genauer:

http://en.cppreference.com/w/c/language/operator_arithmetic
Beitrag #5443421 wurde von einem Moderator gelöscht.
#5443797
Lesenswert?

Arduino F. schrieb:
> Wilhelm M. schrieb:
>> Ja, das mache ich auch schon sehr lange so ...
> Zeigen!
> (bitte)

Grundsätzlich: primitive Datentypen sind die kleinsten "Bausteine". 
Allerdings, sind sie in vielen Einsatzbereichen sowohl im Wertebereich 
wie auch in der Operationenmenge unpassend. Zudem tragen sie keinerlei 
Semantik. Das ist der Tatsache geschuldet, das wir es mit einer 
general-purpose Sprache zu tun haben (irgendetwas muss die Sprache ja 
bereitstellen). Für seine eigene, projektspezifische Problemdomäne 
sollte man sich eigene Datentypen maßgeschneidert erstellen.

Nun, es geht darum, einen Typ zu erstellen, der ein Byte, d.h. eine 
geordnete Sammlung von 8-Bits darstellt. Da diese 8-Bits zunächst keine 
Semantik haben, macht es keinen Sinn

i) implizite Typkonversionen von/zu arithmetischen DT zu erlauben
ii) arithmetische Rechenoperationen

zuzulassen. Als erlaubte Operationen kommt zunächst mal nur die 
Kopierzuweisung und die Kopierkonstruktion in Betracht.

Also
1
auto x = Byte{42};
2
auto y = x;
3
y = x;
soll möglich sein, aber
1
auto z1 = 2 * x;
2
auto z2 = x + y;
soll nicht möglich sein.

Die erste Möglichkeit, wäre ein Typ
1
struct Byte {
2
       uint8_t value;
3
};
zu schreiben. Eine andere Möglichkeit ist:
1
enum class byte : uint8_t {};

Ich habe die zweite Variante genommen, denn es gab mal einen 
gcc-Optimizer isra-Bug, der mit der ersten manchmal Schwierigkeiten 
hatte. Die scoped-enum Variante benutzt auch libstdc++.

Jetzt kann man anfangen, seine gewünschten Operation hinzuzufügen, etwa:
1
template<typename IType>
2
constexpr byte operator<<(byte b, IType shift) noexcept {
3
   return byte(static_cast<uint8_t>(b) << shift);
4
}
oder
1
constexpr bool any(std::byte b) noexecpt {
2
     return b != std::byte{0};
3
}
und andere.
Wobei man die Operatoren dann besser als SFINAE-enabled templates 
schreibt, falls man auch noch andere Bit-Typen hat, wie etwa Nibble, 
o.ä.


*Achtung*: an dieser Stelle kommt regelmäßig der Einwand:
Was, sooo viel Aufwand. Das mache ich doch mit C und int einfacher.

Dazu ist natürlich zu bedenken:

a) den Aufwand macht man sich für seine N-Projekte genau einmal.
b) für den Klienten ist es eben nicht aufwändiger
c) ich gewinne Sicherheit im Sinne von "ease to use, hard to misuse"
d) der Code wird expressiver

Das war es in Kürze und nur ganz grob.
#5443837
Lesenswert?

Peter D. schrieb:
> Sven A. schrieb:
>> was spricht denn dagegen? das steht sogar im datenblatt der controller,
>> bei denen es möglich ist, das man so den pin toggled.
>
> Dagegen spricht, daß jeder erstmal grübelt, wenn er nicht das Datenblatt
> im Kopf hat. Ich benutze es daher absichtlich nicht. In der Regel ist
> der Einspareffekt unerheblich.

Schaltest Du dann bei einem unechtem RMW auch die Interrupts aus und 
legst die anderen Kerne still?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren