Forum: Mikrocontroller und Digitale Elektronik Kann man mit OCR1B rechnen


von Klaus (Gast)


Lesenswert?

Hallo
Von diesem Timer kommen 20ms
1
void timer1_init()
2
  {    
3
              // Timer 1 16 Bit konfigurieren 16MHz
4
  TCCR1B|= (1<<CS11)|(1<<CS10);      // Prescaler 64
5
  TCCR1B|= (1<<WGM12);          // CTC Modus  Nr 4
6
  TIMSK1|=(1<<OCIE1A)|(1<<OCIE1B);    // Interrupt erlauben
7
  OCR1A=4999;                // Timer 1 Register auf 4999 = 20ms
8
  OCR1B=275;                // 
9
  }
Die Mittelstellung des Servos beträgt dabei OCR1B=275.
Kann ich einfach rechnen:
1
if (!(PINA & (1<<PINA1)))  // links drehen
2
      {      
3
            if(OCR1B < 550)      // Begrenzung links = 550
4
              {
5
           OCR1B = OCR1B + 20;
6
            if(OCR1B>=550)    
7
             {
8
          OCR1B = 550;
9
               }
10
              }
11
          }
Kann ich mit OCR1B rechnen oder muss es erst umrechnet werden z.B. 
uint16 wert?

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


Lesenswert?

Klaus schrieb:
> Kann ich einfach rechnen:

Ja, kannst du. Auch
1
OCR1A += 20;

geht. Das Register hat lesend und schreibend 16 Bit, und man liest auch 
den gleichen Wert zurück, den man rein geschrieben hat.

Allerdings würde ich mehrfache Operationen (wie Addition und Vergleich) 
dann doch in einer Zwischenvariablen durchführen, denn erstens 
produzierst du sonst im ungünstigsten Falle Glitches, zweitens werden 
die verschiedenen Operationen dann in CPU-Registern durchgeführt statt 
in den (langsameren) IO-Registern.
1
  uint16_t ocr1b = OCR1B;
2
  if (ocr1b < 550) {
3
    ocr1b += 20;
4
    if (ocr1b >= 550)
5
      ocr1b = 550;
6
    OCR1B = ocr1b;
7
  }

: Bearbeitet durch Moderator
von Klaus (Gast)


Lesenswert?

Habe das jetzt drin:
1
if (!(PINA & (1<<PINA1)))  // links drehen
2
    {     // Start bei 275
3
    uint16_t ocr1b = OCR1B;        
4
        if(ocr1b < 550)  
5
        {    // Begrenzung links = 550
6
         ocr1b = ocr1b + 20;
7
         OCR1B = ocr1b;  
8
        }
9
        }
Beim betätigen des Tasters geht der Servo sofort auf Endwert (550). Es 
gibt keine Zwischenwerte.

von Falk B. (falk)


Lesenswert?

Klaus schrieb:
> Habe das jetzt drin:if (!(PINA & (1<<PINA1)))  // links drehen
>     {     // Start bei 275
>     uint16_t ocr1b = OCR1B;
>         if(ocr1b < 550)
>         {    // Begrenzung links = 550
>          ocr1b = ocr1b + 20;
>          OCR1B = ocr1b;
>         }
>         }
> Beim betätigen des Tasters geht der Servo sofort auf Endwert (550). Es
> gibt keine Zwischenwerte.

Naja, nicht ganz. Kommt drauf an, wie oft die Funktiona ufgerufen wird. 
Aber im Prinzip hast du Recht, man sollte schon sowas wie eine 
Flankenerkennung und ggf. auch Entprellung einbauen. Dann springt 
der Wert nicht zum Anschlag, sondern man kann einzelne Stufen 
einstellen.

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


Lesenswert?

Klaus schrieb:
> sofort auf Endwert (550). Es gibt keine Zwischenwerte.

Wie schnell ist "sofort", womit hast du das getestet?

Ansonsten müsste man halt ein Stück mehr von deinem Programm sehen um zu 
wissen, wann/wie oft du diese Rechnung ausführst.

von c-hater (Gast)


Lesenswert?

Klaus schrieb:
> Habe das jetzt drin:
>
1
> if (!(PINA & (1<<PINA1)))  // links drehen
2
>     {     // Start bei 275
3
>     uint16_t ocr1b = OCR1B;
4
>         if(ocr1b < 550)
5
>         {    // Begrenzung links = 550
6
>          ocr1b = ocr1b + 20;
7
>          OCR1B = ocr1b;
8
>         }
9
>         }
10
>
> Beim betätigen des Tasters geht der Servo sofort auf Endwert (550). Es
> gibt keine Zwischenwerte.

Doch, die gibt es natürlich. Du merkst bloß nichts davon, weil der Code 
ja sehr wahrscheinlich in einer Schleife (z.B. der Hauptschleife des 
Programms) aufgerufen wird. Da braucht es dann nur 13 
Aufrufe/Schleifendurchläufe und der Endwert ist erreicht. Wenn weiter 
kein Code in der Hauptschleife ist, geht das sehr, sehr schnell.
Dazu kommt dann, dass neue Werte in den OCR-Registern in den PWM-Modi 
immer nur einmal pro PWM-Zyklus in die Hardware übernommen werden. Von 
den 13 Aufrufen des Codes bleiben also bei sonst leerer Hauptschleife 11 
bis 12 effektiv unwirksam. Die Hardware sieht nur den Endwert und zuvor 
eventuell noch einen Zwischenwert, der dann 20ms später durch den 
Endwert ersetzt wird.

Oder anders ausgedrückt: du musst dringend die Architektur deines Codes 
überdenken...

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

c-hater schrieb:
> du musst dringend die Architektur deines Codes
> überdenken...

Wenigstens solltest du warten, das die Taste wieder losgelassen wird. 
Das ist immer noch nicht optimal, weil jeder mechanische Taster prellt, 
aber schon mal ein Schritt nach vorne. Als nächstes könntest du dich mit 
Tastenabfragen beschäftigen, die wirklich funktionieren:
https://www.mikrocontroller.net/articles/Entprellung

: Bearbeitet durch User
von Klaus (Gast)


Lesenswert?

Habe nicht erwartet das der Code so schnell ist. Habe jetzt noch delay 
drin und es ist deutlich zu sehen.
Werde eine einfache Entprellung mit _delay(20) erst mal verwenden. Wenn 
der Rest läuft kommt die Entprellung von Peter.
Danke für eure Hilfe.

von c-hater (Gast)


Lesenswert?

Matthias S. schrieb:

> Wenigstens solltest du warten, das die Taste wieder losgelassen wird.
> Das ist immer noch nicht optimal, weil jeder mechanische Taster prellt,
> aber schon mal ein Schritt nach vorne. Als nächstes könntest du dich mit
> Tastenabfragen beschäftigen, die wirklich funktionieren:
> https://www.mikrocontroller.net/articles/Entprellung

Wäre eine Möglichkeit, aber eine dramatisch aufwändige und umständliche 
für den beabsichtigten Effekt. Sehr viel einfacher wäre es, den 
ansonsten unveränderten Code mit einer kleinen Sache zu ergänzen, 
nämlich der Abfrage und dem Reset des Overflow-Interruptflags des 
Timers.

if (is_ovfflag_set())
{
  [bestehender Code]
  reset_ovfflag();
}

Schon ist sichergestellt, das der bestehende Code maximal alle 20ms 
aufgerufen wird und mit dem berechneten Wert auch tatsächlich etwas 
bewirkt wird. Ganz ohne aufwendige und hier auch völlig unnötige 
Entprellung.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

c-hater schrieb:
> Wäre eine Möglichkeit, aber eine dramatisch aufwändige und umständliche
> für den beabsichtigten Effekt.

So dramatisch aufwändig und umständlich ist Copy&Paste für dich?
Immerhin ist das ein Stück solider Code für den Baukasten und kann immer 
wieder mal benutzt werden. Und lernen kann man immer etwas.

von c-hater (Gast)


Lesenswert?

Matthias S. schrieb:

> So dramatisch aufwändig und umständlich ist Copy&Paste für dich?

Naja, zwei simple Zeilen Code zu schreiben ist auf jeden Fall schneller 
erledigt.

> Immerhin ist das ein Stück solider Code für den Baukasten und kann immer
> wieder mal benutzt werden.

Natürlich ist das guter Code und man kann ihn überall dort verwenden, wo 
er nützlich ist. In dieser konkreten Anwendung ist er es aber nicht.

> Und lernen kann man immer etwas.

Jepp. Nur gibt es da diese Baustein-C&P-ler, die nix lernen wollen. "Ich 
hab' doch schon meinen Hammer, alles andere wird damit zum Nagel 
gemacht, koste es, was es wolle..."

Beitrag #7099764 wurde vom Autor gelöscht.
von Klaus (Gast)


Lesenswert?

c-hater schrieb:
> if (is_ovfflag_set())
> {
>   [bestehender Code]
>   reset_ovfflag();
> }

Danke für deine Info. Das kannte ich bisher nicht. Da werde ich mal das 
DB genau anschauen, da ich es noch nie gesehen habe.

von Wolfgang (Gast)


Lesenswert?

Klaus schrieb:
> Habe jetzt noch delay drin und es ist deutlich zu sehen.

Falls sonst noch irgendetwas in deinem Code ablaufen soll, ist delay() 
sicher die schlechteste Möglichkeit. Zum Test mag das ok sein.
Besser steuerst du den Zeitablauf aber z.B. durch Abfrage des 
System-Timers, d.h. über millis().

von Peter D. (peda)


Lesenswert?

IO-Zugriffe haben oft Seiteneffekte, daher IO-Register nicht mehrfach 
lesen bzw. schreiben.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Ich benutze sowas:
1
/*
2
 *  Waits for the start of the next Timer0 cycle.
3
 */
4
static void WaitForNextTimerCycle(void)
5
{
6
// Clear Timer0 OVF event flag.
7
  TIFR |= (1 << TOV0);
8
// Wait for new Timer0 OVF event flag.
9
  while (!(TIFR & (1 << TOV0))){}
10
}
und rufe das in der Hauptschleife auf. So wird ein festes Zeitraster 
gemacht - wenn Timer0 läuft. Im Datenblatt steht, wo die Bits liegen. 
Hier isses ein Tiny25/45/85.

: Bearbeitet durch User
von Klaus (Gast)


Lesenswert?

c-hater schrieb:
> if (is_ovfflag_set())
> {
>   [bestehender Code]
>   reset_ovfflag();
> }

Habe zu diesem Code nichts gefunden. ovfflag wurde bei mir als Fehler 
angezeigt. Muss ich dazu eine bestimmte Bibliothek laden?

von Heiner (Gast)


Lesenswert?

Klaus schrieb:
> c-hater schrieb:
>> if (is_ovfflag_set())
>> {
>>   [bestehender Code]
>>   reset_ovfflag();
>> }
>
> Habe zu diesem Code nichts gefunden. ovfflag wurde bei mir als Fehler
> angezeigt. Muss ich dazu eine bestimmte Bibliothek laden?

Kollege
is_ovfflag_set() ist eine Funktion, die Du selbst schreiben musst, da is 
nix mit paste&copy 😂

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.