www.mikrocontroller.net

Forum: Compiler & IDEs Zeitgleich Ausgänge setzen (AtMega8515)


Autor: Finn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich habe folgendes Priblem:
Ich habe zwei Programme in jeweils einer Schleife. In diesen Programm 
wird immer eine Variable hochgezählt. Und vergleicht. Z.B.:
Wenn Wert zwischen 100 und 200 -> LED an.

Da die Programme unterschiedlich lang sind, müsste ich für jedes 
Programm mit unterschiedlichen An-Aus-Logarythmen die Zeiten anpassen.

Deshalb wäre das mit einem Timer doch einfacher.
Die Takte für die Einzeit liegen bei ca. 2ms. Aus bei ca. 10ms.

Wenn ich jetzt den 8 Bit Timer nehmen. Bei 1Mhz / 1024 = ca. 2ms
Ich könnte doch dann den Wert des Timers auslesen, indem ich das 
Register Abfrage:
Wenn "Timer-Wert-Speicher" = 1 und 2 also 2ms und 4 ms = LED an.
Wenn dann alle Schritte durchgelaufen sind, setze ich den Timer zurück.

Oder gibt es noch eine andere Variante?

Vielen dank für eure Hilfe!

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
z.B. Der Timer ruft alle 1 ms eine Interruptroutine auf und zählt einen 
Zähler bis z.B. 10.

In 2 globalen Variablen stehen die Werte für ein- und aus.
Die ISR vergleicht diese Werte mit dem Zählerstand und schaltet die LEDs 
ein und aus.
Das Hauptprogramm braucht dann nur noch die globalen Variablen über die 
Zeit anpassen.

Autor: Finn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das könnte ich doch über: CTC Clear Timer on Compare Match (Auto Reload) 
machen oder?
Ich lasse den Timer einfach jede ms überlaufen. Dadurch wird dann der 
Interrupt ausgelöst.
Richtig?

Nur das mit den 2 Variablen und alles darunter habe ich nicht 
verstanden. Welche Werte meinst du genau?

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Finn schrieb:
> Nur das mit den 2 Variablen und alles darunter habe ich nicht
> verstanden. Welche Werte meinst du genau?

Habe ich wahrscheinlich falsch verstanden. Ich dachte, Deine 2 
Programmteile schalten 2 unterschiedliche Leds.

Autor: Finn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ne, ich hab es zu kurz erklärt.
Ich habe zwei Leds. Mehr nicht.
Mit Programm meine ich eine if-Schleife.
Programm 1 soll die Leds in einer anderen Reihenfolge schalten lassen. 
Dabei habe ich drei Zeiten:
t ein
t aus-kurz
t aus- lang
Diesen festen Zeiten müssen nur global in jedem Programm verfügbar sein.

Autor: Finn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich habe mal ein bisschen was ausprobiert.
Die unnötigen Zeilen sind auskommentiert.

Hier Code und Fehler:
/* Auto-LED-Blitzer
Version: 0.1
Datum: 027.02.2009
Autor: Finn Schürmann
Target: ATMega 8515
*/


#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <inttypes.h>

int main (void)
{
  DDRA = 0b11111111;    // Port A auf Ausgang
  PORTA = 0b00000000;    // Port A aus
  
  // Timer0 initialisieren
  TCCR0 |=  (1<<WGM01);  //  Hier wird der Timer  
  TCCR0 &= ~(1<<WGM00);  //  auf CTC-Modus gestellt

  TCCR0 |=  (1<<CS02);  //  Prescaler auf clock / 1024
  TCCR0 &= ~(1<<CS01);  //  1.000.000 / 1024 = 976,56 Hz
  TCCR0 |=  (1<<CS00);  //  976,56 Hz / 1000 = 0,98 ms

  OCR0 = 2;        //  Timer zählt bis 2 = 2ms

  TIMSK |=  (1<<OCIE0);  //  Interrupt für Compare Match erlauben

  

  sei();          //  Globale Interrupts aktivieren

      
  // Variablen definieren
  unsigned char programm=1;
//  unsigned char letzterzustand;
  int flash_step_counter = 0;

  // Programm Start
  while (1)
  {


/*    // Programm weiter
    if ( letzterzustand == 0 )
    {
      if ( (PINB & (1<<PB0) ))
      {
        letzterzustand = 1;
        programm++;
      }
    }

    if ( letzterzustand == 1 )
    {
      if ( ! (PINB & (1<<PB0) ))
      {
        letzterzustand = 0;
        programm++;
      }
    }

    // Programm-Begrenzung
    if ( programm > 4 )
    {
      programm = 1;
    }                
*/

    if( flash_step_counter == 100 )
    {  
      flash_step_counter = 0;
    }
    
    ISR ( TIMER0_COMP_vect )
    {
      flash_step_counter++;
    }
    
    
    
    // Programm 1
    if( programm == 1 )
    {
      if( flash_step_counter < 50 )
      {
        PORTA |=  (1<<PA0);
        PORTA |=  (1<<PA2);
      }
      else
      {
        PORTA &= ~(1<<PA0);
        PORTA &= ~(1<<PA2);
      }  
    } 
}
}
Build started 17.2.2011 at 21:13:05
avr-gcc  -mmcu=atmega128 -Wall -gdwarf-2 -Os -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT Auto-LED-Blitzer.o -MF dep/Auto-LED-Blitzer.o.d  -c  ../Auto-LED-Blitzer.c
../Auto-LED-Blitzer.c: In function 'main':
../Auto-LED-Blitzer.c:80: error: static declaration of '__vector_15' follows non-static declaration
../Auto-LED-Blitzer.c:80: error: previous declaration of '__vector_15' was here
make: *** [Auto-LED-Blitzer.o] Error 1
Build failed with 2 errors and 0 warnings...

Wenn ich im Datenblatt nachsehe, ist aber der Vector 15 Timer/Counter0 
Compare Match. Also eigentlich richtig.

Ist der sonstige Aufbau denn richtig?
Liebe Grüße und vielen Dank,
Finn

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eine ISR ist eine Funktion für sich, so wie jede andere Funktion auch.

In C kann man keine Funktionen ineinanderschachteln.

Anstelle von

int main()
{
  ....

  ISR( ... )
  {
    ...
  }

  ....
}

so rum

ISR( ... )
{
 ...
}


int main()
{
   ....
}

so wie mit jeder anderen Funktion auch.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
 int flash_step_counter = 0;

das muss eine volatile Variable sein.
Und int ist hier nicht so günstig. Denn dann musst du selber die 
Zugriffe atomar machen. Wenn eine Variable sowieso nie den Bereich 0 bis 
255 verlassen wird, dann nimm einen uint8_t dafür. Dann brauchst du auf 
atomare Zugriffe nicht achten
volatile uint8_t flash_step_counter = 0;

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du das Umschalten der LED nicht viel Arbeit macht, kann man es ruhig 
auch in der ISR machen.

Autor: Finn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi, danke für die Hilfe.
So sieht jetzt mein Kopf aus.
#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <inttypes.h>

  volatile uint8_t flash_step_counter = 0;
    
  ISR ( TIMER0_COMP_vect )
  {
    flash_step_counter++;
  }

int main (void)
{ 
      .....


Habe beim komiplieren dann auch keine Fehler.
Die flash_step_counter ist jetzt im ganzen Programm erreichbar, weil sie 
volatile und nicht nur in einer Funktion deklariert wird. (Genau wie bei 
z.B. if-Abfragen) richtig?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Finn schrieb:

> Die flash_step_counter ist jetzt im ganzen Programm erreichbar, weil sie
> volatile und nicht nur in einer Funktion deklariert wird. (Genau wie bei
> z.B. if-Abfragen) richtig?


Falsch.
Die Variable ist deswegen im ganzen Programm erreichbar, weil sie global 
(ausserhalb einer Funktion) definiert wurde.

Das volatile sagt dem Compiler nur, dass er sich mit Optimierungen auf 
dieser Variablen zurückhalten soll und keinen Zugriff wegoptimieren 
darf.

Autor: Finn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
ich bin es nochmal.
Heute ist nicht mein Tag.
/* 
Version: 0.1
Datum: 027.02.2009
Autor: Finn Schürmann
Target: ATMega 8515
*/


#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <inttypes.h>

  
volatile unsigned int flash_step_counter = 0;

ISR ( TIMER0_COMP_vect )
{
  flash_step_counter++;
}

int main (void)
{
  DDRA = 0b11111111;    // Port A auf Ausgang
  PORTA = 0b00000000;    // Port A aus
  
  // Timer0 initialisieren
  TCCR0 |=  (1<<WGM01);  //  Hier wird der Timer  
  TCCR0 &= ~(1<<WGM00);  //  auf CTC-Modus gestellt

  TCCR0 |=  (1<<CS02);  //  Prescaler auf clock / 1024
  TCCR0 &= ~(1<<CS01);  //  1.000.000 / 1024 = 976,56 Hz
  TCCR0 |=  (1<<CS00);  //  976,56 Hz / 1000 = 0,98 ms

  OCR0 = 2;        //  Timer zählt bis 2 = 2ms

  TIMSK |=  (1<<OCIE0);  //  Interrupt für Compare Match erlauben

            //  Globale Interrupts aktivieren

  unsigned char programm = 1;
  unsigned char letzterzustand;

  // Programm Start
  while (1)
  {


/*    // Programm weiter
    if ( letzterzustand == 0 )
    {
      if ( (PINB & (1<<PB0) ))
      {
        letzterzustand = 1;
        programm++;
      }
    }

    if ( letzterzustand == 1 )
    {
      if ( ! (PINB & (1<<PB0) ))
      {
        letzterzustand = 0;
        programm++;
      }
    }

    // Programm-Begrenzung
    if ( programm > 4 )
    {
      programm = 1;
    }                
*/

    if( flash_step_counter > 500 )
    {  
      flash_step_counter = 0;
    }
  

    // Programm 1
    if( programm == 1 )
    {

      if( flash_step_counter < 250 )
      {
        PORTA |=  (1<<PA0);
        PORTA |=  (1<<PA2);
      }
      if(flash_step_counter > 250 )
      {
        PORTA &= ~(1<<PA0);
        PORTA &= ~(1<<PA2);
      }
        
    }  

  }
}
- Alle zwei Millisekunden läuft der Timer über.
1.000.000 / 1024 = 976,6 Hz / 1000 = 0,98ms -> Timer nach ca. 2ms voll 
-> ISR wird aufgerufen => 2ms = flash_step_counter +1

Das bedeutet dass nach 1 s die Periode von vorne anfängt. 500 Steps * 
2ms = 1000ms -> 1s.
Die hälfte dieser Zeit (Step 250 *2ms = 500ms) soll PA0 und PA2 an sein. 
Die andere halbe s aus.
Die LEDs müssten also im 1Hz Takt blinken, richtig?
Leider leuchten die LEDs nur in voller Helligkeit vor sich hin.

Schonmal 1000 dank!!
Liebe Grüße,
Finn

Autor: MWS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
SEI() fehlt.

Außerdem könnt' es zu lästigen Nebeneffekten kommen, da Tests eines 
Integers bei 8bittern nicht atomar sind und von der ISR jederzeit 
unterbrochen werden können. Evtl. an den Leds nicht zu sehen, weil zu 
schnell für's Auge, jedoch vorhanden.

Autor: Finn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi, danke für die schnelle Antwort.
Sei(); kann hinter der Initialisierung des Timers richtig?
Ich habe int genommen, da ich sonst ja nur 255 * 2ms = 510ms Dauer in 
der Variable erfassen kann.
Sind denn meine Rechnungen richtig?
Das Programm sollte also theoretisch funktionieren, wenn sei(); 
eingefügt und die Variable angepasst wurde.

Vielen dank und gute Nacht !
Finn

Autor: MWS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Zeit wird nicht stimmen, es gibt eine Formel im Datenblatt für CTC 
die auch hier zutrifft, der Teiler ist dabei OCRx + 1.
Es würde sich anbieten den Prescaler kleiner zu machen, dafür OCR0 
größer.
SEI() muss irgendwann nach der Init kommen und vor while. Bereits nach 
Hinzufügen von SEI() wird das Programm funktionieren, außer ich überseh' 
etwas. Aufgrund der Kürze des Codes würde man den Led-Teil komplett in 
die ISR setzen, dann gibt's auch keine Probleme wegen des atomaren 
Zugriffs. Ansonsten kann man atomaren Zugriff durch Kapselung mit 
CLI()/SEI() erzwingen, oder durch Sperren/Freigabe des spezifischen 
Interrupts.

Autor: Finn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So langsam bin ich am verzweifeln. Die LEDs sind nur am leuchten.

Das kann doch nicht so schwer sein. Ist es möglich, dass der Timer im 
PIC defekt ist?

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Finn (Gast)

>So langsam bin ich am verzweifeln. Die LEDs sind nur am leuchten.

Ruhig Blut.

>Das kann doch nicht so schwer sein.

Ist es auch nicht.

> Ist es möglich, dass der Timer im PIC defekt ist?

Äußerst unwahrscheinlich. Praktisch unmöglich.

MfG
Falk

Autor: Finn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So,
ich habs alles hingeschmissen und nochmal von vorne angefangen.

#define F_CPU 1000000UL


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>



int main (void)
{

DDRA = 0b11111111;
PORTA = 0b00000000;

//  Timer 0 initialisieren

sei();

TIMSK |=  (1<<OCIE0);

OCR0 = 128+1;

TCCR0 |=  (1<<WGM01);
TCCR0 &= ~(1<<WGM00);

TCCR0 &= ~(1<<CS02);
TCCR0 |=  (1<<CS01);
TCCR0 |=  (1<<CS00);




    
  
}


ISR (TIMER0_COMP_vect)
{

  PORTA = 0b11111111;

  _delay_ms(500);

  PORTA = 0b00000000;
}
Berechnung:
1000000 / 64 = 15625 / 1000 = 15,625ms
=> Alle 15,625ms zählt TCNT0 + 1.

2000ms / 15,625ms = 128 => Wenn der Zähler auf TCNT0 = 128 steht, sind 2 
Sekunden vergangen.

Der Interrupt kommt also alle 2ms. Die LEDs an PORTA schalten an und 
alles wird für 0,5s lahm gelegt => bleiben also leuchten. (Ich weiß, 
delay() ist nicht schön -> aber schnell ;-)   )
Danach wieder aus und das gleiche nach den nächsten Zwei sekunden.

So die Theorie!

Praxis:
Alle Register werden passend gesetzt.

Liebe Grüße,
Finn

Autor: Finn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mir ist gerade etwas aufgefallen.
Wenn ich die Stromversorgung entferne und wieder reinstecke, leuchten 
die LEDs ca. 1 Sekunde und gehen dann einmal für ca. 1/4 Sekunde aus. 
Jedenfalls sehr kurz.

Autor: MWS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wo ist den while (1){} abgeblieben ;-)

Außerdem ist ein Delay in der ISR ein No-Go, denn es wird immer ein 
Comp-Interrupt anstehen, da diese schneller ausgelöst werden, als die 
ISR fertig ist.

D.h. nach Reset des PortA wird die ISR SOFORT wieder ausgeführt und 
PortA wieder gesetzt, leuchtet also immer.
Nimm das Delay raus und toggle den Port.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.