Forum: Compiler & IDEs inline hauptloop & zugriff auf C-Variablen


von Dominik B. (theomega)


Lesenswert?

Hallo Leute,
folgende Situation:
Es soll in einem C-Programm (WinAVR, Tiny13) die Haupschleife:
=========
volatile uint16_t counter;

int main() {
  while (1==1) {
    counter++;
  }
}
=========
durch eine InLine-ASM-Funktion ersetzt werden. Der Wert der
Counter-Variable muss dabei ständig aktuell gehalten werden, da diese
in diversen Interrupts verwendet wird.

Ich habe folgenden Code konstruiert:
=========
int main() {
asm volatile("loop: \n\t " \
"ADIW %0,1 \n\t" \
"rjmp loop" : : "w" (counter) : "memory");
}
=========
der funktioniert allerdings nicht,

===========
int main() {
asm volatile("loop: \n\t " \
"ADIW %0,1 \n\t" \
"rjmp loop" :"=w"(counter) : "0" (counter));
}
===========
geht genausowenig.

Mir ist leider unklar welche Parameter der volatile-Funktion noch
übergeben werden müssen.

Wer kann mir helfen?

Danke schön
Dominik

von peter dannegger (Gast)


Lesenswert?

1. Warum willst Du ohne Grund Assembler verwenden ?

2. Wenn Du Assembler verwenden willst, mußt Du erstmal Assembler und
AVR-GCC Internas können.

ADIW funktioniert nur mit ganz bestimmten Registerpaaren und nicht mit
SRAM. Alle diese Registerpaare werden aber vom Compiler benötigt.


Peter

von Dominik B. (theomega)


Lesenswert?

Hy,
oki, danke für den Hinweis, ich gebe zu, dass ich in ASM nicht ganz der
Profi bin. Ich habe allerdings folgendes Problem unter C++:
Ich benötige zur Zeitmessung einen möglichst genauen Timer (Werte
zwischen 0,5 und 1,5 ms). Der Tiny13 hat ja nur einen Timer und dieser
ist bereits für PWM belegt kann also nicht genutzt werden. Ich will es
deshalb mit einer Schleife und einem Zählerwert machen den ich bei
Bedarf in den Interrupts auslesen und auf 0 setzten kann (Timer
neustarten). Bis jetzt habe ich das mit der o.a. while-schlaufe
gemacht. Leider ist die Zeit die der Kompiler für einen Durchlauf
benötigt nicht ganu zu bestimmen und ist bei Optimisation-Level S bei
ca. 30 Befehlen, was eindeutig zu viel ist, zumal mein Tiny nur mit
1,2Mhz läuft.

Evtl fällt ja jemand eine bessere Lösung ein als eine selbstgemachte
Timer-Schleife.

Gruß und Danke schonmal
Dominik

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


Lesenswert?

> Der Tiny13 hat ja nur einen Timer und dieser ist bereits für PWM
> belegt kann also nicht genutzt werden.

Warum sollte man ihn deshalb nicht auch noch als Timer nehmen können?
Die Basisfrequenz des PWM-Timers ist doch konstant, insofern kann man
doch auch seine Zählerwerte auslesen und als Zeitmaß benutzen.

> Ich will es deshalb mit einer Schleife und einem Zählerwert machen
> den ich bei Bedarf in den Interrupts auslesen und auf 0 setzten kann
> (Timer neustarten).

Mit all den Interrupt-Latenzen (während derer ja deine Main-Loop nicht
weiterzählt) dürfte das drastisch weniger genau werden, als den Timer
vernünftig zu benutzen.

Davon abgesehen, stellt sich natürlich die Frage, ob die Wahl eines
ATtiny13 dann die allerbeste war...

Was meinst du eigentlich, was an

while (1 == 1)

besser sei als an z. B.

while (1)

oder

for (;;)

?  Ich fand's irgendwie lustig...

von Dominik B. (theomega)


Lesenswert?

Hy,
das mit dem Timer muss ich checken, ich weiß nichtmehr genau wiso ich
das ausgeschlossen hatte. Die wahl ist auf den Tiny gefallen weil ich
eine möglichst kleine Platine basteln musste und die I/O-Ports gereicht
haben. Im nachhinein betrachtet vermutlich nicht die beste Lösung, aber
jetzt will ich nicht mein ganzen Platinen-Layout und alles nochmal über
den Haufen werfen.
Die While-Schleife fand ich lustig, keine Ahung ob die anderen wirklich
besser gewesen wären.
Ich danke dir erstmal, ich werde wohl das mit dem Timer nocheinmal
checken.

Danke
Dominik

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


Lesenswert?

> Die wahl ist auf den Tiny gefallen weil ich eine möglichst kleine
> Platine basteln musste und die I/O-Ports gereicht haben.

Nun, ich hoffe dann, du hast kein DIL-Gehäuse genommen.  In die
Grundfläche des ATtiny13-DIL passt ja fast ein ATmega8-TQFP rein. ;-)

Ansonsten: ATtiny25/45/85?  Gleiches Pinout wie ATtiny13, aber
deutlich besserer Satz an Features, u.a. zwei Timer-Kanäle.

von Dominik B. (theomega)


Lesenswert?

Hy,
nein, selbstverständlich ist alles in SMD, sonst könnte ich mir den
Aufwand ja sparen.
Ich muss mal durchrechnen ob die GEnauigkeit von meinem PWM-Timer
genügt, sonst werde ich mir wohl oder übel mal die Alternativen Tinies
anschauen müssen!

Danke auf jeden Fall

von Dominik B. (theomega)


Lesenswert?

Also, ich habe es gerade durchgerechnet: Ich kann ja einerseits den
aktuellen Wert des Timers auslesen. Da ich Phase-Correct-Mode
eingestellt habe läuft dieser immer 0-255-0-255-0 usw. Andererseits
kann ich mit einem Iterrup die Anzahl der Überläufe zählen, der
Interrupt tritt wohl jedesmal ein wenn der Timer den Wert 0 hat. Ich
habe keine Prescaler eingestellt, der Timer nimmt also mit 1,2Mhz
zu/ab.

Das Problem ist: Wenn ich jetzt den Timer-Wert nehme um die Zeit zu
stoppen (voher speichern, nacher speichern), dann reicht das nicht,
denn die Zeit die bei einem Zyklus (0-255) geht ist viel zu gering.
Wenn ich dagegen die Anzahl der Zyklen zähle, dann habe ich das
Problem, dass die Ungenauigkeit sehr hoch ist, da mein Zyklus-Counter
ja nur alle 512 Befehle erhöht wird. Beides zusammenführen geht von dem
nicht, weil ich weder vorher noch nacher weiß ob sich der Timer am
steigen oder am fallen befunden hat.

Versteht jemand was ich meine? Ich würde es gerne mit dem Tiny13
machen, auch wenn mir klar ist, das es mit einem anderen MC besser
gehen würde.

Danke schonmal
TO

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


Lesenswert?

Hmm, wenn ich das richtig sehe, müsstest du OC0B auf 255 setzen können
und bekommst dann einen Interrupt, wenn er beim Hochzählen die 255
erreicht, beim Runterzählen dann einen overflow-Interrupt, wenn er
wieder 0 wird.  Damit hast du die Information, wie die aktuelle
,,Uhrzeit'' aus Überlaufwert + aktuellem Zählerwert zu ermitteln
ist.

Ist natürlich ziemlich popelig, keine Frage.  Die ISRs würde ich in
Assembler schreiben und für die entsprechenden Flag-Werte im C-Code
ein oder zwei Register opfern (r2 und r3), damit die ISRs nicht erst
noch mit dem Stack operieren müssen.

von Dominik B. (theomega)


Lesenswert?

Hy,
danke für die Hilfestellung, leider geht es so nicht, weil ich den
Timer ja für die PWM-Generation benötige und OC0B deshalb nicht 255
sein kann.

Ich versuche es gerade mit einem 8-Bit Zähler nach der ganz oben von
mir genannten Methode zu realisieren, mal sehen ob das überhaupt
funktioniert!

Wäre auch sonst für jeden Hinweis dankbar!

Gruß
Dominik

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


Lesenswert?

> ...weil ich den Timer ja für die PWM-Generation benötige und OC0B
> deshalb nicht 255 sein kann.

Das musst du näher erklären.  Man kann doch OC0A für die
PWM-Generierung benutzen und OC0B separat davon z. B. für einen
Interrupt beim Erreichen von TOP.  Oder willst du die PWM-Zählweite
gegenüber 255 noch zusätzlich reduzieren (WGM2 = 1)?

von Dominik B. (theomega)


Lesenswert?

Hy,
ich habe gerade selbst erkannt das ich ja die zwei Compare-Units
separat verwenden kann. Evtl hilft mir das, ich bin gerade dabei
auszutüfteln wie ich die Laufzeit berechne aus folgenden Variablen:
- timeroben (Anzahl wie oft der Timer 255 erreicht hat seit dem Start)
- timerunten (Anzahl wie oft der Timer 0 erreicht hat)
- start (Timer-Wert beim Start)
- stop (Timer-Wert beim Stop)

Ich hoffe eine Formel zu finden die ohne eine Bedingung auskommt, mal
sehen ob mir das gelingt!

Danke auf jeden Fall

Dominik

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


Lesenswert?

Magst du eigentlich ein Wort drüber verlieren, was du damit genau
vorhast?

Hintergrund: ich suche noch nach einer irgendwie sinnvollen
,,Story'',
um in der avr-libc ein Beispiel für die Kombination aus C- und
Assembler-Quellen zu beschreiben.  Nun kann ich mir leicht vorstellen,
dass man vielleicht eine Assembler-ISR à la

.global TIM0_OVF_vect
TIM0_OVF_vect:
  sbi  PORTB, 0
  reti

für irgendetwas benutzen könnte (sehr viel kürzer als alles, was der
Compiler derzeit dafür generieren würde), nur fehlt mir irgendeine
,,Rahmenhandlung'', die eine praktische Motivation für derartiges
Tun
darstellt.

Kannst dich gern auch per pmail bei mir dazu melden, im Gegenzug biete
ich dir an, mich an der gedanklichen Lösung für die Timerfrage auch
mit konkretem Code zu beteiligen.

von Dominik B. (theomega)


Lesenswert?

Hy,
die Sache ist kein Geheimniss, das ganze wird eh sobald es funktioniert
ein OpenSource Projekt, also mit offenem Schaltplan und Quellcode.

Es geht um einen Motorcontroller, hauptsächlich für Flugzeug-Motoren.
Es werden dabei über einen Tiny13 zwei Brückencontroller mit dahinter
geschalteten Mosfets geschaltet. Hinter den Mosfets hängt ein Motor.
Via PWM kann nun die Geschwindigkeit des Motors angepasst werden. Das
ist bis jetzt alles schon realisiert und funktioniert auch.
Der zweite Teil besteht aus dem Eingang des Controllers: Über eine Ader
kommt das PWM-Signal eines RC-Empfängers (aus dem Modellbau) herein.
Dieses Signal soll dann aufgenommen und interpretiert werden und
entsprechend an den Motor wieder ausgegeben werden. Dabei kommen
Zusatzfeatures wie Kalibrierung und automatische Motorabschaltung mit
rein.

Das ganze Projekt ist eigentlich mehr eine Vorstufe zu einem anderen
Projekt wo ich auf der Suche nach einem MotorControler war und keinen
für das entsprechende Geld gefunden habe, also habe ich einen selbst
entwickelt.

Die von mir oben angesprochenen Funktionen werden gebraucht um die
PWM-Daten vom Empfänger aufzunehmen und zu interpretieren. Ich habe
inzwischen folgenden Code gestrickt, er funktioniert zumindest im
Trockendurchlauf (Atmel Studio Debuging), morgen wird sich zeigen ob es
auch wirklich funktioniert:


===============
volatile int8_t timercounter;
volatile int8_t timerstart;
volatile bool firstint;
volatile bool timersteigend;    //War beim start steigend

//Overflow vom Timer, bei Timer-Wert=0
SIGNAL (SIG_OVERFLOW0) {
  if (firstint) {
    firstint=false;
    timersteigend=false;
  }
  timercounter++;
}

//Timer nimmt den Wert 255 an (OCR0A=255)
SIGNAL (SIG_OUTPUT_COMPARE0A) {
  if (firstint) {
    firstint=false;
    timersteigend=true;
  }
  timercounter++;
}

//An PinB4 hängt der Empfänger, der Interrupt tritt bei jeder
Zustandsveränderung ein
SIGNAL (SIG_PIN_CHANGE0) {
  int8_t timerstop=TCNT0;

  if (bit_is_set(PINB,4)) {
    timercounter=0;
    timerstart=TCNT0;
    firstint = true;
  }
  else {
    uint16_t pwm = 0;
    if (timersteigend == true) {
      if (timercounter % 2 == 0) {
        pwm = timercounter*256 - timerstart + timerstop;
      }
      else {
        pwm = timercounter*256 - timerstart + (256 - timerstop);
      }
    }
    else {
      if (timercounter % 2 == 0) {
        pwm = timercounter*256 + timerstart - timerstop;
      }
      else {
        pwm = timercounter*256 + timerstart + timerstop - 256;
      }
    }
    //TUE IRGENDWAS MIT DEM PWM WERT
  }
}

==============================

Leider ist die Berechnung noch sehr aufwendig, ich bin noch auf der
Suche nach einer einfacheren Formel, evtl kannst du, oder jemand
anderes mir dabei helfen.

Danke schonmal, und wie gesagt, sobald ich die Sache abgeschlossen habe
wird das Projekt richtig öffentlich!

Gruß
Dominik

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


Lesenswert?

Bitte sende mir mal 'ne email-Adresse, ich würde das gern erst
einmal privat mit dir weiterschwatzen.

j at uriah heep sax de

von Dominik B. (theomega)


Lesenswert?

Hy,
leider werde ich aus deiner eMail-ADresse nicht schlau, schreib du mir
doch einfach eine: http://www.dbruhn.de dort steht meine
eMail-Adresse.

Gruß
TO

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.