Forum: Compiler & IDEs Programm von mega32 auf mega328P convertieren


von Jürgen S. (jsachs)


Lesenswert?

Hallo,

ich brauche mal Eure Glaskugel. Das Programm ist viel zu komplex um es 
hier zu Posten.

ich habe ein Programm geschrieben, das auf einem mega32 problemlos 
seinen Dienst versieht.
Ich will es nun auf einen mega328 am laufen bringen.

Grundsätzlich läuft es auch.
UART per Interrupt, Timer gehen sicher
ADC scheint auch zu laufen

Ich habe den Eindruck, dass mir der RAM ausgeht.
Das Programm läuft  und ich kann bestimmte Funktionen 1 mal an triggern, 
dann wird alles langsamer (hat mit dem Timer zu tun, dass weis ich 
schon), und beim 2. mal ist er tot. Daher nehme ich an, es geht der RAM 
aus.
Wobei RAM und EEPROM doch gleich groß sind ?!

Hab auch schon versucht, dass Programm teilweise auszukommentieren, 
woraufhin es auch geht.

Hat jemand von Euch schon mal ein Programm konvertiert und kann mir 
sagen was ich noch übersehe.
Im Prinzip sind die beiden Typen doch gleich ?
Der mega328 hat weniger Pins, ein paar Interrupts sind anders benannt.

Danke und Gruss
J. Sachs

von Oliver (Gast)


Lesenswert?

Jürgen Sachs schrieb:
> Das Programm ist viel zu komplex um es
> hier zu Posten.

Als Anhang verdaut die Forensoftware beliebig komplexe Programme.

Was sagt denn avr-size zur Speicherauslastung?

Oliver

von Peter D. (peda)


Lesenswert?

Jürgen Sachs schrieb:
> Das Programm ist viel zu komplex um es
> hier zu Posten.

Unterschätze mal die Leute hier nicht.
Wieviel 100 Files und 10.000 Quelltextzeilen sind es insgesamt?

Einfach mal alles in ein ZIP packen, wieviel MB sind es dann?

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


Lesenswert?

Jürgen Sachs schrieb:
> Im Prinzip sind die beiden Typen doch gleich ?

Nö.

Der ATmega32 ist ein Nachfolger der Linie AT90S4434/8535, aus denen
dann ATmega163 und ATmega16 hervorgegangen sind.  Ab ATmega16 dann
auch mit JTAG zum Debuggen.

Der ATmega328 ist ein Nachfolger der Linie AT90S2232/4433, aus denen
der ATmega8 hervorgegangen ist.  Ab ATmega48/88/168/328 dann auch mit
debugWIRE zum Debuggen.

Schon bei Dingen wie den Timer-Bits würde ich beide Datenblätter sehr
genau nebeneinander halten.

von Peter D. (peda)


Lesenswert?

Die Register des Mega32 entsprechen eher dem Mega8.
Die Register des Mega328 aber dem Mega324.

von Jürgen S. (jsachs)


Lesenswert?

Das Programm ist wirklich zu komplex.
Es ist in mehrere Einzelprojekte aufgeteilt, wie Receiver, Transmitter, 
Bootloader, globale Dateien, etc.
Aus den Files kommen jetzt schon 6 unterschiedliche Builds für 
unterschiedliche Endgeräte raus.
Es ist einfach zu kompliziert für Außenstehende das Projekt zu erklären, 
damit er es nachbauen kann.
Natürlich alles schön in SVN gehostet, sonst könnte ich das auch nicht 
mehr handeln.

Aber zum Thema zurück....
Ich habe die Register durch, dachte ich. Die Unterschiede habe ich mit 
Defines abgefangen.

avr-size liefert:
1
avr-size rx_main.elf 
2
   text    data     bss     dec     hex filename
3
  26414     324     669   27407    6b0f rx_main.elf

bzw im Detail:
1
avr-size -A rx_main.elf 
2
rx_main.elf  :
3
section           size      addr
4
.data              324   8388864
5
.text            26414         0
6
.bss               669   8389188
7
.stab             3732         0
8
.stabstr           655         0
9
.comment            17         0
10
.debug_aranges     288         0
11
.debug_info       1278         0
12
.debug_abbrev      180         0
13
.debug_line       1002         0
14
Total            34559

hier sehe ich aber nicht mein Stack verbrauch. Und ich vermute hier mein 
Problem.
Auch weis ich nicht genau, wie sich die Programmierung in C++ hier noch 
auswirkt zur Laufzeit.
Eine vernünftige Lösung für die Ermittlung des verbleibenden Speichers 
zur Laufzeit habe ich noch nicht gefunden. Ich meine es gab mal ein 
Ansatz der den Speicher mit Werten vollschreibt und diese danach zählt.

Achja, der Bootloader ist derzeit auf Max Size, fängt also bei 0x7000 
an.
Ja, die Wortbreite berücksichitge ich im Programm, wobei ich meine 
letzten Tests auch ohne Bootloader gemacht habe, also direkt geflashed 
per ISP.

Gruss
Juergen

von Oliver (Gast)


Lesenswert?

Jürgen Sachs schrieb:
> Aber zum Thema zurück....
> Ich habe die Register durch, dachte ich. Die Unterschiede habe ich mit
> Defines abgefangen.

Tja, mehr kann es eigentlich kaum sein.

Ein diff über die Disassemblies der beiden Versionen sollte aber die 
letzten Zeifel ausräumen können.

Oliver

von Klaus F. (kfalser)


Lesenswert?

Jürgen Sachs schrieb:
> Auch weis ich nicht genau, wie sich die Programmierung in C++ hier noch
> auswirkt zur Laufzeit.

C++ oder C ?

Jürgen Sachs schrieb:
> hier sehe ich aber nicht mein Stack verbrauch. Und ich vermute hier mein
> Problem.

Bei richtiger Programmierung sollte der Stackverbrauch immer gleich 
sein, oder zumindest sollte der 2. Durchlauf eine Funktion nicht mehr 
Stack verbrauchen als das 1. Mal (wenn überall in die gleichen 
Unterprogramme gesprungen wird).
Hast Du Rekursionen im Programm oder Funktionen, in denen große Array 
angelegt werden?
Werden die Array dynamisch angelegt und nicht mehr freigeben?

Jürgen Sachs schrieb:
> Das Programm läuft  und ich kann bestimmte Funktionen 1 mal an triggern,
> dann wird alles langsamer (hat mit dem Timer zu tun, dass weis ich
> schon), und beim 2. mal ist er tot.

Vielleicht kannst Du zumindest die Timer ISR zeigen?

von Jürgen S. (jsachs)


Lesenswert?

Also das Programm ist zum Großteil C++. Allerdings benutze ich es 
Hauptsächlich um das Programm zu Kapseln, also Memberfunktionen, keine 
Vererbung etc. So lassen sich Programmteile leichter mehrfach nutzen.
Ein Teil des Programms ist noch C, der neuere C++.

Die Timer sind noch "C".
2 Timer genutzt:
- 16 Bit Timer macht ein PWM Signal für Servos
- 8 Bit Timer ,macht eine ungefähre Zeitbasis

Kurz vorab. Ich habe zwei Makros definiert
BV() => Setzt Bit
BVC() => Löscht ein Bit
Es geht hierbei um das "Modul" (Ich nenne meine Hardware Teile Module) 
"1003", also die defines "_TYPE_1003_"
I2C ist derzeit deaktiviert, und ruft nur leere Funktionen auf.
Das Modul läuft mit dem internen RC auf 8MHz, Fuses
1
  L_FUSE=0xe2
2
  H_FUSE=0xd0
3
  E_FUSE=0xff

Zeitbasis Timer
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "timer.h"
4
5
#define REALTIME_TC0 10UL /* time in ms */
6
#define PRESCALE_TC0 1024UL   /* seems to be a good value for 8 and 16 MHz, change CS22-CS20 too */
7
#define OVERFLOW_TC0 ((uint8_t)((REALTIME_TC0 * (F_CPU/PRESCALE_TC0)) / (1000UL)))
8
9
volatile struct _sTimer timer;
10
11
void initTimer(void)
12
{
13
#if _TYPE_1003_
14
    TCCR2A = (_BV(WGM21)|_BV(WGM20));
15
    TCCR2B = (_BVC(FOC2A)|_BV(WGM22)|_BV(CS22)|_BV(CS21)|_BV(CS20));
16
    OCR2A = OVERFLOW_TC0;
17
    TIMSK2 |= _BV(OCIE2A);
18
#else
19
    TCCR2 = (_BVC(FOC2)|_BV(WGM21)|_BVC(WGM20)|_BVC(COM21)|_BVC(COM20)|_BV(CS22)|_BV(CS21)|_BV(CS20));
20
    OCR2 = OVERFLOW_TC0;
21
    TIMSK |= _BV(OCIE2);
22
#endif
23
}
24
25
void stopI2cTimeout(void)
26
{
27
    timer.i2cTimeout = 0;
28
}
29
30
void delayTimer(uint8_t d)
31
{
32
    uint8_t oldtens=0;
33
    uint8_t cnt=0;
34
35
    while(cnt<d)
36
    {
37
        while(oldtens==timer.hundreds)
38
            nop();
39
40
        oldtens=timer.hundreds;
41
        cnt++;
42
    }
43
44
45
}
46
47
extern void fireI2cTimeout(void);  // is inside of i2c routines
48
49
void stopSerialTimeout(void)
50
{
51
    timer.serialTimeout = 0;
52
}
53
54
extern void fireSerialTimeout(void);  // is inside of i2c routines
55
56
void resetTimerFlags(void)
57
{
58
    uint8_t sreg = SREG;
59
    cli();
60
    timer.timerMinuteFlag = 0;
61
    timer.timerSecondFlag = 0;
62
    timer.timerTenthFlag = 0;
63
    timer.timerHundredsFlag = 0;
64
    SREG = sreg;
65
}
66
67
#ifdef _TYPE_1003_
68
ISR(TIMER2_COMPA_vect)
69
#else
70
ISR(TIMER2_COMP_vect)
71
#endif
72
{
73
    timer.hundreds++;
74
75
    timer.timerHundredsFlag = 1;
76
77
    if (timer.hundreds%10 == 0)
78
        timer.timerTenthFlag = 1;
79
80
    if (timer.hundreds>= 100)
81
    {
82
        timer.hundreds = 0;
83
        timer.seconds++;
84
        timer.timerSecondFlag = 1;
85
        if (timer.seconds >= 60)
86
        {
87
            timer.timerMinuteFlag = 1;
88
            timer.seconds = 0;
89
            timer.minutes++;
90
            if (timer.minutes >= 60)
91
            {
92
                timer.minutes = 0;
93
                timer.hours++;
94
            }
95
        }
96
    }
97
    if (timer.i2cTimeout > 0)
98
    {
99
        timer.i2cTimeout--;
100
        if (timer.i2cTimeout == 0)
101
            fireI2cTimeout();
102
    }
103
    if (timer.serialTimeout > 0)
104
    {
105
        timer.serialTimeout--;
106
        if (timer.serialTimeout == 0)
107
            fireSerialTimeout();
108
    }
109
}
Hier werden nur Flags gesetzt, die in main() ausgewertet werden.

Und der Timer für das PWM
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "output.h"
4
#include "timer_1.h"
5
#include "rx_main.h"
6
#include "../../global_files/avr_helper.h"
7
8
volatile uint8_t syncBreakActive = 0;
9
10
void initTimer1 ( void )
11
{
12
    // Timer 0 konfigurieren
13
    TCCR1B = _BV ( WGM12 ); // CTC Modus
14
    TCCR1B |= (_BV ( CS10 ));// | _BV(CS11)); // Prescaler 1
15
    // ((16000000)/0xa0) = 125
16
    OCR1A = 24000;//0x90;//0xa0;
17
18
    // Compare Interrupt erlauben
19
#ifdef _TYPE_1001_
20
    TIMSK |= (_BV ( OCIE1A ));// | _BV(TOIE1));
21
#elif _TYPE_1003_
22
  TIMSK1 |= (_BV ( OCIE1A ));// | _BV(TOIE1));
23
#endif
24
}
25
26
ISR(TIMER1_OVF_vect)
27
{
28
    nop();
29
}
30
31
ISR ( TIMER1_COMPA_vect )
32
{
33
34
    static uint8_t chan = 0, lastchan;
35
    static uint16_t val = 24000;
36
    //static uint8_t dir;
37
    lastchan = chan;
38
  static uint16_t dimm;
39
40
    if (rx.outputOverwrite != 0)  // Overwrite active
41
        return;
42
43
    if (lastchan < MAX_CHANNELS)
44
        Outputs.setOutputChannel ( lastchan, 0 );
45
46
    if (syncBreakActive == 0)
47
    {
48
        chan++;
49
        while ( ( chan < MAX_CHANNELS ) && ( Outputs.getType(chan) != OUT_TYPE_PROP ) )
50
        {
51
            chan++;
52
        }
53
54
    }
55
    
56
    if (chan < MAX_CHANNELS)
57
    {
58
        //PORTB |= _BV(chan);
59
        val = Outputs.getChDataOut(chan);
60
        // we only output data, if we have a valid output value, ZERO means disabled
61
        if (val > 0)
62
            Outputs.setOutputChannel ( chan, 1 );
63
        else
64
            Outputs.setOutputChannel(chan, 0);
65
        OCR1A = val;
66
    TCNT1 = 0x0000;  // make sure we start at Zero
67
    }
68
    else if (chan==(MAX_CHANNELS))
69
    {
70
        syncBreakActive = 1;
71
        OCR1A = 22400;
72
        chan++;
73
74
    }
75
    else if (chan>=(MAX_CHANNELS+1))
76
    {
77
        // Das go kommt von der mainline
78
        if (syncBreakActive == 0)
79
        {
80
            syncBreakActive = 0;
81
            chan = 0;
82
        }
83
    }
84
}

Vielleicht seht ihr, was ich nicht sehe.

Danke
Juergen

von Jürgen S. (jsachs)


Lesenswert?

Ganz vergessen,
Keine Rekursion, zumindest nicht bewusst.
Keine großen Arrays, mal ein "char buffer[5]" für die Wandlung int auf 
ASCII als temp buffer.
1
void sendHeader(uint8_t toAddr, uint8_t myAddress)
2
{
3
    char buffer[6];
4
    uartPuts_p(PSTR("@"));
5
    uartPuts(uint8ToAh(buffer, toAddr));
6
    uartPuts(uint8ToAh(buffer, myAddress));
7
}

Ich denke man versteht was ich hier mache.
Sowas kommt öfters vor, also verschachtelte Funktionen, die ja auch 
Stack benötigen.

Gruss
Juergen

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


Lesenswert?

Jürgen Sachs schrieb:
> BVC() => Löscht ein Bit

Was denn, sowas?
1
#define BVC(x) ~(1 << (x))

Das wäre ein Bit löschen, aber dann ist das hier tödlich:
1
TCCR2 = (_BVC(FOC2)|_BV(WGM21)|_BVC(WGM20)|_BVC(COM21)|_BVC(COM20)|_BV(CS22)|_BV(CS21)|_BV(CS20));

Oder ist BVC() nur eine umständliche Form, eine 0 aufzuschreiben?
1
#define BVC(x) (0 << (x))

Dann wäre es ja Äquivalent zu
1
#define BVC(x) 0

und obige Zuweisung wäre zumindest OK.

von Jürgen S. (jsachs)


Lesenswert?

Ist ein
1
#define _BVC(x) ((0<<x))

dient nur der vereinfachung, wenn man alle Bits eines Riegisters listed 
und Explizit "0" oder "1" setzt, kann man schneller sehen was man 
vergessen hat.

Geht nur nicht immer, da es ja Register gibt, wo man einige Werte 
Unverändert lässt.

Bei deinem ersten Ausdruck
1
#define BVC(x) ~(1 << (x))
würde ich doch das ganze Byte negieren, bis auf das eine Bit...
Das wäre eher nicht gut :-)

Wie gesagt geht es auf einem mega32.

Habe es eben auch nochmal auf einem anderen Modul (Hardware Variante) 
versucht, die auch mit 8MHz, aber mega32 läuft, da geht alles.

Ich muss folglich etwas übersehen haben, oder ich komme bei dem mega328 
tatsächlich an sein Speicher Limit. Wobei ich bisher dachte das ich 
nicht mal ca 1k belegt habe und somit noch über 1k RAM für Stack übrig 
ist, sihe avrsize oben.
Und der mega328 hat ja auch 2k RAM, wie der mega32

PS: wisst ihr wie das tool hieß, was den freien Speicher zur Laufzeit 
ermittelt hat ?

Gruss
Juergen

von Oliver (Gast)


Lesenswert?

Der erzeugte Assemblercode sollte für die beiden Prozessoren identisch 
sein, bis auf die anders gesetzten Bits in den Timerregistern. avr-size 
sollte daher identische Werte liefern.

Oliver

von Jürgen S. (jsachs)


Lesenswert?

Oliver schrieb:
> Der erzeugte Assemblercode sollte für die beiden Prozessoren identisch
> sein, bis auf die anders gesetzten Bits in den Timerregistern. avr-size
> sollte daher identische Werte liefern.
>
> Oliver

Korrigiere mich, aber ist das nicht nur so, wenn man jegliche 
Optimierung deaktiviert ?
Es kann ja sein, das der Compiler denkt er kann hier einen anderen 
Befehl verwenden um XX statt YY zu laden ?!

Nachtrag:
Das geht bei mir sowieso nicht, da durch die wenigeren Pinns, andere 
Konfiguration vorgenommen wird.
Der meg328 hat ja weniger pins, daher wird der Code, der z.B. diese auf 
Ausgang Konfiguriert, per define ausgeklammert. Der Code ist also in 
jedem Fall anders.

Gruss
Juergen

: Bearbeitet durch User
von Oliver (Gast)


Lesenswert?

Der Befehlssatz ist bei den Prozessoren identisch, daher sollte da auch 
mit Optimierung der selbe Assemblercode rauskommen. Wie gesagt, ein paar 
Byte Unterschied durch die unterschiedliche Anzahl der Timerregister 
kann es geben, mehr sollte es aber nicht sein.

Oliver

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


Lesenswert?

Jürgen Sachs schrieb:
> Ist ein
1
#define _BVC(x) ((0<<x))

Dann kannst du aber auch gleich schreiben:
1
#define _BVC(x) 0

(Oben hattest du es noch ohne Unterstrich geschrieben …)

Eine Null nach links zu schieben, ist stets eine unsinnige Aktion.  Es
bleibt eine Null.  Dass du damit mental das Bit auf 0 belassen willst,
verdeutlichst du ja bereits durch die Benutzung des entsprechenden
Makros, aber in dessen Definition muss man das dann nicht so
umständlich verklausulieren.

Persönliche Anmerkung: ich bin da eher ein Freund, das so hier zu
schreiben:
1
  CTRLREG = /* !_BV(BIT1) | */ _BV(BIT2);

Jürgen Sachs schrieb:
> Der Code ist also in jedem Fall anders.

Dann wird es schwierig mit dem Vergleichen der Disassembler-Listings.

von Jürgen S. (jsachs)


Lesenswert?

Das ist richtig, aber wenn es einfach wäre, würde ich ja den Fehler 
selbst finden....

Ich denke, ich sehe den Wald vor lauter Bäumen nicht mehr.

Ich werde das Gefühl nicht los, ich Jage ein gcc bug...
Warum ?

Ich wollte auf der mega32 Platform den Code reduzieren, damit er 
schlanker wird und weniger Speicher Benötigt.
Das ist im Prinzip eine komplett gekapselte Klasse.
Macht auch keinen IO nur mit dem EEPROM und ruft Funktionen auf, die es 
so oder so schon gibt.
Mach ich die Klasse raus (Kann ich per define einfach ausklammern), 
komme ich nicht mehr weit.
Dachte erst ich habe noch irgend einen Verweis, Zugriff auf eine 
uninitialiserte Variable. Nichts, nichts zu finden.
Jetzt habe ich herausgefunden, das ich ohne die Klasse in meiner Port 
Initialisierung fest hänge. Genau gesagt, startet der Kontroller ständig 
neu.
Die Klasse hat damit nichts zu tun, gar nichts.
Und das Kuriose ist, das ich nur ein paar Ports nicht initialisieren 
muss und es geht. Welche ist egal Hauptsache ein paar raus....

Mache ich die Klasse rein, geht es, mache ich ein paar Ports und die 
Klasse raus, geht es.

Ich habe versucht an dem LSS File (mit und ohne Klasse) einen 
Unterschied zu sehen, aber da reichen meine Assembler Kenntnisse weit 
nicht aus.

Gruss
Juergen

von Jürgen S. (jsachs)


Lesenswert?

So, jetzt habe ich die Reihenfolge der Port Initialisierung geändert, 
und nun geht es wieder auch ohne die Klasse....

Ich gehe mal Käfer Jagen....

Gruss
Juergen

von Oliver S. (oliverso)


Lesenswert?

> Jürgen Sachs schrieb:
>> Der Code ist also in jedem Fall anders.
>
> Dann wird es schwierig mit dem Vergleichen der Disassembler-Listings.

Nun, ein einigermaßen anständiges diff-Programm lässt sich auch durch 
ein paar zusätzliche Zeilen nicht gleich aus dem Konzept bringen.

Versuch macht kluch...

Oliver

von Jürgen S. (jsachs)


Lesenswert?

Oliver S. schrieb:
>> Jürgen Sachs schrieb:
>>> Der Code ist also in jedem Fall anders.
>>
>> Dann wird es schwierig mit dem Vergleichen der Disassembler-Listings.
>
> Nun, ein einigermaßen anständiges diff-Programm lässt sich auch durch
> ein paar zusätzliche Zeilen nicht gleich aus dem Konzept bringen.
>
> Versuch macht kluch...
>
> Oliver

Auf die Idee mit dem Diff bin ich noch nicht gekommen.
Das ist eine Idee. Werde ich noch versuchen.

Gruss
Juergen

von Jürgen S. (jsachs)


Angehängte Dateien:

Lesenswert?

Ich komme hier leider nicht weiter.

Der Diff unterscheidet sich leider erheblich. Und mein Assembler 
Kenntnisse sind hier am Ende.

ich habe mal ein ZIP angehängt mit beiden LSS Files.

Das 1001 (mega32) geht, das 1003 (mega328p) geht nicht richtig.

Vielleicht kann ja jemand mit etwas Wissen kurz einen Blick darauf 
werfen.

So wie ich das sehe, hat der mega328p ja wesentlich mehr Vectoren (21 vs 
23).

Danke
Juergen

von D. V. (mazze69)


Lesenswert?

Jürgen Sachs schrieb:
> Das 1001 (mega32) geht, das 1003 (mega328p) geht nicht richtig.

Entgegen den landläufigen Hinweisen wenigstens ein Datenplatt zu 
bemühen, bist du in der glücklichen Lage, gleichwohl zwei dieser 
heiligen Schriften zu konsultieren. Nur wer die Unterschiede kennt, kann 
in die ewigen Jagdgründe emigrieren...

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Jürgen Sachs schrieb:
> ich habe mal ein ZIP angehängt mit beiden LSS Files.

Ooch nöö, das ist ja Linux ohne Zeilenende.

Meinst Du wirklich, da wühlt sich jemand durch, ohne den Source zu 
kennen?


Jürgen Sachs schrieb:
> Jetzt habe ich herausgefunden, das ich ohne die Klasse in meiner Port
> Initialisierung fest hänge. Genau gesagt, startet der Kontroller ständig
> neu.

Was denn nun, hängt er oder resettet er und welche Resetquelle ist 
gesetzt?

Füge dochmal einige Debugausgaben ein. Einen Pin hast Du bestimmt frei 
zur Ausgabe auf ein Terminalprogramm.
Und selbstverständlich gehört beim Debuggen der Watchdog disabled.

: Bearbeitet durch User
von Oliver (Gast)


Lesenswert?

Jürgen Sachs schrieb:
> Der Diff unterscheidet sich leider erheblich. Und mein Assembler
> Kenntnisse sind hier am Ende.

Das sieht nur so aus, weil in den lss-Files Zeilennummern usw. mit drin 
sind, die natürlich nicht übereinstimmen. Erzeug dir mal richtige 
Dissassembler-Files, dann siehst du, daß das allermeiste in den beiden 
Pogrammen identisch ist. Ja, der 328 hat ein paar Vectoren mehr, daher 
verschieben sich alle labels, aber das ist nicht weiter tragisch.

Du vergleichst allerdings Äpfel mit Birnen. Das sollte schon für beide 
Prozessoren der selbe Sourcecode sein. Ist es aber nicht, die 
1003er-Version enthält mehr Code (einiege Routinen mehr). Das hat sicher 
Einfluß auf den Sram-Bedarf.

Insofern ist die Ausage "es läuft auf dem 32er, aber nicht auf dem 
328er" nicht richtig, es ist halt nicht das selbe Programm.

Oliver

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


Lesenswert?

Poste doch zumindest mal noch die ELF-Files.  Die kann sich dann jeder
selbst disassemblieren, sich die Symboltabelle ansehen usw.

von Jürgen S. (jsachs)


Lesenswert?

D. V. schrieb:
> Jürgen Sachs schrieb:
>> Das 1001 (mega32) geht, das 1003 (mega328p) geht nicht richtig.
>
> Entgegen den landläufigen Hinweisen wenigstens ein Datenplatt zu
> bemühen, bist du in der glücklichen Lage, gleichwohl zwei dieser
> heiligen Schriften zu konsultieren. Nur wer die Unterschiede kennt, kann
> in die ewigen Jagdgründe emigrieren...

Was will mir der Nutzer mit diesen Worten sagen ?
Das Register anders heißen und zum Teil anders belegt sind, ist mir 
bekannt und ich denke auch alle Punkte angepasst zu haben.

Einfache Programme, die z.B. nur Serielle Daten ausgeben, laufen ja 
auch.

Gruss
Juergen

von Jürgen S. (jsachs)


Lesenswert?

Jörg Wunsch schrieb:
> Poste doch zumindest mal noch die ELF-Files.  Die kann sich dann jeder
> selbst disassemblieren, sich die Symboltabelle ansehen usw.

Ich habe gestern noch einen Punkt auf dem PC Programm gesehen (Das die 
RS232 Daten auswertet), wo mich zu einer falschen Aussage geführt hat.
Scheinbar kamen die Daten manchmal zu schnell und wurden dann nicht 
abgearbeitet, bis ein weiteres Ereignis aufgelaufen ist 
(Tastatureingaben oder mehr Daten). Seid 2 Jahren in Betrieb und noch 
nie aufgefallen....
Ist im Prinzip ein Terminal, wo mir die Rückmeldungen auswertet und im 
Klartext anzeigt, Programm updates lädt usw...
Das habe ich nun angepasst. Ein Test steht noch aus.

Das Programm geht immer noch nicht, aber so wie es aussieht bleibt das 
Programm doch nicht "hängen" für ein paar Sekunden und macht dann 
weiter.

Das der 328er mehr Funktionen hat, wundert mich. Sollte genau anders rum 
sein, erklärt aber den größeren Code. Ich versuche mal die Funktionen 
raus zu finden und das im Code zu prüfen.

Wegen dem Linux Zeilenende, sorry ich arbeite Praktisch nur unter Linux.

Ich verifiziere das im laufe des Tages und Poste sonst die ELF Files.
Habe von Euch ja eine lange Liste zum Checken bekommen.

Danke, ich weiß das zu schätzen.
Gruss
Juergen

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


Lesenswert?

Jürgen Sachs schrieb:
> Das der 328er mehr Funktionen hat, wundert mich.

Der ist einfach ein paar Jahre jünger von der Entwicklung her.

> Sollte genau anders rum
> sein, erklärt aber den größeren Code.

Größerer Code entsteht vor allem deshalb, weil bei den mega*8 
IO-Register
außerhalb des regulären IO-Bereichs liegen und nur noch über reguläre
Speicherzugriffe (LDS/STS) erreichbar sind, während beim ATmega16 noch
alles in die unteren 64 Adressen gepasst hat, auf die man mit IN/OUT
zugreifen kann.

> Wegen dem Linux Zeilenende, sorry ich arbeite Praktisch nur unter Linux.

Sollte auch unter Windows aber kein Thema sein.  Notepad kann damit
umgehen, viele andere Editoren auch.  Die Leute werden ja kaum auf der
Kommandozeile mit "more" arbeiten. ;-)

von Peter D. (peda)


Lesenswert?

Du kannst natürlich auch über einen Zwischeschritt gehen.

Erstmal das Programm vom 32 auf den 324 umschreiben, dann bleiben die 
Portpins ja gleich und Du mußt nur die Timerregister usw. ändern.

Und wenn das läuft, kannst Du die Portpins ändern, also von 324 auf 328.

von Oliver (Gast)


Angehängte Dateien:

Lesenswert?

Jürgen Sachs schrieb:
> Das der 328er mehr Funktionen hat, wundert mich. Sollte genau anders rum
> sein, erklärt aber den größeren Code.

Im 1003er stecken mehr Klassen drin, daher gibt es da zu Begin schonmal 
mehr statische Konstruktoren.

Aber der Programmablauf ist auch anders. Anbei ein Screeshot von 
WinMerge, der eine Ausschnitt aus main zeigt (ziemlich am Anfang von 
main).

Im 1003er File gigt es da einige Funktionsaufrufe, die im 1001er nicht 
stattfinden.

Ganz kriegt WinMErge das nicht synchronisiert, der Aufruf von 
<_Z7adcInitv>, der links als letztes zu sehen ist, kommt rechts auf der 
nächsten Bildschirmseite, <_Z8initGearv> kommt zwei Seiten später.

Die Aufruffolge von <__strlen_P> und <_Z17sendI2CBlocking_phPhh> kommt 
rechts in der Folge noch mehrfach vor, links gibt es die nicht.

Das ist nicht das selbe Programm.

Oliver

von Jürgen S. (jsachs)


Lesenswert?

Hallo Peter,
es ist eigentlich ein Programm für alle.
Dort wo es Unterschiede gibt, gibt es defines, die die Änderung 
berücksichtigen, als Beispiel:
1
void initTimer(void)
2
{
3
#if _TYPE_1003_
4
    TCCR2A = (_BV(WGM21)|_BV(WGM20));
5
    TCCR2B = (_BVC(FOC2A)|_BV(WGM22)|_BV(CS22)|_BV(CS21)|_BV(CS20));
6
    OCR2A = OVERFLOW_TC0;
7
    TIMSK2 |= _BV(OCIE2A);
8
#else
9
    TCCR2 = (_BVC(FOC2)|_BV(WGM21)|_BVC(WGM20)|_BVC(COM21)|_BVC(COM20)|_BV(CS22)|_BV(CS21)|_BV(CS20));
10
    OCR2 = OVERFLOW_TC0;
11
    TIMSK |= _BV(OCIE2);
12
#endif
13
}

Bei den Outputs ist es ähnlich.
Alle Ausgänge sind in einer Structur erfasst, mit PORT und PIN
1
void modulPortSetup ( void )
2
{
3
    //setup our outputs, and how they are configured
4
#ifdef _TYPE_1001_
5
    // JP1, Servo 1-8
6
    Outputs.setOutputConfig ( 7, OUT_PORTB, PB7 );  // Bremslicht
7
    Outputs.setOutputConfig ( 6, OUT_PORTB, PB6 );  // Rückfahr Scheinwerfer
8
    ....................
9
#elif _TYPE_1004_
10
#warning "check port defs type 1004"
11
  
12
13
    Outputs.setOutputConfig ( 8, OUT_PORTD, PD5 );  // Fernlicht, LED 1
14
    Outputs.setOutputConfig ( 9, OUT_PORTD, PD6 );  // Nebellicht, LED 2
15
16
    ....................
17
#elif _TYPE_1003_
18
    // JPx, Servo 1-8
19
    Outputs.setOutputConfig ( 0, OUT_PORTD, PD5 );
20
    //Outputs.setOutputConfig ( 1, OUT_PORTC, PC7 );
21
    Outputs.setOutputConfig ( 2, OUT_PORTC, PC0 );
22
    ....................
23
#else
24
#error "Module type not set or unknown"
25
#endif
26
}
Ich speichere hierbei im übrigen nicht den Port, sondern nur einen ENUM 
Wert, so kann ich mit einem Bitfeld in einem Byte, Port und Pin 
speichern.
Im Programm spreche ich nur Ausgang "1" oder "5" an.
Es gibt dann quasi eine Funktion die über einen Switch case block den 
jeweiligen Port anspricht.

So kann ich mit wenigen Zeilen Änderung, den Code an unterschiedliche 
Hardware anpassen.

Außerdem kann man zur Laufzeit die Funktion eines Pins anpassen, daher 
Arbeite ich lieber mit einer Referenz, als mit Ports und Pins.

Gruss
Juergen

von Jürgen S. (jsachs)


Lesenswert?

Oliver schrieb:
> Jürgen Sachs schrieb:
>> Das der 328er mehr Funktionen hat, wundert mich. Sollte genau anders rum
>> sein, erklärt aber den größeren Code.
>
> Im 1003er stecken mehr Klassen drin, daher gibt es da zu Begin schonmal
> mehr statische Konstruktoren.
>
> Aber der Programmablauf ist auch anders. Anbei ein Screeshot von
> WinMerge, der eine Ausschnitt aus main zeigt (ziemlich am Anfang von
> main).
>
> Im 1003er File gigt es da einige Funktionsaufrufe, die im 1001er nicht
> stattfinden.
>
> Ganz kriegt WinMErge das nicht synchronisiert, der Aufruf von
> <_Z7adcInitv>, der links als letztes zu sehen ist, kommt rechts auf der
> nächsten Bildschirmseite, <_Z8initGearv> kommt zwei Seiten später.
>
> Die Aufruffolge von <__strlen_P> und <_Z17sendI2CBlocking_phPhh> kommt
> rechts in der Folge noch mehrfach vor, links gibt es die nicht.
>
> Das ist nicht das selbe Programm.
>
> Oliver
Danke,
Ja manche Module haben I2C, dass ich aber nicht nutze. Es gibt Ansätze 
darüber ein Debugging zu machen. Im Prinzip eine RS232 über I2C, habe 
ich aus Zeitgründen aber nicht mehr verfolgt, da es immer wieder zu 
Problemen kam. Daher habe ich das auch deaktiviert, dachte ich.
1
#ifdef _DEBUG_ENABLED_
2
#define SEND_DEBUG(x) sendI2CBlocking((uint8_t)I2C_DEBUG_UART_ADR, (uint8_t*)x, strlen(x))
3
#define SEND_DEBUG_LEN(x, y) sendI2CBlocking((uint8_t)I2C_DEBUG_UART_ADR, (uint8_t*)x, y)
4
#define SEND_DEBUG_P(x) sendI2CBlocking_p((uint8_t)I2C_DEBUG_UART_ADR, (uint8_t*)x, strlen_P(x))
5
#define SEND_DEBUG_PLEN(x, y) sendI2CBlocking_p((uint8_t)I2C_DEBUG_UART_ADR, (uint8_t*)x, y)
6
#else
7
#define SEND_DEBUG(x) /*x*/
8
#define SEND_DEBUG_LEN(x, y) /*x,y*/
9
#define SEND_DEBUG_P(x) /*x*/
10
#define SEND_DEBUG_PLEN(x, y) /*x,y*/ 
11
#endif
Was aber bedeutet, solange DEBUG_ENABLED NICHT definiert ist, sollte 
der code nicht implementiert werden.
So ist es ja beim mega32.
Aber laut dem Diff, ist es beim mega328 doch eingebunden.

Der Sache gehe ich mal gleich nach.

Juergen

von Jürgen S. (jsachs)


Lesenswert?

Der Tipp war Gold wert.
Ich habe kurz noch 2 Warnungen in den Defines eingebaut um zu sehen was 
greift.
Beim mega32 ist es deaktiviert, beim mega328 aktiv.
Was Fatal ist, da die I2C Pins dort mit anderer Hardware belegt sind.

Ich werde berichten..

Gruss
Juergen

von Peter D. (peda)


Lesenswert?

Jürgen Sachs schrieb:
> Es gibt dann quasi eine Funktion die über einen Switch case block den
> jeweiligen Port anspricht.

Quasi Arduino-Style.

Jürgen Sachs schrieb:
> So kann ich mit wenigen Zeilen Änderung, den Code an unterschiedliche
> Hardware anpassen.

Kann ich auch super bequem machen, mit meiner sbit.h.
Da diese Macros aber schon zur Compilezeit aufgelöst werden, macht der 
Compiler einfach SBI/CBI bzw. SBIS/CBIS daraus.

von Jürgen S. (jsachs)


Lesenswert?

Peter Dannegger schrieb:
> Jürgen Sachs schrieb:
>> Es gibt dann quasi eine Funktion die über einen Switch case block den
>> jeweiligen Port anspricht.
>
> Quasi Arduino-Style.
Ich habe mich mit der Programmierung für das Arduino nicht wirklich 
beschäftigt.
Für mich war es die einzige Möglichkeit das so umzusetzen, ohne Port und 
Pin komplett abzuspeichern.

>
> Jürgen Sachs schrieb:
>> So kann ich mit wenigen Zeilen Änderung, den Code an unterschiedliche
>> Hardware anpassen.
>
> Kann ich auch super bequem machen, mit meiner sbit.h.
> Da diese Macros aber schon zur Compilezeit aufgelöst werden, macht der
> Compiler einfach SBI/CBI bzw. SBIS/CBIS daraus.

Hört sich interessant an, würdest du mir/uns das näher erklären ?
Hatte das hier mal angefragt, würde mit so 30 Byte Speicher sparen :-)

Gruss
Juergen

von Josef D. (jogedua)


Lesenswert?

Jürgen Sachs schrieb:
> PS: wisst ihr wie das tool hieß, was den freien Speicher zur Laufzeit
> ermittelt hat ?

Falls es noch benötigt wird:
http://www.rn-wissen.de/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc#Dynamischer_RAM-Verbrauch

von Jon (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Kann ich auch super bequem machen, mit meiner sbit.h.
> Da diese Macros aber schon zur Compilezeit aufgelöst werden, macht der
> Compiler einfach SBI/CBI bzw. SBIS/CBIS daraus.

Du kannst damit aber keine "Pin-Konfigurationen" abspeichern.
Bei einem objektorientierten Ansatz kann es unter Umständen Sinn machen 
z.B. jeder Instanz einer Klasse "Schieberegister" den jeweiligen Data-, 
Clock, und Enable-Pin mitzugeben.

Aber ja, leider will man den Overhead den man durch Run-Time 
Konfigurationen kriegt leider selten haben...

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


Lesenswert?

Jon schrieb:
> Bei einem objektorientierten Ansatz kann es unter Umständen Sinn machen
> z.B. jeder Instanz einer Klasse "Schieberegister" den jeweiligen Data-,
> Clock, und Enable-Pin mitzugeben.

Kann man aber auch im Konstruktor statisch(*) machen, dann braucht man
den Overhead trotzdem nicht.

(*) Soll heißen: zur Compilezeit bekannt und danach nicht mehr
änderbar, nicht "static" als C++-Schlüsselwort.

: Bearbeitet durch Moderator
von Jon (Gast)


Lesenswert?

Jörg Wunsch schrieb:

> Kann man aber auch im Konstruktor statisch(*) machen, dann braucht man
> den Overhead trotzdem nicht.
>
> (*) Soll heißen: zur Compilezeit bekannt und danach nicht mehr
> änderbar, nicht "static" als C++-Schlüsselwort.

Ich muss sagen, in C++ bin ich nicht so bewandert, bin jetzt von C 
ausgegangen wo alle "Attribute" in einer Strukt gebündelt werden?

Kannst du vielleicht ein Beispiel bringen wie sowas aussehen würde?
Mir ist nicht ganz klar wie du den Port/Pin festhalten kannst ohne dem 
Compiler sämtliche Optimierungsmöglichkeiten zu nehmen...

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


Lesenswert?

Jon schrieb:
> Mir ist nicht ganz klar wie du den Port/Pin festhalten kannst ohne dem
> Compiler sämtliche Optimierungsmöglichkeiten zu nehmen...
1
#include <avr/io.h>
2
3
class led {
4
  private:
5
    volatile uint8_t * const ddr;
6
    volatile uint8_t * const port;
7
    volatile uint8_t * const pin;
8
    const uint8_t pinmask;
9
10
  public:
11
    led(uint8_t n, volatile uint8_t *r, volatile uint8_t *p, volatile uint8_t *i):
12
      pinmask(1 << n), ddr(r), port(p), pin(i) {
13
      *r |= pinmask;
14
    }
15
16
    void set(void) { *port |= pinmask; }
17
    void clear(void) { *port &= ~pinmask; }
18
    void toggle(void) { *pin = pinmask; }
19
};
20
21
int
22
main(void)
23
{
24
  led L1(0, &DDRB, &PORTB, &PINB);  // port B0
25
26
  L1.set();
27
  L1.toggle();
28
  L1.clear();
29
30
  return 0;
31
}

Das Compilat:
1
.global main
2
        .type   main, @function
3
main:
4
/* prologue: function */
5
/* frame size = 0 */
6
/* stack size = 0 */
7
.L__stack_usage = 0
8
        sbi 0x17,0
9
        sbi 0x18,0
10
        ldi r24,lo8(1)
11
        out 0x16,r24
12
        cbi 0x18,0
13
        ldi r24,0
14
        ldi r25,0
15
        ret

von Jürgen S. (jsachs)


Lesenswert?

Josef D. schrieb:
> Jürgen Sachs schrieb:
>> PS: wisst ihr wie das tool hieß, was den freien Speicher zur Laufzeit
>> ermittelt hat ?
>
> Falls es noch benötigt wird:
> 
http://www.rn-wissen.de/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc#Dynamischer_RAM-Verbrauch

Danke, das hatte ich gesucht.

Gruss
Juergen

von Jürgen S. (jsachs)


Lesenswert?

Jörg Wunsch schrieb:
> ......
> Das Compilat:
>
>
1
> .global main
2
>         .type   main, @function
3
> main:
4
> /* prologue: function */
5
> /* frame size = 0 */
6
> /* stack size = 0 */
7
> .L__stack_usage = 0
8
>         sbi 0x17,0
9
>         sbi 0x18,0
10
>         ldi r24,lo8(1)
11
>         out 0x16,r24
12
>         cbi 0x18,0
13
>         ldi r24,0
14
>         ldi r25,0
15
>         ret
16
>
Interessanter Ansatz.
Ich wusste gar nicht dass man const class member erst im Construtor 
Initialisieren darf.
Muss man hier die Parameter Variablen (Port, Pin, DDR) nicht auch als 
"const" declarieren, zumindest der Form halber ?

Jetzt müsste ich von der Klasse LED nur noch ein Array definieren und 
alles ist gut. Das könnte mir einiges an overhead sparen, vorallem in 
den Interrupts für das PWM, da ja der Switch case wegfällt usw...

Gruss
Juergen

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


Lesenswert?

Jürgen Sachs schrieb:

> Ich wusste gar nicht dass man const class member erst im Construtor
> Initialisieren darf.

Man darf sie nur dort initialisieren, und zwar nur in der
member initialization list (also auch nicht erst innerhalb der
folgenden geschweiften Klammern).

> Muss man hier die Parameter Variablen (Port, Pin, DDR) nicht auch als
> "const" declarieren, zumindest der Form halber ?

So genau kenne ich mich da auch nicht aus, aber ich denke nicht.

> Jetzt müsste ich von der Klasse LED nur noch ein Array definieren und
> alles ist gut.

Habe mal etwas nachgelesen.  Scheint seit C++11 dabei zu sein. 
Zumindest
versteht GCC 4.7 sowas:
1
int
2
main(void)
3
{
4
  led leds[2] = {
5
    led(0, &DDRB, &PORTB, &PINB),
6
    led(1, &DDRB, &PORTB, &PINB)
7
  };
8
9
  leds[0].set();
10
  leds[0].toggle();
11
  leds[0].clear();
12
  leds[1].set();
13
14
  return 0;
15
}

von Jon (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Habe mal etwas nachgelesen.  Scheint seit C++11 dabei zu sein.
> Zumindest
> versteht GCC 4.7 sowas:

Man kann doch schon immer ein Array aus Objekten einer Klasse anlegen, 
oder nicht? Zumindest bin ich bisher davon ausgegangen. Wie gesagt, C++ 
ist bisher noch eine Baustelle bei mir...
Ahja, oder ging das etwa bisher nur mit STD::Vektor und seit C++11 auch 
mit "Plain C Arrays"?

Noch eine Frage @ Jörg Wunsch:
Der Assembler-Output überzeugt mich :-) Mir ist nur nicht klar wie der 
Compiler das hinkriegt. Ich vermute das geht nur weil:
- die Klasse in der aktuellen Übersetzungseinheit komplett bekannt ist, 
d.h. auch der Code des Konstruktors ist bekannt
- &PORTB und Konsorten zur Compilezeit bekannt sind

Was wäre denn wenn ich meine Klasse "led" in Header und Source-File 
auftrennen würde und nur die Schnittstellen im Header bekannt mache? Ich 
denke, dann muss der Compiler wieder die umständlicheren Befehle 
benutzen...

(Kann das leider nicht nachprüfen, da ich unterwegs bin und auf dem Win 
Rechner keinen gcc habe :/)

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


Lesenswert?

Jon schrieb:

> Man kann doch schon immer ein Array aus Objekten einer Klasse anlegen,
> oder nicht?

Vor C++11 aber offenbar nur mit dem Default-Konstruktor der Klasse,
was für diesen Fall (Initialisierung von const members) nicht hilft.

> Noch eine Frage @ Jörg Wunsch:
> Der Assembler-Output überzeugt mich :-) Mir ist nur nicht klar wie der
> Compiler das hinkriegt. Ich vermute das geht nur weil:
> - die Klasse in der aktuellen Übersetzungseinheit komplett bekannt ist,
> d.h. auch der Code des Konstruktors ist bekannt
> - &PORTB und Konsorten zur Compilezeit bekannt sind

Ja, irgendwie so.

> Was wäre denn wenn ich meine Klasse "led" in Header und Source-File
> auftrennen würde und nur die Schnittstellen im Header bekannt mache?

Kann man nur mal ausprobieren, ja.  Aber es ist natürlich logisch, der
Compiler kann nur bauen, was er auch kennt.

Habe keine Ahnung, inwiefern dann LTO da noch helfen könnte.

von Josef D. (jogedua)


Lesenswert?

Jon schrieb:
> Was wäre denn wenn ich meine Klasse "led" in Header und Source-File
> auftrennen würde und nur die Schnittstellen im Header bekannt mache? Ich
> denke, dann muss der Compiler wieder die umständlicheren Befehle
> benutzen...

Ist das wirklich eine Einschränkung?
Mir scheint, dass ist wie mit den inline-Funktionen in C.
Die müssen doch wohl auch im Header stehen, wenn man sie in mehreren 
Modulen
benutzen will.

von Jürgen S. (jsachs)


Lesenswert?

Mal kurz als Meldung zum eigentlichen Problem. :-)

Der Tipp mit den zusätzlichen Funktionen war die Urspüngliche Ursache.
Ich hatte in der Moduldefinition das I2C Debugging nicht deaktiviert, 
was Fatal war.
Für die die es interessiert, ich habe ein Include für ein Makefile, in 
dem die wichtigsten Spezifikationen Definiert werden. Hierdurch passt 
sich der Code auf den Modultyp und die Eigenschaften an. In der erstem 
CCMODULE_DEFS Zeile Fehlte die "#" vor dem "-D_DEBUG_ENABLED_":
1
else ifeq ($(TYPE), 1003)
2
  #BCA8-BTM222 mit 8MHz build in
3
  MODULE_TYPE="1003"
4
  MODULE_ID=0x1003
5
  MY_DEF_ADDR=ADDR_RX_MAIN;
6
  BOOTLOADER_ADR=0x7000
7
  F_CPU=8000000UL
8
  MCU=atmega328p
9
  MODULE_VERSION=\"0.2.1\"
10
  MY_DEF_ADDR=$(ADDR_RX_MAIN_DEF)
11
  L_FUSE=0xe2
12
  H_FUSE=0xd0
13
#  H_FUSE=0xd1
14
  E_FUSE=0xff
15
  CC_MODULE_DEFS = -D_TYPE_1003_=$(MODULE_TYPE) #-D_DEBUG_ENABLED_
16
  CC_MODULE_DEFS += -D_HAS_SPS_="_HAS_SPS_"
17
  MAX_FUNC_CNT=32
18
else ifeq ($(TYPE), 1004)

Das war allerdings nicht die alleinige Ursache, danach startete der Code 
Permanent neu.
Um es einzugrenzen habe ich dann irgendwann einen codeblock eingebaut 
wie diesen (Beispielhaft)
1
while(1)
2
{
3
  uartPuts("Ich Lebe");
4
  delay(lange);
5
}
Und habe mich so langsam vor gearbeitet, bis ich wieder im Hauptloop 
war.
Dort bin ich ohne weitere Änderung angekommen, habe dieses Konstrukt 
wieder entfernt und es lief.
Anschließen habe ich noch entdeckt, das meine Servoimpulse zu lange 
sind, da diese sich nicht an F_CPU anpassen, was schnell korrigiert war. 
Ich habe noch ein leichtes Jittern, laut Osci und Servo knurren. Sofern 
mein Osci das anzeigen kann ca 1,425ms und 1,43ms. Also ca 0,005ms.
Genauer kann ich es an meinem Scope nicht ablesen.
Ist so ein Jitter von ca. 5us noch in den Griff zu bekommen?

Punkt ist, es geht jetzt. Ich habe noch einige Punkte in Sachen Hardware 
entdeckt. Falscher PIN, habe irgendwie überlesen, das ADC6 und ADC7 
reine ADC und keine IO sind. Das muss ich noch anpassen, aber mit etwas 
Fädeldraht ist das auch schnell gemacht.

Vorhin habe ich nochmal einen SVN Diff gemacht zwischen den Versionen am 
Anfang Rev 21.01.2014@703 und Rev 25.01.2014@727 und kann leider, bis 
auf oben genannte Punkte keine Relevante Punkte erkennen.

Daher mal Asche auf mein Haupt und riesigen Dank an Eure Hilfe.
Gruss
Juergen

PS: Ich werde das mal mit dem "LED" Objekt testen und hier noch 
berichten, auch wenn es jetzt nicht ganz zum Thema passt.

von Jürgen S. (jsachs)


Angehängte Dateien:

Lesenswert?

So,

wie versprochen, hab ich mal einen Test gemacht, mit den io_ports class.
Ein paar kleine Fehler hatten sich eingeschlichen bei Jörg, aber er 
wollte ja nur, das ich meinen Kopf anstrengen muss :-)

Ich habe die Beispiel Files mal angehängt, die include Pfade müsst Ihr 
anpassen....
Und es ist Linux Code, als nur "CR" Zeilenende ;-)
1
#include <avr/io.h>
2
3
#include "../global_files/io_port.h"
4
5
class allIOs
6
{
7
public:
8
  io_port ios[5] = {
9
  // LED PD5, PD6
10
  io_port(PD5, &DDRD, &PORTD, &PIND, true),
11
  io_port(PD6, &DDRD, &PORTD, &PIND, true),
12
  // Schalter PD2, PD3, PD4
13
  io_port(PD2, &DDRD, &PORTD, &PIND, false),
14
  io_port(PD3, &DDRD, &PORTD, &PIND, false),
15
  io_port(PD4, &DDRD, &PORTD, &PIND, false)
16
  };
17
}allios;
18
19
20
21
22
int main(void)
23
{
24
    while(1)
25
    {
26
        io_port *io = &allios.ios[0];
27
        io->toggle();
28
29
        if(allios.ios[2].readPin())  // nicht entprellt, nur fuer test !
30
            allios.ios[1].toggle();
31
    }
32
}

ist nur 860 Bytes groß. Und mal für ein Pollin Evaluation Board 
angepasst.
avr-size -x main_io_ports.elf
   text     data      bss      dec      hex  filename
  0x35c      0x0     0x23      895      37f  main_io_ports.elf

Ich muss sagen, der Ansatz hat was !
Ob das jetzt Ressourcen spart oder nicht, geht wieder über meine 
Assembler Kenntnisse hinaus.
Auf alle Fälle kann man schön "Abstrakt" Programmieren :-)

Die Klasse allIOs ist in dem Beispiel eventuell nicht notwendig, aber 
bei mir sind hier noch mehr variablen verpackt, daher wollte ich das so 
mal testen.

Gruss
Juergen

von Ralf G. (ralg)


Lesenswert?

Ich muss mich mal reindrängeln:

Jon schrieb:
> Mir ist nicht ganz klar wie du den Port/Pin festhalten kannst ohne dem
> Compiler sämtliche Optimierungsmöglichkeiten zu nehmen...

+1

Ich füttere meinen Compiler mit dem Code ...

Jörg Wunsch schrieb:
> ...

... und von Optimierung keine Spur! Portadressen aus dem Speicher holen, 
Funktionen aufrufen.


Jörg Wunsch schrieb:
> Das Compilat:
1
 .global main
2
         .type   main, @function
3
 main:
4
 /* prologue: function */
5
 /* frame size = 0 */
6
 /* stack size = 0 */
7
 .L__stack_usage = 0
8
         sbi 0x17,0
9
         sbi 0x18,0
10
         ldi r24,lo8(1)
11
         out 0x16,r24
12
         cbi 0x18,0
13
         ldi r24,0
14
         ldi r25,0
15
         ret

Wo kommt denn da sowas her?
Was hab' ich schon wieder verpasst?

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


Lesenswert?

Ralf G. schrieb:
> Wo kommt denn da sowas her?

Was bitte?  Wie man zu dem Compilat kommt, oder was ist deine Frage?

von Ralf G. (ralg)


Lesenswert?

Jörg Wunsch schrieb:
> Was bitte?  Wie man zu dem Compilat kommt, oder was ist deine Frage?

Ich habe das so interpretiert, dass der Quellcode (obendrüber) zu diesem 
Ergebnis führt. Irgendwas muss dort doch fehlen?

Weil:

Ralf G. schrieb:
> Jon schrieb:
>> Mir ist nicht ganz klar wie du den Port/Pin festhalten kannst ohne dem
>> Compiler sämtliche Optimierungsmöglichkeiten zu nehmen...

Als Ergebnis hätte ich vermutet, dass ganz normal auf die Ports 
zugegriffen wird, so als wären sie global. Aber das Ergebnis sind bei 
mir ganz normale ('umständliche') Zeigeroperationen und sogar 'richtige' 
Funktionsaufrufe (Bei dem einen Mal hätte ich inlinen erwartet.)

Edit:
zwei Änderungen habe ich noch am Quelltext vorgenommen:
- Die Initialisierung von 'pinmask' am Anfang erzeugte bei mir eine 
Warnung.
-
1
void toggle(void) { *pin = pinmask; }
nach
1
void toggle(void) { *pin ^= pinmask; }

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ralf G. schrieb:

> Ich habe das so interpretiert, dass der Quellcode (obendrüber) zu diesem
> Ergebnis führt.

Ja.

> Irgendwas muss dort doch fehlen?

Nein, das Beispiel war vollständig.  Habe es per copy&paste eben
nochmal compiliert.

> Als Ergebnis hätte ich vermutet, dass ganz normal auf die Ports
> zugegriffen wird, so als wären sie global. Aber das Ergebnis sind bei
> mir ganz normale ('umständliche') Zeigeroperationen und sogar 'richtige'
> Funktionsaufrufe (Bei dem einen Mal hätte ich inlinen erwartet.)

Dann hast du einen zu alten Compiler.  Ab GCC 4.7 jedenfalls ergibt
sich das, was du da sehen kannst.

> Edit:
> zwei Änderungen habe ich noch am Quelltext vorgenommen:
> - Die Initialisierung von 'pinmask' am Anfang erzeugte bei mir eine
> Warnung.

OK, ich hatte keine Warnungen aktiviert.  Ist eine Warnung, dass er
die Konstruktoraufrufe umsortiert, muss man entsprechend tauschen:
1
    led(uint8_t n, volatile uint8_t *r, volatile uint8_t *p, volatile uint8_t *i):
2
      ddr(r), port(p), pin(i), pinmask(1 << n) {

>
1
void toggle(void) { *pin = pinmask; }
> nach
>
1
void toggle(void) { *pin ^= pinmask; }

Nein, das ist falsch.  Bei allen hinreichend neuen AVRs ist die
effizientestes Methode, einen Portpin auf PORTx zu toggeln, dass man
dessen Bitwert auf PINx schreibt.  Die XOR-Variante müsste auf
PORTx gehen, erzeugt aber aufwändigeren Code.

von Ralf G. (ralg)


Lesenswert?

Jörg Wunsch schrieb:
> dass man
> dessen Bitwert auf PINx schreibt.

Ah ja. Totaler Käse von mir. Na klar! PORTx ...

Jörg Wunsch schrieb:
> Dann hast du einen zu alten Compiler.  Ab GCC 4.7 jedenfalls ergibt
> sich das, was du da sehen kannst.

Hab' mir extra dazu gestern den 4.7.2 runtergeladen und eingestellt. :-/

Edit:
Allerdings: 'make' war da nicht mit dabei. Da habe ich das vom 4.5er 
genommen. Wird da vielleicht noch was 'umsortiert'?

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ich habe das einfach auf der Kommandozeile compiliert mit:
1
avr-gcc -Os -mmcu=atmega1281 -S leds.C

von Ralf G. (ralg)


Lesenswert?

Jörg Wunsch schrieb:
> Ich habe das einfach auf der Kommandozeile compiliert mit:

Das habe ich jetzt mal versucht. Da wird allerdings automatisch die alte 
Version verwendet. (Die mit der Toolchain 'richtig' installiert wurde?) 
Das Ergebnis ist gleich (riesig). Ich habe die *.C-Datei mal in das 
Verzeichnis von 'avr-gcc' kopiert und den Aufruf von diesem Verzeichnis 
gestartet. Ergebnis:
avr-gcc: error: CreateProcess: No such file or directory

Warum kann man im AVR-Studio unter Custom Options/External Tools/avr-gcc 
nicht einfach den Compiler eintragen und das funktioniert?

von Ralf G. (ralg)


Lesenswert?

Soooooo: Fehler gefunden.
- 'avr-gcc' unter 'Custom Options/External Tools/avr-gcc' mit
  richtigem(!) Pfad eingetragen
- Compileroption -v eingetragen und festgestellt: Das interessiert 'den'
  überhaupt nicht! Nimmt einfach die 4.5er Version!
Also: direkten Verweis auf 'avr-gcc-4.7.2' -> jetzt geht's!

von Ralf G. (ralg)


Lesenswert?

Ausnahmsweise. (Falls in dem Thread noch Platz ist.)
Wieso vervielfacht sich die Codegröße, wenn L1 außerhalb von 'main' 
definiert wird? Ich würde die Variante übersichtlicher für die Kontrolle 
des RAM-Bedarfes halten.

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.