Zu großer Code, obwohl winziges Programm

Persönliche Seite #1088671
Lesenswert?

1
#include <avr/io.h>      //Grundfunktionen
2
#ifndef F_CPU        //Vordefinieren für delay.h
3
#define F_CPU 1000000UL    //Definition von F_CPU in Herz
4
#endif      
5
#include <util/delay.h>    //Warteschleifen
6
#include <stdint.h>
7

8
int main (void)        //Hauptfunktion
9
{              //Anfang der Hauptfuktion
10
DDRB=0b11111111;      //Alles Ausgänge
11
uint16_t a;          //Variable 16Bit
12
char i;            //Zählvariable für die Schleife
13
a=1000;            //Der Impuls soll 1ms lang sein
14
  while(1)      
15
  {
16
  i=50;          //Durch diese Definierung ist die While(i) Schleife immer 1s (50*0,02s) lang
17
    while(i)
18
    {
19
    PORTB=0b11111111;
20
    _delay_us(a);    //Impulslänge
21
    PORTB=0b00000000;
22
    _delay_us(20000-a);  //Hierdurch wird erreicht, dass das Gesamtsignal genau 20ms lang ist
23
    i--;
24
    }
25
  a=a+100;        //Ich will aber das die Impulse länger werden
26
    if(a==2000)      //Da aber ab einer Impulslänge von über 2ms der Servo kaputt gehen kann, wird hier nun die Notbremse gezogen
27
    {
28
    a=1000;
29
    }
30
}
31
return 0;
32
}

Hallo,
Dieser Code hat die Funktion 11 verschiedene Pulslängen an PORTB eines 
Attiny2313 zu bilden. Das Problem ist aber, dass der Code eine Größe 
erreicht die etwa doppelt so groß ist, wie der Speicherplatz eines 
Attiny2313. Die Optimierung 0s ist eingestellt.
Vielleicht könnte mir ja jemand helfen. Danke schonmal im Vorraus.

MfG
Inox
#1088682
Lesenswert?

Dein Flashfresser dürfte die float-Arithmetik sein, die von _delay_us 
benötigt wird.

Es wäre besser, wenn du einen Timer programmierst und aus der 
Interruptroutine heraus dem Hauptprogramm signalisierst, daß soundsoviel 
Zeit vergangen ist.

Das geht dann mit reiner int-Arithmetik...
Gast #1088685
Lesenswert?

Hallo,

vielleicht wegen diesen Zeilen:
> _delay_us(20000-a);
>a=a+100;

Soweit ich weiss mag die AVR Lib C dies nicht. Dort steht,
das für die _delay_us Funktionen möglichst Konstanten benutzt
werden sollen.
Quote AVR LibC Reference:

In order for these functions to work as intended, compiler optimizations 
must be enabled, and the delay time must be an expression that is a 
known constant at compile-time. If these requirements are not met, the 
resulting delay will be much longer (and basically unpredictable), and 
applications that otherwise do not use floating-point calculations will 
experience severe code bloat by the floating-point library routines 
linked into the application.

Vielleicht solltest du dich mit den Timern der Avr beschäftigen.
Die Tutorials bieten hier einen guten Einstieg.

gruß ralf
#1088737
Lesenswert?

Hi,

du kannst dir ja mal das Assemblerlisting oder das map-file angucken. Da 
siehst du, was dein Programm so aufbläst.

_delay_us ist so deklariert:
1
void _delay_us(double __ms)
und macht auch float-Berechnungen während der Laufzeit, die nicht weg 
optimiert werden können, wenn du keine Konstante übergibst.
Alternativ kannst du mal versuchen, eine eigene for-Schleife zu 
schreiben und _delay_loop_1() oder _delay_loop_2() aufzurufen. Die sind 
in der "delay_basic.h" deklariert.

Oder du nimmst einen Timer und wartest, bis das Overflow-Bit gesetzt 
wird.

Gruß
Thomas
Gast #1088788
Lesenswert?

> hm aber die idee mit dem "loopen" der delay_us(1) ist gar nicht
> schlecht.
> Werde es jetzt vorerst auch mal so machen, mal schaun wie genau das
> wird...

Da die Delay-Zeiten immer Vielfache von 100µs sind, würde ich eine
Schleife um einen _delay_us(100) machen. Das macht die Sache genauer.
Reicht die Genauigkeit immer noch nicht, machst du die 100 um so viel
kleiner, wie es dem Schleifen-Overhead entspricht. Das Argument von
_delay_us darf dabei durchaus auch eine gebrochene Zahl, also z.B.
97.4 sein, wichtig ist nur, dass es konstant ist.

Aber früher oder später solltest du es auch mit Timern versuchen, den
dafür sind diese da, und du hast ja schließlich auch Geld dafür bezahlt.
Der Hauptvorteil ist, dass damit der komplette Zeitbedarf des restlichen
Codes der in den Schleifen deines obigen Programms steht, weitgehend
kompensiert wird. Und solltest du irgendwann noch irgendwelche
Interrupts verwenden, stimmen die Zeiten der _delay_xx-Funktionen
überhaupt nicht mehr, da jede Unterbrechung durch einen Interrupts ihre
Ausführungszeit um den Rechenzeitbedarf des Interrupthandlers
verlängert.

Uhu Uhuhu schrieb:
> Wenn du mit delay_us(1) einen Aufruf von _delay_us mit Parameter int 1
> meinst, wirst du wenig Glück haben. _delay_us braucht selbst
> Fließkommaarithmetik.

Aber nur zur Compilezeit, nicht zur Laufzeit, deswegen stört es nicht.
#1088939
Lesenswert?

Halte ich für ein Gerücht. Man kann auch den Maschinencode, den der 
Compiler erzeugt hat, untersuchen und den Code so modifizieren, daß 
möglichst wenig unnötiger Code entsteht.

Das ist weniger Gefummel, als wenn man alles in ASM schreibt und man 
lernt dabei, sehr effizienten C-Code zu schreiben und nebenbei noch den 
Assembler.
Persönliche Seite #1088952
Lesenswert?

Mein Problem mit dem Timer ist aber im Moment folgendes und zwar bekommt 
mein Timer0 (8Bit) 1000000 Impulse in der Sekunde (kein Vorteiler), das 
heißt wiederum das mein Timer 3906,25 pro Sekunde überläuft und das ist 
schon zu langsam, weil der Timer folglich dann nur alle 0,256ms 
überläuft. Der Timer müsste aber alle 0,1ms überläufen.

Mach ich nur en Denkfehler oder besteht die Lösung darin dem µC einen 
höheren Takt zu geben? Oder ließe sich das noch ganz anders lösen?

MfG
Inox

PS: Frohe Weihnachten! :-)
(Firma: HSCS) #1088969
Lesenswert?

das was du da machen willst, kann der timer ganz allein! benutze den 
pwm-modus und stelle den vorteiler so ein, dass der timer nach 20ms 
überläuft und einen interrupt auslöst. mit dem compare-register stellst 
du dann die breite der impule ein, die du haben willst. wenn der 
interrupt 50mal durchgelaufen ist, änderst du den wert der in das 
compare-register geladen wird. trenne dich von deinen zeitschleifen, das 
ist nonsens, die hardware kann das viel besser und effizienter und der 
code wird viel kürzer.
Gast #1088979
Lesenswert?

Im Datenblatt auf Seite 68 im Absatz "Clear Timer on Compare Match Mode"

Im CTC Modus Zählt der Counter (TCNT++) hoch bis zu einem Vergleichswert 
der in OCR0A (TCNT==OCR0A) gespeichert ist.

Der hat den Modus also doch.

Das Vorladen des TCNT im Normalen Modus sollte aber auch gehen.
(Firma: HSCS) #1089017
Lesenswert?

Gast wrote:
> Im Datenblatt auf Seite 68 im Absatz "Clear Timer on Compare Match Mode"
>
wenn er sich nun mit dem timer ein delay zaubern will, dann macht das 
sinn. so wie sein programm aussieht will er eine pwm und das kann der 
timer auch, dass einzige was sein programm tun muss ist die 
compare-werte für das tastverhältnis nachzuladen, den rest macht der 
timer selbst.
Gast #1089044
Lesenswert?

Zu groß? Kein Wunder. _delay wird nicht mit einer Konstanten aufgerufen.

Ralf W. wrote:
1
In order for these functions to work as intended, compiler 
2
optimizations must be enabled, and the delay time must be an expression 
3
that is a known constant at compile-time. If these requirements are not 
4
met, the resulting delay will be much longer (and basically unpredictable),
5
and applications that otherwise do not use floating-point calculations will 
6
experience severe code bloat by the floating-point library routines linked 
7
into the application.
Persönliche Seite #1091629
Lesenswert?

Guten Abend,
Ich hab mir gedacht ich berichte euch mal was ich nun gemacht habe:
1
#include <avr/io.h>      //Grundfunktionen
2
#ifndef F_CPU        //Vordefinieren für delay.h
3
#define F_CPU 1000000UL            //Definition von F_CPU in Herz
4
#endif      
5
#include <util/delay.h>            //Warteschleifen
6
#include <stdint.h>
7
#include <avr/interrupt.h> 
8

9
ISR(TIMER1_COMPA_vect)
10
{
11
OCR1A=2500-OCR1A;   //Im ISR wird das Steuersignal für den Servo, aus der Differenz der Periodenlänge und des vorherigen Vergleichswert gebildet
12
}
13

14

15
int main (void)        //Hauptfunktion
16
{        
17
DDRB=0b11111100;      //Alles Ausgänge bis auf PB0 und PB1
18
PORTB=0b00000011;      //Pullup für PB0 und PB1
19
TCCR1A=0b01010000;      //Tooglen von PB3
20
TCCR1B=0b00001010;      //CTC-Mode und Prescaler 8; Prozessortakt
21
TIMSK=0b01000000;      //Interrupt bei Timer1 Comp.Match A
22
OCR1A=2375;    //das bringt den Servo in Neutrallage (1,5ms)
23
sei();         //Interrupts an
24
while(1)      //Programmschleife
25
{
26
  if ( !(PINB & (1<<PINB0)) )     //Abfrage eines Schalters
27
  {
28
  OCR1A=OCR1A+3;
29
  _delay_ms(50);                  //Verlangsamung der Veränderung
30
  }
31
  if ( !(PINB & (1<<PINB1)) )     //Abfrage eines Schalters
32
  {
33
  OCR1A=OCR1A-3;
34
  _delay_ms(50);                  //Verlangsamung der Veränderung
35
  }
36
}
37
return 0;  //C-Standard
38
}

Letzendlich bin ich am CTC-Modus hängengeblieben ;), nie gedacht, dass 
es so simpel ist, auch mit den Interrupts...
Dieses Programm erzeugt mit Erfolg meine gewünschte PWM, die ich für den 
Servo brauche. Zusätzlich kann man das PWM Signal in 0,024ms Stufen 
verändern, d.h. den Servo aus der Neutralposition links oder rechts 
drehen lassen. Dies geschieht durch die 2 angeschlossenen Schalter.
Mehr dazu bald unter Modellbauservo Ansteuerung im wiki.

Danke nochmal für eure Hilfe,

MfG
Inox
#1091658
Lesenswert?

Hallo Daniel

Die Timer-Version ist natürlich die sauberere Lösung.

Aber wie Kürbis schon sagte, so wärs auch gegangen:
AVR Memory Usage
Device:  attiny2313
Program: 198 bytes (9.7% Full)
1
#include <avr/io.h>
2
#include <util/delay.h>
3

4
void 
5
my_delay_us(unsigned int ms)                          // delay ms
6
{
7
   while(ms) {
8
      _delay_us(0.96);
9
      ms--;
10
   }
11
}
12

13
int 
14
main(void)
15
{
16
   DDRB=0b11111111;     //Alles Ausgänge
17
   uint16_t a;          //Variable 16Bit
18
   char i;              //Zählvariable für die Schleife
19

20
   a=1000;                 //Der Impuls soll 1ms lang sein
21
   while(1)      
22
   {
23
      i=50;                //Durch diese Definierung ist die While(i)
24
                           //Schleife immer 1s (50*0,02s) lang
25
      while(i)
26
      {
27
         PORTB=0b11111111;
28
         my_delay_us(a);   //Impulslänge
29
         PORTB=0b00000000;
30
         my_delay_us(20000-a);  //Hierdurch wird erreicht, dass das
31
                           //Gesamtsignal genau 20ms lang ist
32
         i--;
33
      }
34
      a=a+100;             //Ich will aber das die Impulse länger werden
35
      if(a==2000)          //Da aber ab einer Impulslänge von über 2ms 
36
                           //der Servo kaputt gehen kann, 
37
                           //wird hier nun die Notbremse gezogen
38
      {
39
         a=1000;
40
      }
41
   }
42
   return 0;
43
}
Gast #1091782
Lesenswert?

Dashier:
1
void 
2
my_delay_us(unsigned int ms)                          // delay ms
3
{
4
   while(ms) {
5
      _delay_us(0.96);
6
      ms--;
7
   }
8
}

ist unbestreitbar ein krasser Widerspruch zu demhier:
1
my_delay_us(20000-a);  //Hierdurch wird erreicht, dass das
2
                       //Gesamtsignal genau 20ms lang ist
Persönliche Seite #1091795
Lesenswert?

Daniel B. wrote:
>   OCR1A=OCR1A+3;

Auch wenn dein Programm funktioniert, so hat es noch ein paar Fußangeln.

Sehr versteckt im Tutorial findest du dazu das hier:

http://www.mikrocontroller.net/articles/Interrupt#Atomarer_Datenzugriff

OCR1A ist ein 16-Bit Register, das nicht atomav verändert werden kenn. 
Wenn während des Inkrementierens um 3 eine IRQ auftritt, kann das 
zuProblemen führen (reca condition) und das Servo macht nen Sprung.
Persönliche Seite #1091904
Lesenswert?

1
int main (void)        //Hauptfunktion
2
{              //Anfang der Hauptfuktion
3
DDRB=0b11111100;      //Alles Ausgänge bis auf PB0
4
PORTB=0b00000011;      //Pullup für PB2
5
TCCR1A=0b01010000;      //Tooglen von PB3
6
TCCR1B=0b00001010;      //CTC-Mode und; Prescaler 8; Prozessortakt
7
TIMSK=0b01000000;      //Interrupt
8
OCR1A=2375;          //Neutralposition (2375*0,008=1,5ms)
9
sei();            //Interrupt an
10
while(1)
11
{
12
  if ( !(PINB & (1<<PINB0)) )  //Abfrage eines Schalters
13
  {
14
        cli();
15
  OCR1A=OCR1A+3;
16
        sei();
17
  _delay_ms(50);        //Verlangsamung der Veränderung
18
  }
19
  if ( !(PINB & (1<<PINB1)) )  //Abfrage eines Schalters
20
  {
21
        cli();
22
  OCR1A=OCR1A-3;
23
        sei();
24
  _delay_ms(50);        //Verlangsamung der Veränderung
25
  }
26
}
27
return 0;
28
}

Ich hab das jetzt so gemacht. Geht auch oder?

MfG
Inox
#1091929
Lesenswert?

Hi Daniel,

die Idee mit dem atomaren Zugriff ist zwar im Prinzip richtig, beim 
Zugriff auf die Timerregister (ausnahmsweise) überflüssig: den atomaren 
Zugriff regelt die Timer-Hardware selber (siehe ATtiny Manual S. 88: 
accessing 16 Bit registers).

Ein anderes, noch wichtigeres Problem regelt die Timerhardware im 
PWM-Modus aus selbstständig:

angenommen, Dein OCR1A steht auf 15 und Dein Counter zählt hoch:
10, 11, 12, 13, 14, 15, 16, 17, 18, ....
Zum Zeitpunkt, an dem der Counter auf 13 steht, liest Du OCR1A aus 
(=15), ziehst 3 ab und schreibst das Ergebnis (=12) zurück nach OCR1A.

Das Ergebnis ist:
Dein Counter hat den Match mit OCR1A verpasst!

Um das zu vermeiden, ist OCRnA doppelt-gepuffert (siehe auch Man. S. 
95):
Wenn Du OCRnA änderst, änderst Du nur ein Schattenregister. Dessen Wert 
wird immmer dann in das "richtige" OCRnA übernommen, wenn der Timer 
überläuft bzw. NULL erreicht (genaueres steht im Man. bei den 
verschiedenen PWM-Modi beschrieben).

Fazit:
Dein erstes Hardware-PWM Programm war korrekt.

Viel Spass noch, Stefan
#1091964
Lesenswert?

@  Stefan Kleinwort (sk)

>die Idee mit dem atomaren Zugriff ist zwar im Prinzip richtig, beim
>Zugriff auf die Timerregister (ausnahmsweise) überflüssig: den atomaren
>Zugriff regelt die Timer-Hardware selber (siehe ATtiny Manual S. 88:
>accessing 16 Bit registers).

Das reicht nicht!

MFG
Falk
(Firma: HSCS) #1092017
Lesenswert?

also ich verstehe das alles nicht. warum werden hier komische 
zeitschleifen programmiert, wenn der timer einen pwm-modus hat und es 
ganau das ist worum es hier geht. ich habe das ganz zum anfang schon 
gesagt. der threadersteller hat dann ja den ctc-modus genutzt und sich 
wieder ein delay gebastelt. warum macht man sowas?? nur weil das auch 
gehen muss??? ich kapiers nicht, gibt es da einen wichtigen grund für??
das ist doch so, als wenn ich mir sage: "mensch eine tür müsste man in 
autos einbauen und dann könnte man sogar einsteigen und mitfahren." und 
danach wird tagelang darüber diskutiert, wie man fenster so verändern 
könnte, dass man dadurch bequem einsteigen kann, dabei gibt es die tür 
längst!

ich versteh es nicht!
Gast #1092023
Lesenswert?

Hallo Frank,

der TS benutzt doch mittlerweile den PWM, und das ist immerhin Dein 
Verdienst!
Die Delays, die noch in dem Programm sind, sin ja für die 
"Bedienoberfläche", das ist ne andere Baustelle und ich denke, so nur 
zum Testen gedacht.

Viele Grüße, Stefan
Gast #1092202
Lesenswert?

@Falk:

Manual ATmega32, S.89:
"When the low byte of a 16-bit register is written by the CPU, the high 
byte stored in the temporary register, and the low byte written are both 
copied into the 16-bit register in the same clock cycle. When the low 
byte of a 16-bit register is read by the CPU, the high byte of the 
16-bit register is copied into the temporary register in the same clock 
cycle as the low byte is read".

Solange ein IR nicht das temporary register ändert, ist ein atomarer 
Zugriff nicht notwendig - diesen erledigt in diesem Fall die 
Timer-Hardware (beide 8-Bit-Register werden von der HW gleichzeitig [in 
the same clock cycle] in die 16-Bit Timer-Register geschrieben).

Gruß, Stefan
#1092221
Lesenswert?

@  Stefan (Gast)

>Solange ein IR nicht das temporary register ändert, ist ein atomarer
>Zugriff nicht notwendig

Das tut sie in diesem Beispiel aber.

1
OCR1A=2500-OCR1A;

> - diesen erledigt in diesem Fall die
>Timer-Hardware (beide 8-Bit-Register werden von der HW gleichzeitig [in
>the same clock cycle] in die 16-Bit Timer-Register geschrieben).

Du verwechselt den atomaren Zugriff auf Hardwareregister mit dem 
atomaren Zugriff auf normale Variabeln im RAM. Bei BEIDEN muss man 
aufpassen und mit cli/sei arbeiten, wenn auf die selbe Variable/Register 
im Hauptprogramm UND einer ISR zugegriffen wird. Der Mechanismus mit dem 
temp-Register ermöglicht "nur" den atomren zugriff auf 16 Bit 
Hardwareregister mit einer 8 Bit CPU. cli/sei würden dort gar nicht 
nützen, das beeinflusst die 16 Bit Register keine Nanosekunde.

MFG
Falk
Gast #1092597
Lesenswert?

@Falk, @Johann:
Ok, ich bin vom Source des TS von 12:50 ausgegangen, dort ist kein 
Interrupt vorhanden. (wahrscheinlich ist er aber einfach nicht 
mitgepostet worden).
Mit IR, jedenfalls so, wie er um 00:54 gepostet wurde, habt Ihr 
natürlich Recht.

@Daniel:
Warum benutzt Du den CTC-Mode? Der Timer hat so viele schöne PWM-Modi, 
die komplett in der Timer-Hardware, ohne einen Software-Eingriff laufen.

Gruß, Stefan
Persönliche Seite #1092634
Lesenswert?

@Stefan
Tut mir leid, hab da nur die Hauptfunktion gepostet. Wollte aus 
Platzgründen nicht den Rest noch dazu posten.

Ja zudem war das mein erstes Programm, das den Timer nutzt. Warum hab 
ich nicht einen anderen Modus genutzt? Gute Frage, mir wurde der 
CTC-Modus schonmal vorgeschlagen und naja da hab ich mir gedacht, der 
wird wohl mehr Ahnung ham als ich, also machst du es mit dem CTC-Modus^^
Nunja fürs nächstemal werd ich genauer schaun, welcher Modus für mein 
Problem passend ist.

MfG
Inox

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