Forum: Compiler & IDEs Ungehorsamer ATtiny24


von Marco S (Gast)


Lesenswert?

Hallo.

Ich habe da ein Problem. Ich nehme einen ATtiny24, betreibe den mit ca. 
4V, hänge 2 LED dran (PortB 2, PortA 5) und an einen Pin (PortA 4) einen 
Signalgenerator als Eingangsignalgeber. Der c-code darauf ist:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <inttypes.h>
4
5
#define toggleLED1() do{if (PORTB & (1 << PB2)) PORTB &= ~(1 << PB2); \
6
else PORTB |= (1 << PB2); }while(0)
7
#define toggleLED2() do{if (PORTA & (1 << PA5)) PORTA &= ~(1 << PA5); \
8
else PORTA |= (1 << PA5); }while(0)
9
10
volatile int8_t  clk = 0;
11
12
ISR(PCINT0_vect) {
13
  if (PINA & (1 << PCINT4)) {
14
    /* steigende Flanke */
15
    toggleLED1();
16
    clk += 1;
17
    if (clk & 0x01) {
18
      toggleLED2();
19
    }
20
  }
21
}
22
23
int main() {
24
  DDRB |= (1 << PB2);        /* LED1 */
25
  DDRA |= (1 << PA5);        /* LED2 */
26
  GIMSK = ( 1 << PCIE0 );
27
  PCMSK0 = ( 1 << PCINT4 );  /* Pin-Change-Interrupt an PA4 */
28
  sei();
29
  for ( ;; ) {}
30
  return 0;
31
}

, compiliert mit avr-gcc (GCC) 4.2.0 unter Verwendung von avr-libc 
1.4.6.

Ziel zunächst soll es sein, dass LED1 mit halber Taktfrequenz und LED2 
mit 4-tel Taktfrequenz blinkt. Dazu werte ich den Pin-Change-Interrupt 
aus und LED1 blinkt wie erwartet. LED2 sollte durch die UND-Verknüpfung 
nur jede 2. steigende Flanke ausnutzen. Das Problem hierbei: LED2 blinkt 
mit gleicher Frequenz/Phasenlage wie LED1. Gerade so, als ob "if (clk & 
0x01)" nicht gegeben sei. Ändere ich die Bedingung für LED2 auf "if (clk 
& 0x02)", dann blinkt LED2 gar nicht mehr. Bei "if (clk < 16)" wiederum 
Dauerblinken, obwohl beim Übersteichen des Zahlenbereiches Pausen 
auftreten sollten. Vom generierten assemblercode sieht es gar nicht so 
schlecht aus (hier nur Interrupt-Routine).
1
ISR(PCINT0_vect) {
2
  68:   1f 92           push    r1
3
  6a:   0f 92           push    r0
4
  6c:   0f b6           in      r0, 0x3f        ; 63
5
  6e:   0f 92           push    r0
6
  70:   11 24           eor     r1, r1
7
  72:   8f 93           push    r24
8
        if (PINA & (1 << PCINT4)) {
9
  74:   cc 9b           sbis    0x19, 4 ; 25
10
  76:   13 c0           rjmp    .+38            ; 0x9e <__vector_2+0x36>
11
                /* steigende Flanke */
12
                toggleLED1();
13
  78:   c2 9b           sbis    0x18, 2 ; 24
14
  7a:   02 c0           rjmp    .+4             ; 0x80 <__vector_2+0x18>
15
  7c:   c2 98           cbi     0x18, 2 ; 24
16
  7e:   01 c0           rjmp    .+2             ; 0x82 <__vector_2+0x1a>
17
  80:   c2 9a           sbi     0x18, 2 ; 24
18
                clk += 1;
19
  82:   80 91 60 00     lds     r24, 0x0060
20
  86:   8f 5f           subi    r24, 0xFF       ; 255
21
  88:   80 93 60 00     sts     0x0060, r24
22
                if (clk & 0x01) {
23
  8c:   80 91 60 00     lds     r24, 0x0060
24
  90:   80 ff           sbrs    r24, 0
25
  92:   05 c0           rjmp    .+10            ; 0x9e <__vector_2+0x36>
26
                        toggleLED2();
27
  94:   dd 9b           sbis    0x1b, 5 ; 27
28
  96:   02 c0           rjmp    .+4             ; 0x9c <__vector_2+0x34>
29
  98:   dd 98           cbi     0x1b, 5 ; 27
30
  9a:   01 c0           rjmp    .+2             ; 0x9e <__vector_2+0x36>
31
  9c:   dd 9a           sbi     0x1b, 5 ; 27
32
  9e:   8f 91           pop     r24
33
  a0:   0f 90           pop     r0
34
  a2:   0f be           out     0x3f, r0        ; 63
35
  a4:   0f 90           pop     r0
36
  a6:   1f 90           pop     r1
37
  a8:   18 95           reti

Probiere schon seit Tagen und komme nicht weiter. Es scheint mir, als ob 
der Tiny nicht das macht, was er sollte. Im Errata habe ich nichts 
gefunden, aber vielleicht weiss ja einer von euch was hier los ist.

Gruß
Marco

von Jörg X. (Gast)


Lesenswert?

Zu deinem Problem kann ich nichts sagen, außer: Bist du sicher, dass im 
AVR dass richtige Programm 'steckt' ? ;) Oder ein Kurzschluss bei den 
LEDs ... Der Code oben sieht korrekt aus (wenn auch merkwürdig )

mir fallen solche Sachen ein , wie z.B.
 - volatile braucht man, wenn eine Variable in main() UND isr geändert 
wird (hier tut's 'static' auch - erzeugt aber weniger asm-code)
 - die TOGGLE-macros sind seeehr umständlich: PORTx ^=(1<<PINy) würde 
auch gehen (^ ist XOR in C)
 - und clk würde ich als unsigned Typen nehmen (ist aber wohl 'ne 
Prinzipfrage, nichts generelles)
( - ein "clk % 2" ist besser lesbar als "clk & 1", imho!)

Aber wie gesagt, in dem geposteten Code kann ich keine wirklichen Fehler 
entdecken

hth. Jörg

von MNR (Gast)


Lesenswert?

Interessanter Code. Was soll das do ... while(0) bewirken?
Ohne den Fehler entdeckt zu haben, mal ein kleiner Tip: Port Togglen 
geht per
1
PORTA^=_BV(PA5)
 einfacher.

Gruß, Matthias

von MNR (Gast)


Lesenswert?

Jörg war schneller...

von Jörg X. (Gast)


Lesenswert?

Haarscharf ;)
das do{...}while war/ist, glaube ich, ein Konstrukt um längere Makros zu 
schreiben, an die man beim aufrufen trotzdem ein Semikolon anhängen 
kann. An einen {...} Block darf man ja kein ";" anhängen.

gruß Jörg

von Andreas K. (a-k)


Lesenswert?

Und auf neueren AVRs wie dem Tiny24 mit
1
PINA = _BV(PA5)
 noch einfacher.

von Walter (Gast)


Lesenswert?

nach deiner Beschreibung schaut es ja so aus als ob cnt nicht erhöht 
würde,
der Compiler macht da ja ein interessantes cnt -(-1) draus.
Hast du schon den Vorschlag von Jörg probiert cnt unsigned zu machen?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> der Compiler macht da ja ein interessantes cnt -(-1) draus.

Was anderes kann er nicht, zumindest nicht mit einem Direktwert.

von Marco S (Gast)


Lesenswert?

@ zum richtigen Programm:
Die Bytes, die ich aus dem Flash auslesen kann, sind diesselben wie 
avr-objdump -d ausspuckt.

@volatile, static, signed, unsigned, ...:
Habe alle probiert. Macht das Programm nicht funktionsfähig. 
Letztendlich läuft es immer auf ein "lds/sts 0x0060" hinaus.

@warum do{}while(0)?
Keine Ahnung. Habe ich mal irgendwo aufgeschnappt. Einfache 
Blockklammerung geht natürlich auch. Aber ist eh' nur Präprozessor. An 
eine {}-Klammerung darf man natürlich soviele Leeranweisungen 
dranhängen, wie man will. Die EXOR-Variante hatte ich natürlich auch 
schon (spart 2 Bytes ein). Warum hier das Komplizierte, egal.

@PINA = _BV(PA5)
In der Tat. Funktioniert prima. Wer die Datenblätter liest, ist halt im 
Vorteil. Danke für den Tip.

Es scheint mir, als ob der Befehl "clk += 1", der zu "subi r24, 0xFF" 
umgewandelt wird, oder ein anderer Befehl, kaputt ist. Kann es denn 
sein, daß der AVR seine eigene Maschinensprache teilweise nicht mehr 
versteht?

von Walter (Gast)


Lesenswert?

was mir etwas befremdlich vorkommt ist die Speicheradresse von cnt,
irgendwie so mitten drin. Ich bin ziemlich sicher dass du keinen Fehler 
im AVR entdeckt hast, sondern dass irgendwer (der Stack) auf deinem 
armen cnt rumprügelt ...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Was ist an 0x60 ,,mittendrin''?  Das ist die erste RAM-Adresse.

von Rolf Magnus (Gast)


Lesenswert?

> @warum do{}while(0)?
> Keine Ahnung. Habe ich mal irgendwo aufgeschnappt.

Ist doch immer schön, wenn jemand irgendwas von irgendwo übernimmt, ohne 
einen Grund zu kennen.

> Einfache Blockklammerung geht natürlich auch.

Solange sie nicht in einem if steht:

[C]
if (blah)
  toggleLED1();
else
  toggleLED2();
[C]

Ohne die while-Schleife bekommst du das nicht durch den Compiler.

von Peter D. (peda)


Lesenswert?

Man könnte aber auch ganz profan ne Funktion draus machen und die als 
inline deklarieren.
Dann ist der Code sogar wieder leserlich und verstehbar.


Peter

von Simon K. (simon) Benutzerseite


Lesenswert?

Marco S wrote:
> Es scheint mir, als ob der Befehl "clk += 1", der zu "subi r24, 0xFF"
> umgewandelt wird, oder ein anderer Befehl, kaputt ist. Kann es denn
> sein, daß der AVR seine eigene Maschinensprache teilweise nicht mehr
> versteht?

Das ist schon richtig so, falls dir das noch nich so klar geworden ist. 
Der AVR kann keine Addition mit einem Immediate. Nur eine Subtraktion.

Also:
1. a+b == a - (-b)
2. -1 == 0xFF == 0b11111111

von Stefan Sczekalla (Gast)


Lesenswert?

Hi,

--------------------------
Und auf neueren AVRs wie dem Tiny24 mit
PINA = _BV(PA5)
noch einfacher.
--------------------------

Uppps - was hab ich denn da verpasst ?!?
Es wäre nett wenn mir jemand auf die Sprünge helfen könnte.
Wieso "toggelt" PINA = _BV(PA5 ) ?

Grüße,

Stefan

von Simon K. (simon) Benutzerseite


Lesenswert?

Stefan Sczekalla wrote:
> Hi,
>
> --------------------------
> Und auf neueren AVRs wie dem Tiny24 mit
> PINA = _BV(PA5)
> noch einfacher.
> --------------------------
>
> Uppps - was hab ich denn da verpasst ?!?
> Es wäre nett wenn mir jemand auf die Sprünge helfen könnte.
> Wieso "toggelt" PINA = _BV(PA5 ) ?
>
> Grüße,
>
> Stefan

Mega644 Datenblatt (02/07 Seite 68):

> However, writing a logic one to a bit in the PINx Register, will result in
> a toggle in the corresponding bit in the Data Register.

Seite 70:
> Writing a logic one to PINxn toggles the value of PORTxn, independent on
> the value of DDRxn. Note that the SBI instruction can be used to toggle
> one single bit in a port.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bin endlich zum Testen gekommen.

Wenn man davon absieht, dass ich 'ne Weile gesucht habe um zu merken,
dass auf dem STK500/505 Port A5 (der ja auch für ISP benutzt wird)
offenbar nach A4 geroutet wird, funktioniert ansonsten dein Programm
genau so, wie von dir vorgesehen.

von Marco S (Gast)


Angehängte Dateien:

Lesenswert?

Es wurde immer verrückter und jetzt ...


Habe mehrere avr-gcc Versionen probiert:
avr-gcc-3.4.6
avr-gcc-4.1.2
avr-gcc-4.2.0
avr-gcc-4.2.1.20070703

Überall dasselbe. Dann mal aus Neugier WinAVR-20070525 unter WinXP 
probiert (zuvor alles unter linux) und damit das Programm, was oben 
Angegeben ist compiliert. Dann das Hex-File genommen und unter linux 
programmiert 1). Wunder oh Wunder; Programm läuft. Baff. Dann das o.a. 
Programm mit dem o.a. Compiler übersetzt, programmiert und läuft. Hä? 
Während Tage zuvor der Fehler bei mir wenigstens reproduzierbar war, 
geht selbst dies nicht meht. Dafür läuft es, wie es soll. Na gut. 
Assemblercode von Windows- und Linux-Version verglichen. Fast identisch. 
Unter der Linuxvesion wird der Stackpointer nicht nur in __ctors_end, 
sondern nochmal bei Eintritt in main initiiert. Da in main eine 
Endlosschleife steht, sollte dies unbedeutend sein.

1) Genervt vom Suchen, dass unter XP wohl kein Parallelportprogrammierer 
existiert, habe ich mich entschlossen einen AVR910-Typ zu bauen. Also 
mal eben auf dem Steckbrett einen 2313 nach dem Plan von 
http://www.mikrocontroller-projekte.de/ aufgesteckt. Auch um mal zu 
sehen, ob diese Schnittstellenlösung 115200 kBit schafft. Mit 2k2 in der 
Ausgangsstufe auch bei meinem hochkapazitiven Kabel. Funzt, also alles 
auf Lochraster. Nach dem Debuggen nicht vorhandener Leckströme durfte 
AVRStudio4 ran. Wunderbar, programmiert blitzschnell. Dann das ganze 
unter Linux und avrdude. Über 54 sek. für knapp 700 Byte sind im 
avr910-Modus schleppend langsam. Im avr109 oder butterfly-Modus geht's 
schneller, dafür landen aber nur 0xFF im kompletten flash. Gut das es 
Python gibt (gut 0.7 sek).

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Marco S wrote:

> Assemblercode von Windows- und Linux-Version verglichen. Fast
> identisch.  Unter der Linuxvesion wird der Stackpointer nicht nur in
> __ctors_end, sondern nochmal bei Eintritt in main initiiert. Da in
> main eine Endlosschleife steht, sollte dies unbedeutend sein.

Deinem Unix-Compiler fehlt der ATmega256x-Patch, der uns nebenbei noch
beschert hat, dass main() nun eine normale Funktion geworden ist.  Das
wird künftig mal dahingehend aufgelöst werden, dass main() entweder
mit __attribute__((OS_task)) oder __attribute__((OS_main)) deklariert
wird, um seine bisherige Sonderrolle zu erhalten.

> 1) Genervt vom Suchen, dass unter XP wohl kein
> Parallelportprogrammierer existiert, habe ich mich entschlossen
> einen AVR910-Typ zu bauen.

Bau dir lieber einen USBisp oder USBasp, die sind schneller.  Oder
kauf dir einen AVR Dragon.  Der kann bei Bedarf auch gleich noch
HV-Programmierung.

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.