mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik C 'Zeitsteuerung', µS - 24h. Bitte mal ansehen


Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bewässerungsautomätchen:
Beitrag "Blumen gießen"

Ich hab's bis jetzt soweit, daß der Servo periodisch hin- und herfährt. 
Leider ist der Ansatz etwas verwurschtelt, das Ergebnis ist 
unregelmäßig.
Außerdem vermute ich, daß eine solche Steuerung wesentlich 
übersichtlicher programmiert werden kann - ich kann's leider noch nicht.
Deswegen freue ich mich über Verbesserungsvorschläge!

Eigentlich wär ja die Steuerung als Reihe von Funktionen gut zu 
gebrauchen..

servo(position)
nichtstun(3 minuten)
servo(position)
nichtstun(24 stunden)

aber wie, mit nur einem timer?

Man muß in jedem Fall zwischen einem gut anpaßbaren Programm und der 
sparsamen, exakt zugeschnittenen Lösung abwägen.
Der Servo braucht ja eigentlich nur 2 Signallängen, aber es ist 
natürlich praktisch, wenn man die Zeiten gut anpassen kann. Aber mit 
welcher Auflösung? Volle 8 Bit werden vermutlich sowieso nicht zu 
erreichen sein.

Desweiteren weiß ich nicht, wie man verhindert, daß das Umschalten 
zwischen den Timer-Interrupts, Prescalern, etc. in allgemeinem Chaos 
endet.
Wahrscheinlich läßt sich auch die ISR entschlacken, aber ich sehe nicht 
so ganz wie.

Naja, hier mal der Code:

// Include files
  #include <avr/io.h>       // Device-specific IO definitions
  #include <stdint.h>        // Standard Integer Types
  #include <stdlib.h>        // General utilities
  #include <avr/sleep.h>      // Power Management and Sleep Modes
  #include <avr/interrupt.h>    // Interrupts


// CPU Frequency
  // Internal Oscillators
  //  128KHz
  //  4.8MHz
  //  9.6MHz
  //Can be divided by 8 with CKSEL fuse or 1,2,4..256 System Clock Prescaler Register
  
  // F_CPU defined in Makefile

// System Clock Prescaler
  //   CLKPS3 CLKPS2 CLKPS1 CLKPS0 Clock Division Factor
  // T0
    #define SCP_2                        1 << CLKPS0
    #define SCP_4                 1 << CLKPS1
    #define SCP_8                 1 << CLKPS1 | 1 << CLKPS0 
    #define SCP_16          1 << CLKPS2
    #define SCP_32          1 << CLKPS2 |         1 << CLKPS0
    #define SCP_64          1 << CLKPS2 | 1 << CLKPS1
    #define SCP_128          1 << CLKPS2 | 1 << CLKPS1 | 1 << CLKPS0
    #define SCP_256  1 << CLKPS3  


  
// Timer/Counter Clock Select from Prescaler
  // T0
    #define T0_CLK_1              1 << CS00
    #define T0_CLK_8        1 << CS01
    #define T0_CLK_64        1 << CS01 | 1 << CS00
    #define T0_CLK_256  1 << CS02
  
  // Results: 
  // 1: 4,800,000 Hz - 0.2 µs
  // 8: 600,000 Hz - 1.6 µs  
  // 64: 75,000 Hz - 13.3 µs  
  // 256: 18,750 Hz - 53.3 µs

// Hardware defines
  // Servo
  #define SERVO PB4
  #define SERVO_POS_A 60 //closed
  #define SERVO_POS_B 2 //open


// GLOBAL VARIABLES

uint8_t volatile servo = SERVO_POS_B;
uint8_t counts = 0;
uint8_t signals=0;

uint8_t clock=0;


// FUNCTIONS & PROCEDURES

void slow(void)
{
  // Prescales the System Clock
  CLKPR = 1 << CLKPCE;
  CLKPR = SCP_256;
  
  // set Timer/Counter
  TCCR0B = T0_CLK_256;
  // Interrupt
  TIMSK0 |= 1 << TOIE0;

}


void fast(void)
{
  // Prescales the System Clock
  CLKPR = 1 << CLKPCE;
  CLKPR = 0;
  
  // set Timer/Counter
  TCCR0B = T0_CLK_256;
  // Interrupt
  TIMSK0 |= 1 << OCIE0A;
}



// INTERRUPT SERVICE ROUTINES

ISR(TIM0_COMPA_vect)
{ 

  OCR0A=TCNT0+4;
  
  if (TCCR0B == T0_CLK_256)
  {
    PORTB |= ( 1<<SERVO );
    TCCR0B  = T0_CLK_64;
    OCR0A=TCNT0+45;      // about 60µs
  }
  else
  {
    TCCR0B  = T0_CLK_8;
    counts++;
    
    if (servo == counts)
    {
        PORTB &= ~( 1<<SERVO );
      
      counts = 0;
      
      // next time in 13ms
      TCCR0B = T0_CLK_256;
      OCR0A = TCNT0+255;
      
      
      
      if(signals == 76) // about 1s
      {
        signals=0;
        slow();
        if(servo == SERVO_POS_B)
        {
          servo = SERVO_POS_A;
        }
        else
        {
          servo = SERVO_POS_B;
        }
        
      }
      else
      {
        signals++;
      }
    }
  }
}


ISR(TIM0_OVF_vect)
{ 
  clock++;
  if(clock == 4) // 14s
  {
    clock=0;
    fast();
  }
}






int main( void )
{

// Variables





// PORT SETUP
  // writing to PORTx before setting the DDRx is important to guarantee intended power-up pin states
  // inputs are active-low (pulled to GND when switches are closed)
  // enable internal pull-up resistors for inputs, outputs 'low'
  PORTB = 0;
  
  // define outputs in data direction register
  DDRB = 1<<SERVO;

// TIMER  
  fast();

// SLEEP MODE
  set_sleep_mode(SLEEP_MODE_IDLE);
  
// INTERRUPTS
  sei();
  
  
// // // // // // // // // // // // // //
  
  
  
  for (;;)  // ever
  {
    sleep_mode();
  }
}

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mist, das sollte doch in gcc!?

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn man den Systemtakt halbiert, hat man beim Timer mit Prescaler 256 
eine Auflösung von 106.6µs pro bit. Das würde ausreichen, um das 
Servosignal direkt per PWM zu erzeugen, aber dann hat man bestenfalls 10 
Positionen / pro Bit 18° Auslenkung.

Periode ist dann 27.3ms.

Aber spart das soviel Code? Wichtiger ist eigentlich die übersichtliche 
Struktur des Programms.

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

Bewertung
0 lesenswert
nicht lesenswert
> Desweiteren weiß ich nicht, wie man verhindert, daß das Umschalten
> zwischen den Timer-Interrupts, Prescalern, etc. in allgemeinem Chaos
> endet.

Indem man das einfach nicht macht.
Einige dich mit dir selbst wie du den Timer einstellst und
belasse es dabei.
Der Timer gibt dir eine Zeitbasis und von der leitest du in
der ISR alles weitere ab.

Wenn dein Timer zb alle 10µs einen Interrupt auslöst,
dann ist es leicht in der ISR daraus eine Uhr zu bauen,
indem die 10µs aufsummiert werden. Wenn du zusätzlich
ein periodisches Signal alle 1 ms brauchst, dann baust du
noch einen zusätzlichen Zähler mit ein, der bei jedem
ISR Aufruf um 1 weiterzählt, bis 100 erreicht sind. Hat
der Zähler 100 erreicht, dann sind seit Beginn der Zählerei
auf diesem Zähler 1 ms vergangen.

Welchen µC benutzt du eigentlich, dass du nur einen Timer
zur Verfügung hast?

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke erstmal für Deine Antwort. Ich benutze einen tiny13 ( 
http://atmel.com/dyn/products/product_card.asp?part_id=3175 )

Wenn ich aber so eine große Zeitspanne abdecken will, also 24 Stunden in 
10µS -Schritten, brauche ich aber 'viele' Variablen.
Außerdem, wenn das Timing in Software gemacht wird, kann ich um den 
Faktor >256 weniger oft einen Sleep-Mode verwenden (Batteriebetrieb).

Aber vielleicht ist der Mittelweg der richtige: Für die 24h Wartezeit 
heruntertakten, und sonst eine einheitliche Zeitbasis?

Hast Du vielleicht auch einen Vorschlag, wie die Ablaufsteuerung, die ja 
eigentlich jetzt nur in der ISR stattfindet, übersichtlich nach main() 
verlagert werden könnte?

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

Bewertung
0 lesenswert
nicht lesenswert
Koko Lores wrote:
> Danke erstmal für Deine Antwort. Ich benutze einen tiny13 (
> http://atmel.com/dyn/products/product_card.asp?part_id=3175 )
>
> Wenn ich aber so eine große Zeitspanne abdecken will, also 24 Stunden in
> 10µS -Schritten, brauche ich aber 'viele' Variablen.

Halb so wild.
Eine Variable zählt bis 100   -> ms
1000 ms ergeben 1 Sekunde
60 Sekunden sind 1 Minute
60 Minuten sind 1 Stunde
24 Stunden sind 1 Tag

Macht insgesammt, (Moment muss zählen) 5 Variablen.

> Außerdem, wenn das Timing in Software gemacht wird, kann ich um den
> Faktor >256 weniger oft einen Sleep-Mode verwenden (Batteriebetrieb).

Da musst du dich entscheiden, was denn dein Basistakt sein
soll. Ich hab 10µs genommen um mal irgendeine Zahl ins Spiel
zu bringen. Die Frage ist: welches muss dein kleinster
Basistakt sein. Ich denke mal, den wird dir das Servo vorgeben
und da hängt es wieder davon ab, wieviele Servostellungen du
erreichen willst. Mit 10µs kannst du ca. 100 Servopositionen
erreichen. Wenn dir 50 auch reichen dann peile mal 20µs an.
Welchen Wert du dann tatsächlich nimmst, hängt auch davon
ab mit welchem Takt du den µC betreibst und welchen Vorteiler
du nimmst.
Du hast eine Wunschvorstellung der kleinsten Zeiteinheit.
Dann probierst du mal, mit welcher Vorteilereinstellung
du diesem Wunsch nahe kommst. Daraus ergbt sich dann das
Ist-Basistiming. Und mit dem rechnest du dann weiter.

> Batteriebetrieb
Die meiste Zeit wird in der ISR nicht viel passieren.
Der µC legt sich also gleich wieder schlafen, nachdem
er die Zeit-Buchhaltung erledigt hat.

Ausserdem: Welche Batterie willst du nehmen? Wenn dein
Servo einen Schlauch abquetscht, dann hat es eine 2000mAH
in ein paar Stunden ausgelutscht. (Das ist das was mir persönlich
an dieser Lösung nicht gefällt: Das Servo muss ständig arbeiten)

>
> Aber vielleicht ist der Mittelweg der richtige: Für die 24h Wartezeit
> heruntertakten, und sonst eine einheitliche Zeitbasis?
>
> Hast Du vielleicht auch einen Vorschlag, wie die Ablaufsteuerung, die ja
> eigentlich jetzt nur in der ISR stattfindet, übersichtlich nach main()
> verlagert werden könnte?

Lass sie doch in der ISR.
Wenn dein Basistakt nicht allzu schnell ist, dann hast du massenhaft
Zeit in der ISR. Ob die jetzt in der ISR ist oder in main(): Es
ist immer die gleiche Ablaufsteuerung und damit gleich übersichtlich.

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank, jetzt bin ich wieder motiviert, es weiter zu probieren!

Der Servo betätigt ein Ventil, zum Abquetschen habe ich keinen passenden 
Schlauch gefunden. Aber auch das Abquetschen mit Exzenter braucht nur 
beim Verstellen Energie.
Grob gerechnet:

Servo
1s * 350mA = 350mAs = 0.1mAh

µC
5m * 3mA = 900mAs = 0.25mAh
24h * 0.7mA = 16.8mAh


-> 1 Tag ~ 20mAh, eher weniger

also mehr als 100 Tage gießen, dem 3-Monats-Urlaub steht nichts mehr im 
Wege.. Ich hoffe, man kann für den Stand-By noch kleinere Werte 
annehmen, das ist ja erstaunlich viel!

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

Bewertung
0 lesenswert
nicht lesenswert
Koko Lores wrote:
> Vielen Dank, jetzt bin ich wieder motiviert, es weiter zu probieren!
>
> Der Servo betätigt ein Ventil, zum Abquetschen habe ich keinen passenden
> Schlauch gefunden.

Ah richtig. Du hast ja ein Ventil umgebaut.

> Servo
> 1s * 350mA = 350mAs = 0.1mAh
>
> µC
> 5m * 3mA = 900mAs = 0.25mAh
> 24h * 0.7mA = 16.8mAh
>
>
> -> 1 Tag ~ 20mAh, eher weniger
>
> also mehr als 100 Tage gießen, dem 3-Monats-Urlaub steht nichts mehr im
> Wege.. Ich hoffe, man kann für den Stand-By noch kleinere Werte
> annehmen, das ist ja erstaunlich viel!

Mal blöd gefragt: Warum willst du das Teil nicht an
den Netzstrom hängen? So häufig haben wir in ME nun auch
wieder keine Ausfälle. Noch eine kleine Batterie zum Buffern
der Uhr falls doch und gut ists.


Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe draußen keine Steckdose, und hätte sonst auch keine Lust auf den 
Kabelsalat - man könnte natürlich auch gut Solarzellen benutzen, 
anstelle der Batterien!

Aber so ist es sehr günstig / einfach (+ schnell nachzubauen). 3 
Batterien, Stückchen Lochrasterplatine, Tiny13, Servo, Ventil, Schlauch. 
Noch einen Kondensator, Widerstand, Transistor.

Wenn man nicht an der Hardware/Software lernen muß wie ich gerade, ist 
das in zwei Stunden aufgebaut und am Laufen. Eine nette, kleine, 
sinnvolle Bastelei. Finde ich.


Jetzt hab ich die Schn erstmal wieder voll. Das darf doch nicht wahr 
sein! Erst sieht alles vielversprechend aus, und jetzt stimmt das Timing 
anscheinend vorne und hinten nicht:
// Hardware defines
  // Servo
  #define SERVOPORT PORTB
  #define SERVOPIN PB4
  #define SERVO_POS_A 97  // closed
  #define SERVO_POS_B 30  // open
  #define SERVO_PERIOD 20 // ms
  #define SERVO_TIME 2000 // ms (16 bit)



// GLOBAL VARIABLES

volatile struct {
   unsigned servo:1;
   unsigned pulse:1;
   unsigned move:1;
} flag;

volatile uint8_t s_pulse_len = SERVO_POS_B;
volatile uint8_t s_pulse_cur = 0;

volatile uint16_t now_ms;
volatile uint16_t ms_0B;
volatile uint16_t now_ms_0B;

volatile uint16_t millisecond = 0;  // 1000
volatile uint8_t second = 0;    //   60
volatile uint8_t minute = 0;    //   60
volatile uint8_t hour = 0;      //   24



// INTERRUPT SERVICE ROUTINES

ISR(TIM0_COMPA_vect)
{ 

  OCR0A=TCNT0+1;       //13.3µs
  
  
  // servo pulse
  if(flag.pulse == 1)
  {
    
    if (s_pulse_cur == 0)
    {
      SERVOPORT |= ( 1<<SERVOPIN ); // high
      s_pulse_cur++;
      
      // timer for signal period
      if ((millisecond + SERVO_PERIOD) >= 1000)
      {
        now_ms = millisecond + SERVO_PERIOD - 1000;
      }
      else
      {
        now_ms = millisecond + SERVO_PERIOD;
      }
    }
    else if (s_pulse_cur == s_pulse_len)
    {
      SERVOPORT &= ~( 1<<SERVOPIN ); // low
      s_pulse_cur = 0;
      flag.pulse = 0;
    }
    else 
    {
      s_pulse_cur++;
    }
  }

} // ISR


ISR(TIM0_COMPB_vect)
{ 

  OCR0B=TCNT0+75;       // 1ms
  
  ms_0B++;  
  
  millisecond++;
  if(millisecond == 1000)   // 1s
  {
    millisecond = 0;
    second++;
    if(second == 60)     // 1m
    {
      second = 0;
      minute++;
      if(minute == 60)   // 1h
      {
        minute = 0;
        hour++;
        if(hour == 24)   // 1d
        {
          hour = 0;
        }
      }
    }
  }
  
  
  // servo signal generation
  if(flag.servo && millisecond == now_ms) // no good, takes up to 1 second before 'true' when beginning signal
  {
    // enable pulse generation
    flag.pulse = 1;
  }
  
  
  // activate servo once
  if(flag.move)
  {
    flag.servo = 1;
    flag.move = 0;
    
    // timer for servo signal
    now_ms_0B = ms_0B + SERVO_TIME;
  }
  else if (ms_0B == now_ms_0B)
  {
    flag.servo = 0;
  }
  
  
  
} // ISR



// MAIN

int main( void )
{

// Variables


// PORT SETUP
  // writing to PORTx before setting the DDRx is important to guarantee
  // intended power-up pin states
  // inputs are active-low (pulled to GND when switches are closed)
  // enable internal pull-up resistors for inputs, outputs 'low'
  PORTB = 0;
  
  // define outputs in data direction register
  DDRB = 1<<SERVOPIN;

// TIMER  
  /*/ Prescales the System Clock
  CLKPR = 1<<CLKPCE;
  CLKPR = 0; */
  
  // set Timer/Counter
  TCCR0B = T0_CLK_64;    // 13.3µs - 3.4133ms
  // Interrupts
  TIMSK0 |= 1<<OCIE0A | 1<<OCIE0B;

// SLEEP MODE
  set_sleep_mode(SLEEP_MODE_IDLE);
  
// INTERRUPTS
  sei();
  
  flag.move = 1;
// // // // // // // // // // // // // //
  
  
  for (;;)  // ever
  {
    sleep_mode();
  }
}

Der Puls ist jetzt 750µs lang, dabei sollte er nur etwa 30*13.33µs = 400 
µs lang sein. Die ganze Pulsfolge sollte 2 Sekunden lang sein, und liegt 
jetzt bei 900ms. Irgendwas läuft extrem schief.

Und ich habe das Gefühl, daß ich irgendwie auf dem Holzweg bin, und das 
alles viel eleganter geht.
Codegröße schon 582 bytes..:-(

Dabei erzeugt es lediglich eine Pulsfolge.. traurig.

Das liegt vermutlich auch an den vielen volatile Variablen, oder?

Ich hoffe auf weitere glückbringende Hinweise!

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe leider immer noch keine Fortschritte gemacht. Daher mal konkrete 
Fragen:

Muß ich die Variablen überhaupt alle volatile deklarieren, wenn sie in 
verschiedenen Interrupts benutzt werden?

Wenn ich eine lokale variable in einer ISR benötige, die ihren Wert bis 
zum nächsten Aufruf behält, wie wird sowas deklariert? static?

Und könnten die Timing-Probleme durch die 16 bit variablen verursacht 
werden? Andererseits wird damit ja nur im Interrupt gerechnet..

Oder braucht die ISR schon zuviel Zeit? Alle 75 Timer-Takte werden beide 
Interrupts 'gleichzeitig' ausgelöst, bringt das was durcheinander?

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Muß ich die Variablen überhaupt alle volatile deklarieren, wenn sie in
> verschiedenen Interrupts benutzt werden?

Du mußt sie dann als volatile deklarieren, wenn sie von einer ISR und 
dem Hauptprogramm zugegriffen werden.

> Wenn ich eine lokale variable in einer ISR benötige, die ihren Wert bis
> zum nächsten Aufruf behält, wie wird sowas deklariert? static?

Das ist eine Möglichkeit. Man nutzt si normalerweise, wenn die Variable 
ausschließlich der ISR (oder auch einer normalen Funktion) bekann sein 
soll.

Eine andere Möglichkeit ist, eine globale Variable zu nehmen.

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Du mußt sie dann als volatile deklarieren, wenn sie von einer ISR *und*
>dem Hauptprogramm zugegriffen werden.

= mindestens einer ISR und dem Hauptprogramm?
Aber was ist bei zwei ISR ohne Hauptprogramm?

Könntest Du so gut sein, und Dich auch der anderen Probleme kurz 
annehmen? Ich wäre Dir ausserordentlich dankbar - so ein kleines 
Progrämmchen, und ich bekomme es nicht hin, weil ich die Probleme nicht 
sehe.

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> = mindestens einer ISR und dem Hauptprogramm?
> Aber was ist bei zwei ISR ohne Hauptprogramm?

Da eine ISR die andere nicht unterbrechen kann auf so einem kleinen 
Ding, müssen sie nicht volatile sein.

Zu Deinen anderen Problemen: Ich versuchs gerade, mir ist aber nicht 
klar, was der Servo für ein Signal bekommt. Wenn Du das nochmal kurz 
beschreiben könntest, fällt es mir leichter, den inneren Schweinehund zu 
überwinden...

Zudem kenn ich den AVR nicht gut.

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ha, toll.

Es soll ungefähr das Folgende ausgegeben werden.
Servosignal:

          __
         |  |
         |  |______________________.....

High 0.6-1.3ms dann ca. 20ms Low

Die Servostellung ist durch die Dauer des Impulses bestimmt, 
normalerweise 1-2ms, mit 1.5ms = Mittelstellung. Die Zeiten, die ich 
versuche stehen unter dem letzten Code.

Danke!

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also handelt es sich um PWM.

Sehe ich das richtig, daß Dein µC PWM hardwaremäßig kann? Dann solltest 
Du das auch so manchen.

Du brauchst dann nur noch eine ISR, die mit dem Ablauf eines vollen 
Zyklus (also high- + low-Output zusammen) aufgerufen wird.

Die zählt dann Deine Softwareuhr hoch und prüft, ob eine Aktion 
auszuführen ist. Wenn der Servo verstellt werden muß, wird nur das 
Timerregister verändert, das die Länge des Pulses bestimmt.

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hatte auch über Hardware-PWM nachgedacht, aber ich glaube, daß es 
nicht besser ist: Der tiny13 hat nämlich nur den einen Timer/Counter, 
und wenn ich den auf PWM einstelle, habe ich eine schlechtere Auflösung, 
da ja die 20ms mit 8 Bit aufgelöst werden müssen. Und derselbe Prescaler 
muss dann meine Zeitbasis schaffen - das geht nicht auf. Für die 
benötigten 20 Perioden lohnt sich das eigentlich auch nicht, es gibt ja 
sonst nichts zu berechnen.

Oder übersehe ich etwas?

Es gilt herauszufinden, warum das Timing so schief läuft. Gibt es 
irgendwo eine Anleitung zum Thema 'Debuggen' mit Debugger? Wenn ich 
irgendwo sehen könnte, wie lange die ISR braucht, könnte ich zumindest 
feststellen, ob sie zu lange braucht.
Oder soll ich mal einen Pin für die Dauer der ISR einschalten? Könnte 
ich messen..

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die maximale Auflösung der PWM wird durch die Auflösung des Timers 
gegeben. Für Deinen Servo dürfte es keine große Rolle spielen, ob die 
Zykluslänge 20 µs, oder 50 ist - solange die Frequenz viel höher ist, 
als der Tiefpaß am Eingang des Servos - sehe ich das richtig?

Du kannst also den Zyklus des Timers so einstellen, wie es am besten 
paßt.

Der Software-Timer für Deine Ereignisplanung ist jedenfalls das kleinste 
Problem: Du mußt ja nicht in hh:mm:ss:msn rechnen - es müßte doch 
reichen, infach einen Sekundenzähler hochzuzählen, der bei 86400 wieder 
auf 0 gesetzt wird.

Den Sekundentakt bastelst Du Dir durch abzählen der Zyklen Deiner PWM.

Nachdem ich etwas gerechnet habe: Die 8-Bit Auflösung müßte für Deinen 
Servo eigentlich ausreichen: Das entspricht knapp 0,4% pro Schritt.

Ich habe sowas ähnliches vor einiger Zeit mit einem MSP430F2011 gemacht 
- nachdem ich mit Software-PWM auf dem Bauch gelandet war. Das hat auf 
Anhieb wunderbar funktioniert.

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich verstehe Dich nicht..oder Du mich nicht..oder beides?

Das Servosignal ist ein Impuls mit (in meinem Fall) 500-2500µs = 
0.5-2.5ms. Diese Impulslänge ist entscheidend, wie der Servo das intern 
mit dem Ist-Stand vergleicht, weiß ich nicht. Der Impuls wird dann etwa 
alle 20ms wiederholt (unkritisch).

Mit PWM muss der Timer also eine Periode von etwa 20ms abdecken. Ein 
Wert eines 8-Bit Timers entspricht dabei einer Dauer von 78µs (wenn man 
den Takt einstellen kann - ansonsten kommt man nicht auf die volle 
Auflösung). D.h. ich habe für die 2ms On-Zeit etwa 25 Schritte.

Meinstest Du das?

Könnte man probieren, spart vielleicht auch ein bißchen Platz im Flash - 
aber dann weiß ich immer noch nicht, warum der obige Code Mist baut.

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Das Servosignal ist ein Impuls mit (in meinem Fall) 500-2500µs =
> 0.5-2.5ms. Diese Impulslänge ist entscheidend, wie der Servo das intern
> mit dem Ist-Stand vergleicht, weiß ich nicht. Der Impuls wird dann etwa
> alle 20ms wiederholt (unkritisch).

OK, das war mir als Modellbaubanausen nicht klar.

Trotzdem sollte sich die Sache mit 8-Bit-PWM erledigen lassen:

- Zykluszeit 2,5 ms
- Auflösung besser als 0,4%
- Wenn die Zykluszeit abgelaufen ist, wird der PWM-Output abgeschaltet, 
der Timer läuft so weiter, wie er eingestellt ist.
- 7 Zyklen ohne PWM-Ausgabe
- 1 Zyklus mit PWM-Ausgabe
usw.

Das Ganze geht mit einer ISR.

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke, wenn ich es anders nicht hinbekomme versuch ich's mal so! Aber 
erstmal gucken, was schiefläuft...

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das bei Deinem Programm ist ein Design-Problem - mein Job ist u.a. 
ordentlicher Softwareentwurf und mein innerer Schweinehund ist in der 
Freizeit nicht dazu zu bewegen, sich mit schrägem Design 
auseinanderzusetzen. Also nicht böse sein.

Man sagt zwar so leichtsinnig, dem Inschinjör sei nicht zu zu schwör, 
aber das stimmt nicht. Aus zerknitterten Komonenten kann er keine 
Hochglanzlösung machen... und wenn er könnte, fehlt der Ehrgeiz.

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie sollte man in einem Forum böse sein? Entweder man freut sich über 
'gute' Beiträge, oder es sind keine Beiträge da.

Was mich jetzt aber interessiert, ist, ob Du den Versuch, die 
Signalerzeugung in Software zu erledigen als Design-Problem ansiehst, 
oder etwas anderes. Wenn es noch was anderes ist, würde ich natürlich 
gerne wissen, um was es da geht, damit ich es ordentlich entwerfen kann. 
Oder aber - wie würdest Du die Aufgabe lösen, wenn kein Hardware-PWM 
möglich wäre?
Daß das Gewusel mit den Flags etc. nicht elegant ist, fürchte ich ja 
auch, aber mir ist leider keine bessere Idee gekommen..

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit welchem Takt läuft denn der µC? Schafft er die 1. ISR überhaupt in 
13,3 µs?

Autor: Power (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und bei den kleinen Tinys aufpassen: Hardwarestack! Maximal 3 Ebenen, 
dann wird von vorne überschrieben. Das produziert oft Mist, für mich ein 
Grund auf den Tinys Assembler zu proggen, passt mehr 'rein und man 
verheddert den Stack nicht so schnell. ;-)

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das kann bei Kokos derzeitigem Programm nicht passieren.

Jedenfalls ist das ein Grund, warum mir die MSP430F20xx deutlich besser 
gefallen...

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Er läuft mit 4.8MHz - ob er die ISR schafft, weiß ich nicht, vielleicht 
ja nicht, wenn's nicht so läuft, wie's soll..

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das sind 66,5 Takte in 13,3 µs - schätze, das wird eng... Da ja auch 
noch eine zweite ISR vorhanden ist, würde eine Softwarelösung - die man 
vermutlich wirklich am besten in ASM realisieren sollte - nicht sehr 
präzise.

Machs mit Hardware-PWM - das ist einfacher, genauer und belastet das 
Rechenknechtchen kaum.

Der Design-Aspekt der Sache:
Erste Frage bevor eine Zeile programmiert wird: Ist das Problem per 
Software auf der gegebenen Hardware lösbar?

Autor: Power (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Erste Frage bevor eine Zeile programmiert wird:
Erst mal auf dem Konzeptblock die Programmstruktur und den geplanten 
Ablauf festhalten, dadurch schließen sich oft schon nicht machbare 
Sachen aus. Außerdem ist die Struktur hinterher wiederzuerkennen und 
durchschaubarer.
3/4 der Programmierarbeit findet auf dem Papier statt, das Umsetzen in 
Code ist dann das kleinste Problem.

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich sehe es mittlerweile ein (danke), und habe Uhus Vorschlag von weiter 
oben mal in die Tat umgesetzt. Jetzt gibt es aber wieder ein neues 
Problem:

Die Pulsweite ist nicht konstant, teilweise gibt es Phantompulse
Der Pulsabstand stimmt meistens, aber es gibt Lücken.

Ich bin mir allerdings auch nicht 100% sicher, ob ich den richtigen PWM- 
Mode ausgewählt habe.

Fast PWM Mode:
Clear OC0B on Compare Match, set OC0B at TOP

Mode WGM2 WGM1 WGM0 Mode TOP Update OCRx TOV
3     0    1     1  Fast PWM 0xFF   TOP  MAX

// set Timer/Counter / PWM
TCCR0B = T0_CLK_64;    // 13.3µs - 3.4133ms
// Interrupts
TIMSK0 |= 1<<OCIE0A;


ISR(TIM0_COMPA_vect)
{ 

  OCR0A=TCNT0+188;       //2506µs
  
  // servo pulse

  
  if(servo.cycle == 7)
  {
    // enable PWM
    TCCR0A = (1<<COM0B1) | (1<<WGM01)| (1<<WGM00); // Mode 3: Fast PWM
    OCR0B=100;
    
    servo.cycle++;
  }
  else if (servo.cycle == 8)
  {
    // disable pwm
    TCCR0A = 0;
  
    servo.cycle=0;
  }
  else
  {
    servo.cycle++;
  }

} // ISR

  

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Äh, liegt das vielleicht daran, daß der Interrupt nicht mit der PWM 
synchron ist, und diese vor Erreichen des Zielwertes ausschaltet?

Kann das mit den PWM Einstellungen behoben werden?

Autor: Power (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Probiers mal mit dem CTC-Mode (Mode 2), WGM01 im TCCR0A gesetzt. Und den 
Output-Compare0A-INT benutzen (ist das der 'ISR(TIM0_COMPA_vect)'?).

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nee, im CTC wird doch immer der Timer auf 0 gesetzt - dann kann ich den 
Output-Compare0A-INT doch nicht mehr benutzen! Oder versteh ich das 
falsch?

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also der B-Match ist jetzt auf PWM eingestellt, mit A als Interrupt 
steuert er den Ablauf..

Autor: Power (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ups, stimmt! War noch beim Timer (Uhr). Der Mode 1 (WGM00) dürfte eher 
passen!

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Die Pulsweite ist nicht konstant, teilweise gibt es Phantompulse
> Der Pulsabstand stimmt meistens, aber es gibt Lücken.

Läuft da noch die zweite ISR mit?

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für den Mode-Vorschlag, kann ich leider erst morgen ausprobieren. 
Die zweite ISR ist nicht mehr da, geht auch gar nicht, weil es nur zwei 
Compare-Units gibt.. Ach so, overflow ginge ja auch noch.
Also, wie synchronisieren? Vielleicht fällt mir morgen was ein..

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So ist es auch nicht synchron. Hätte mich irgendwie auch gewundert.

                           _________________________
___________________________|           PWM COMPB     |________________


       __________________________________________
_______|                 2500µs    INT COMPA       |________________


Die überschneiden sich irgendwie. Wobei der PWM Wert mit 100 erstmal 
wesentlich kürzer ist, als der Int. Aber wahrscheinlich auch nur das 
erste, und dann alle N-mal.

Dann muß ich vielleicht auf die 2.5ms Taktung verzichten, und den 
Overflow Interrupt benutzen ? Der ist dann auf jeden Fall so lang wie 
der PWM-Zyklus.. Probier ich mal aus.

Und wie komme ich dann auf eine 'gerade' Zeiteinheit? Den anderen 
Timerinterrupt benutzen?
Oder wie meintest Du, daß ich mit einem Interrupt auskomme?

Danke schonmal, tolles Wetter!

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das klappt im Prinzip, aber ich habe ca. 1.8ms nach dem Impuls einen 
Spike, vermutlich schaltet PWM ein, und wird danach vom overflow-int 
wieder abgeschaltet. passt ja von der zeit. Irgendwelche Ideen?

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Für den Tiny und PWM kann ich Dir keine Tipps geben - hab mich mit dem 
Teil noch nie länger als 5 min. am Stück befaßt...

Zum Interrupt: Mehr als einen Sekundentakt brauchst Du zum blumengießen 
nicht. Wenn Deine PWM 2.5 ms Zykluszeit hat, dann kannst Du diesen Takt 
benutzen, um den Sekundentakt abzuleiten: Du zählst einfach alle 400 
Interrupts den Sekundenzähler um 1 hoch; wenn der 86400 erreicht, setzt 
Du ihn wieder auf 0.

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ist ja klar, aber wenn Du mit Zykluszeit den Timeroverflow meinst - 
mit der Prescaler komme ich auf 3.4ms. Auch wenn die Lösung mit ziemlich 
exakt 2.5ms  durch einen Interrupt einstellbar wäre, es klappt ja nicht. 
Und wenn ich den Overflow-Int benutze, bekomme ich die o.g. Spikes.

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, dank hannes jetzt doch wieder Servo-PWM in Software mit dem Besten 
aus beiden vorherigen Versuchen.

Ich hatte in dem Versuch 
Beitrag "Re: C 'Zeitsteuerung', µS - 24h. Bitte mal ansehen"
praktische jeden Timer-Schritt zusätzlich in einer Variablen mitgezählt, 
das ist natürlich total bescheuert...

Und dank Uhus Plan ist es sehr übersichtlich.

Das Signal steht also wieder, fehlt 'nur' noch die Steuerung.

Danke für die Hilfe!

ISR(TIM0_COMPA_vect)                                 // 4.8MHz / 64
{ 

  if(servo.cycle == 7)
  {
    SERVOPORT |= ( 1<<SERVOPIN );        // high
    servo.cycle++;
    OCR0A = TCNT0 + s_pulse_len;         // pulse-width
  }
  else if (servo.cycle == 8)
  {
    SERVOPORT &= ~( 1<<SERVOPIN );       // low
    servo.cycle = 0;
    OCR0A = TCNT0 + 188;                 // 2506µs
  }
  else
  {
    OCR0A = TCNT0 + 188;                 // 2506µs
    servo.cycle++;
  }

} // ISR

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Spikes:
Hast Du die High-Phase der PWM am Anfang, oder am Ende des PWM-Zyklus?

Wenn sie am Anfang liegt, ist wohl mit Spikes zu rechnen, weil die PWM 
mit dem Overflow die Ausgabe wieder auf High schaltet und die ISR ein 
wenig Zeit braucht, bis die PWM abgeschaltet ist. Was man dann sieht, 
ist die Interrupt-Latenzzeit + die Zeit, die die ISR braucht, bis der 
Befehl zum Sperren der PWM ausgeführt ist.

Ich hatte beim MSP430F2011 auch ein Störsignal - ich habe es 
wegbekommen, indem ich einen PWM-Modus ausgewählt habe, der die 
High-Phase nicht an den Anfang des Zyklus, sondern ans Ende legt. (Dort 
funktioniert das so: Der Timer hat zwei Register, das erste wird auf die 
Zykluszeit eingestellt - in Deinem Fall 2.5 ms - das andere bestimmt den 
Zeitpunkt innerhalb des Zyklus, an den das Ausgabesignal umschaltet, 
also <= 2.5 ms. Der Interrupt wird vom ersten Register erzeugt. Das 
Timerregister wird beim Erreichen des Zyklusendes zurückgesetzt.)

Ich mußte dann zwar den Registerwert entsprechend umrechnen, aber das 
Signal war sauber.

Zum Sekundentakt:
Für die PWM brauchst Du doch den Interrupt, um die Pause zu erzeugen - 
sehe ich das richtig?

Wenn das so ist, dann nimm den 2.5 ms Takt. Es macht keinen Sinn, nur 
wegen ein paar µs mehr den krummen Wert des Prescalers als Zeittakt zu 
benutzen - mit der Hardware-PWM hat der Hobel eh Zeit genug...

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Allerdings ist hier jeder 8. Aufruf != 2.5ms.
Also entweder ignorieren oder kompensieren, z.B. die Differenz zum 
Maximalwert 188 beim nächsten Schritt dranhängen..

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo und danke für die Rückmeldung!

Meines Wissens brauche ich den Interrupt in jedem Fall, um die Pause zu 
erreichen, weil es beim AVR - glaube ich - nicht zwei Register für die 
PWM gibt, wie Du beschrieben hast. D.h. die Timer/Counter/PWM-Auflösung 
sind 8 Bit mit dem vom Prescaler erzeugten Takt. Basta.
Da komme ich halt nicht auf einen praktischen Wert (für die Sekunde), 
sondern muß die 2.5ms mit einem Interrupt erzeugen, im Code der Wert 188 
(mal 13.33µs).

Einen anderen PWM Mode werde ich bei Gelegenheit mal ausprobieren, aber 
für die ganzen Modi und Registerbeschreibungen habe ich vorerst keine 
Geduld mehr, sondern möchte erst noch ein Stück weiterkommen. Der 
Mehr-Code gegenüber PWM ist jetzt ja nicht mehr so schwerwiegend.

Ich probier' mich erstmal wieder an der eigentlichen Steuerung..

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde (beim Tiny13 in ASM) keine Hardware-PWM für ein Servo 
benutzen.

Den Portpin für den Impuls würde ich in der Timer-OVF-ISR setzen
(sbi) wenn:
- die Impulspause (68 ISRs) abgelaufen ist
- der Transistor für die Servo-Stromversorgung eingeschaltet ist

Den Portpin für den Impuls würde ich in jeder Timer OCR-ISR löschen, 
wenn er bereits aus ist, dann bleibt er halt aus (cbi).


Die Aufgaben würde ich also so verteilen:
- OCR-ISR:
  - Impuls ausschalten (egal ob er an war)

- OVF-ISR:
  - Synchronisations-Flag für Mainloop setzen
  - Impulspausenzähler runterzählen, bei 0:
    - Impulspausenzähler auf Startwert setzen,
    - Impuls-Pin setzen, falls Servo-Stromversorgung aktiv ist

- Mainloop:
  - Synchronisations-Flag prüfen, falls nicht gesetzt, dann sleep
  - Synchronisations-Flag löschen (Job wird ja gemacht)
  - Uhr um 393µs hochzählen
  - Servo-Timeout herunterzählen und ggf Servo-Strom ausschalten
  - Uhrzeit mit Schaltzeiten vergleichen, bei Treffer:
    - Servostellung in OCR eintragen,
    - Servo-Timeout (1/2s?) setzen,
    - Servo-Strom einschalten
  - SLEEP im Mode IDLE aktivieren

- Main:
  - Stackpointer initialisieren (in ASM)
  - Ports initialisieren
  - Variablen initialisieren
  - Timer mit Vorteiler 64 einschalten,
  - OVF-Int und OCR-Int freigeben
  - Sleep im Mode Idle vorbereiten
  - Interrupts freigeben

...

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Hannes, danke für Deinen Vorschlag!
Momentan habe ich alles in einer OCR-ISR, und Hardware-PWM benutze ich 
ja nicht.
Bis auf eine kleine Abweichung von der Zeit klappt es so sehr gut. Der 
Fehler sollte aber leicht zu finden sein.
Aber ich werd' noch mal sehen, ob ich nicht auch was in die main 
verlagern sollte.

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zu Hannes' Beitrag:
Beitrag "Re: PNP als Schalter: widersprüchlich. Aber konkret gefragt" ff.

Aber wie kommt man vom OVF alle 3.41248ms auf eine Sekunde?
Mit dem OCR alle 188 Schritte komme ich auf ziemlich genau 2.5ms..

Leider geht mir gerade irgendwie etwas Zeit verloren, wenn der Servo 
sich bewegt - und das, obwohl ich die Abweichung durch den Servo-Impuls 
kompensiere. Da wird er sich irgendwo verstecken...

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zur Kompensation des Taktes muß ein weiterer Zyklus durchlaufen werden, 
sonst kommt's nicht hin. Damit bilden dann zwei Durchläufe einen 
regulären - wenn davon eine Zeitbasis abgeleitet wird, aufpassen, und 
den zusätzlichen Durchlauf nicht mitzählen..

Damit wächst der Code allerdings wieder.. und es könnte sein, daß die 
Zeit nicht ganz reicht. Wenn das Servosignal erzeugt wird (für 2 
Sekunden) ist das Timing etwas länger. Bezogen auf die Sekunde, mit der 
probehalber PB0 getoggelt wird, einige ms.
ISR(TIM0_COMPA_vect)
{

  if(servo.on)
  {
    if(servo.cycle == 7)            // 
    {
      SERVOPORT |= ( 1<<SERVOPIN );      // high
      servo.cycle++;
      OCR0A = TCNT0 + pulse_width;      // pulse-width
    }
    else if (servo.cycle == 8)
    {
      SERVOPORT &= ~( 1<<SERVOPIN );     // low
      servo.cycle = 9;
      OCR0A = TCNT0 + CYCLE - pulse_width;  // complete the 2506µs period
      tick++;
    }
    else if (servo.cycle == 9)
    {
      OCR0A = TCNT0 + CYCLE;  // complete the 2506µs period
      tick++;
      servo.cycle = 0;
    }
    else
    {
      servo.cycle++;
      OCR0A = TCNT0 + CYCLE;          // 2.5ms
      tick++;
    }
  }
  else
  {
    OCR0A = TCNT0 + CYCLE;          // 2.5ms
    tick++;
  }
  
  if(tick == 400)            // 1s
  {
    PORTB ^= (1<<PB0);
    tick = 0;
    second++;
    
    if(second == 1)
    {
      pulse_width = SERVO_POS_B;
      SERVO_ON()
      servo.cycle = 0;
      servo.on=1;
    }
    else if(second == 3)
    {
      servo.on=0;
      SERVO_OFF()
    }
    else if(second == 10)
    {
      pulse_width = SERVO_POS_A;
      SERVO_ON()
      servo.cycle = 0;
      servo.on=1;
    }
    else if(second == 12)
    {
      servo.on=0;
      SERVO_OFF()
    }
    else if(second == 20)
    {
      second = 0;
    }
  }

} // ISR

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Koko Lores wrote:
> Zu Hannes' Beitrag:
> Beitrag "Re: PNP als Schalter: widersprüchlich. Aber konkret gefragt" ff.
>
> Aber wie kommt man vom OVF alle 3.41248ms auf eine Sekunde?

Mit den 0,393ms (weiter oben) habe ich wohl Blödsinn in den 
Taschenrechner eingetippt, sorry... Der OVF erfolgt natürlich alle 
3,41248ms.
Ein nachgeschalteter Software-Zähler erreicht die volle Sekunde nach 293 
Überlauf-Interrupts, braucht also zwei Bytes.

> Mit dem OCR alle 188 Schritte komme ich auf ziemlich genau 2.5ms..


Dann müsstest Du den Zählumfang des Timers begrenzen. Bei meinem 
Vorschlag ging ich von einem freilaufenden Timer aus.

Aus dem Hut weiß ich jetzt nicht, ob der Tiny13 im CTC-Mode (also Timer 
bei Compare löschen) noch beide Compare-Interrupts unterstützt. Wenn ja, 
dann kann man statt des OVF den einen Compare bei 2,5ms mit CTC 
zuschlagen lassen, den anderen Compare bei 1,0..2,0ms zum Beenden des 
Impulses. Auch hier brauchst Du einen nachgeschalteten Zähler in 
Software, der alle 400 Schritte eine Sekunde erreicht (also auch zwei 
Bytes). Ob das wirklich genauer ist, ist fraglich, denn der interne 
RC-Oszillator ist weder genau noch temperaturstabil, für eine ernsthafte 
"Uhr" also von vornherein ungeeignet.

> Leider geht mir gerade irgendwie etwas Zeit verloren, wenn der Servo
> sich bewegtDas kann nach meinem Konzept (extrem kurze ISRs, Synchronisierung der 
Jobs in der Mainloop mittels Jobflags, Sleep-Mode Idle, wenn alle Jobs der 
Mainloop fertig sind) eigentlich nicht passieren. Denn der Zeitzählung ist es 
egal, ob ein Impuls abgesetzt wurde oder nicht. Und da ein Impuls immer kürzer ist 
als eine Timer-Runde (Überlauf), gibt es keinerlei Kollisionen.

> - und das, obwohl ich die Abweichung durch den Servo-Impuls
> kompensiere. Da wird er sich irgendwo verstecken...

Da gibt es eigentlich nichts zu kompensieren, es sei denn, Du arbeitest 
irgendwo in einer ISR mit Warteschleifen und verpennst dadurch 
Interrupts.

...

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hannes hat sein Ding in Assembler geschrieben - damit sind die Chancen 
deutlich besser, die Software-PWM hinzubekommen, als mit C.

Zur Zeitkorrektur: Die könntest Du eigentlich auch im Hauptprogramm 
machen, wenn Du weißt, wie groß der Fehler je Sekunde ist.

Dann wäre die ISR etwas entlastet.

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Uhu Uhuhu wrote:
> Hannes hat sein Ding in Assembler geschrieben

Ja, richtig, ich denke bei Programmentwürfen auch in ASM, der AVR kann 
auch nur Maschinencode, also ASM...

> - damit sind die Chancen
> deutlich besser, die Software-PWM hinzubekommen, als mit C.

Wer C wirklich virtuos versteht kann das AUCH IN C. Allersdings kann der 
auch soviel ASM, dass er das Ergebnis seiner Arbeit nachvollziehen und 
ggf. optimieren kann.

>
> Zur Zeitkorrektur: Die könntest Du eigentlich auch im Hauptprogramm
> machen, wenn Du weißt, wie groß der Fehler je Sekunde ist.
>
> Dann wäre die ISR etwas entlastet.

Die ISR gehört unabhängig von der Sprache so kurz wie möglich. Wobei ich 
auch schon Programme schrieb, bei denen die gesamte Arbeit in der 
Timer-ISR erledigt wurde. Allerdings kann man in ASM schnell mal 
durchrechnen (durchzählen) wieviel Takte der Code benötigt.

...

Autor: Koko Lores (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke wiederum für Eure Antworten, das macht ja geradezu Spaß, so viele 
Möglichkeiten der Umsetzung kennenzulernen.

Zur Korrektur - Da ich mit einem COMP Interrupt sowohl Signallänge als 
auch Pausenlänge erzeuge, habe ich für die 'Zeitbasis' eine abweichende 
Taktzeit, die muß man korrigieren.

Aber Du hast natürlich recht - den nachgeschalteten Zähler brauche ich 
in jedem Fall, und da auch mit dem OVF recht genau 1s abzuleiten ist, 
werde ich das als nächstes versuchen.
Ich weiß nicht mehr, warum ich mich dagegen entschieden hatte.

Dann ist alles synchron, es muß nichts ausgeglichen werden, und der Code 
wird wieder kleiner. Klingt gut!

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Koko Lores wrote:
> Danke wiederum für Eure Antworten, das macht ja geradezu Spaß, so viele
> Möglichkeiten der Umsetzung kennenzulernen.
>
> Zur Korrektur - Da ich mit einem COMP Interrupt sowohl Signallänge als
> auch Pausenlänge erzeuge, habe ich für die 'Zeitbasis' eine abweichende
> Taktzeit, die muß man korrigieren.

Nunja, das ist Geiz am falschen Ende. Du hast zwar nur einen Timer, der 
kann aber mehrere verschiedene Interrupts erzeugen. Es ist also 
sinnfrei, alles in einem Interrupt machen zu wollen.

Ein zweiter Compare-Interrupt bringt vermutlich nicht mehr Genauigkeit 
als der OVF-Interrupt, denn auch der Timer-Zählumfang mit 188 ergibt 
nicht exakt 2,5ms, da bräuchte man den Zählumfang von 187,5. Da 
verursacht der Nachteiler von 293 statt 292,96875 bei Timer-Zählumfang 
von 256 schon weniger Abweichung.

>
> Aber Du hast natürlich recht - den nachgeschalteten Zähler brauche ich
> in jedem Fall, und da auch mit dem OVF recht genau 1s abzuleiten ist,
> werde ich das als nächstes versuchen.
> Ich weiß nicht mehr, warum ich mich dagegen entschieden hatte.

Ich vermute, Du drückst Dich (wie viele C-Anfänger) vor Interrupts bzw. 
dem extrem hardwarenahen Programmieren.

>
> Dann ist alles synchron, es muß nichts ausgeglichen werden, und der Code
> wird wieder kleiner. Klingt gut!

Klingt nicht nur gut, ist auch gut.

Der Überlauf-Int "taktet" die Mainloop über ein Jobflag,
der Compare-Int schaltet nur den Impuls aus (egal ob er diesmal an war),
den Rest erledigt die Mainloop:
- Zeit hochzählen und Schaltuhrfunktion ausüben
- Servostrom einschalten, wenn Schaltzeit erreicht ist
- Servoposition einstellen
- Servo-Timeout verwalten (Strom wieder aus)
- Impulspause verwalten
- Impuls einschalten wenn Servospannung anliegt und Impulspause abläuft

Ich räume ein, dass dieser hardwarenahe Programmierstil vermutlich in 
ASM überschaubarer realisiert werden kann als in C.
Im Prinzip fragt ja jeder Teil des Programms ja nur die Zustände einiger 
Variablen (Flag, Portzustand, Zählvariablen) ab und reagiert darauf 
sowie verändert die entsprechenden Variablen (hochzählen, runterzählen, 
auf Startwert setzen).

...

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.