Forum: Mikrocontroller und Digitale Elektronik RC Servo Signalmessung mit ATTiny85 und Konsorten


von Jasson J. (jasson)


Lesenswert?

Hoi zäme,

ich möchte einen Gedankengang mit der Schwarmintelligenz verifizieren:

Ich möchte ein klassisches RC-Servo Signal mit einem Tiny85 ausmessen 
und auf ein PWM spiegeln.
Der selbe Ansatz, der mir eingefallen ist habe ich auch mehrfach im Netz 
gefunden:
Da der Tiny keinen InputCapture hat und nur 8 Bit breite Timer Register 
ist der Ansatz, eine TimerOverflow ISR zum inkrementieren eines Counters 
zu nutzten und eine PinChange ISR, um die Flanken zu erkennen, wo die 
Pulsdauer gebildet wird á la
pulseDuration = overFlowCnt*256 + timerVal
>Mein Eindruck ist, das Ergeniss jittert ordentlich, weil bei bestimmten
>Timings Raiseconditions zwischen Overflow und Pinchange auftreten?

Mir ist nun in den Sinn gekommen, einfach in Präinterrupt-Zeiten zurück 
zu gehen und den ganzen Programfluss zum Polling eines Signalpins zu 
nutzten.
Bei Fsys = 8Mhz und z.B. einer Zielauflösung von 200Ticks/Millisekunde, 
bleiben 40 Zyklen. In diesem Fall kommt einem die ewige Inactivezeit des 
klassischen RC-Servo Signales zur Hilfe, in der man alles mögliche 
machen kann.
Ich denke, man kann hier noch deutlich schneller als 40 tick breite 
Timeslote gehen.

In Pseudocode stelle ich mir das so vor

tickCntr = 0;
while(pin == LOW); //wait for rising edge
ResetAndStartTimer();
while(pin == HIGH)// wait for falling edge and meassure the time
{
   while(Timer < 40);// create equidistance time slots
   tickCntr++;
}
DoSomethingWithTickCntr(); // is allowed to take "long" since signal is 
18ms inactive.

: Gesperrt durch Moderator
von Jasson J. (jasson)


Lesenswert?

Zu Hause und ausprobiert
 -
mit dem Polling-Ansatz habe ich gute Ergebnisse.

Systemtakt ist 8Mhz, Timer 0 läuft mit Prescaler = 1.
Um zum Polling zeitlich äquidistante Slots zu schaffen wird TCNT0 auf 
kleiner 16 geprüft.
Das erzeugt 2µs lange Zeitslots und bei einer Auslenkung von 500µs 
entsprechend 250 "Ticks".
Das Konzept ist, dass die Auslenkung etwas dirty eben ein bisschen 
größer gemacht werden muss als +-500µs, um die für das PWM 255 voll zu 
kriegen.

Zu diesem Kern habe ich noch ein bisschen Logik gedichtet, die mit zwei 
Pins ein Forward und Backward Enable macht und bei Übergang eine 
Verzögerung erzeugt, um einer eventuellen H-Brücke etwas Zeit zu geben.
Dazu gibt es um den Nullpunkt herum einen Totbereich, damit das ganze in 
"Neutral" nicht laufend zwischen vorwärts und rückwärts pendelt.
1
#include <avr/io.h> 
2
#include <stdint.h> 
3
4
#define NEUTRAL 663 // due to 2µs timebase => 1300µs
5
#define DEAD_BAND 5
6
7
#define NEUTRAL_UPPER  NEUTRAL+DEAD_BAND
8
#define TOP  NEUTRAL_UPPER+255
9
10
#define NEUTRAL_LOWER  NEUTRAL-DEAD_BAND
11
#define BOTTOM NEUTRAL_LOWER-255
12
13
volatile static uint16_t pulseLength = 0, state = 5;
14
15
16
/*
17
PB1 PWM Out / OCR1A
18
PB2 Backward Enable
19
PB3 "InputCapture"
20
PB 4 Forward Enable
21
*/
22
23
int main(void)
24
{ 
25
  DDRB = 0b00010110; 
26
 
27
  TCCR0B = 1; 
28
29
   TCCR1 = 0b01100001; 
30
  OCR1C = 255; 
31
32
  while(1)
33
  { 
34
     while((PINB & 8) == 0);
35
    TCNT0 = 0;
36
    pulseLength = 0;
37
    while((PINB & 8) == 8)
38
    {
39
      while(TCNT0 < 16); // creates timeslots with 2µs length
40
      TCNT0 = 0;
41
      pulseLength++;
42
    } 
43
44
    if(pulseLength > NEUTRAL_UPPER) // grater than 
45
    { 
46
      if(state == 8)
47
      {
48
        DDRB |= 0b00000010;//set Pin to output
49
        if(pulseLength > TOP) pulseLength = TOP;
50
        pulseLength -= NEUTRAL_UPPER;
51
        OCR1A = pulseLength;
52
        PORTB |= 0b00010000;
53
      }
54
      else
55
      {
56
        OCR1A = 0; // set PWM to 0% dutycycle
57
        DDRB &= 0b11111101;//set Pin to input
58
        PORTB &= 0b11101011; // set fw/be enable low
59
        state++;
60
      }
61
    } 
62
    else if(pulseLength < NEUTRAL_LOWER)
63
    {
64
      if(state == 2)
65
      {
66
        DDRB |= 0b00000010;//set Pin to output
67
        if(pulseLength < BOTTOM)pulseLength = BOTTOM;
68
        OCR1A = BOTTOM - pulseLength;
69
        PORTB |= 0b00000100;
70
      }
71
      else
72
      {
73
        OCR1A = 0; // set PWM to 0% dutycycle
74
        DDRB &= 0b11111101;//set Pin to input
75
        PORTB &= 0b11101011; // set fw/be enable low
76
        state--;
77
      }
78
    }
79
    else
80
    {
81
      OCR1A = 0; // set PWM to 0% dutycycle
82
      DDRB &= 0b11111101;//set Pin to input
83
      PORTB &= 0b11101011; // set fw/be enable low
84
85
      if(state > 5) state--;
86
      if(state < 5) state++;
87
    }
88
  }
89
90
}

von Pedro M. (pedromills)


Lesenswert?

Danke für die Info.

von Jasson J. (jasson)


Lesenswert?

Cool, nutzt es vielleicht tatsächlich jemandem -

was hast du vor zu beuen?
Bei mir ist´s ein einfacher Drehzahlsteller für kleinere Motoren.

von Timo N. (tnn85)


Lesenswert?

Bin ich blind oder hast du einfach nur versucht den CTC-Mode (Clear 
Timer on Compare Match) mit  Timer/Counter0 Output Compare Match A 
Interrupt
durch eine while-Schleife nachzubilden?

Warum verwendest du den Interrupt nicht und erzeugst deine Timeslots mit 
einer while-Schleife?

: Bearbeitet durch User
von Jasson J. (jasson)


Lesenswert?

>Bin ich blind oder hast du einfach nur versucht den CTC-Mode (Clear
>Timer on Compare Match) mit  Timer/Counter0 Output Compare Match A
>Interrupt
>durch eine while-Schleife nachzubilden?

Stimmt, richtig gesehen


>Warum verwendest du den Interrupt nicht und erzeugst deine Timeslots mit
>einer while-Schleife?

Weil ich nichts anderes machen will, als das PWM und Fw/Bw Pins.
Da bin ich ohne springen zur ISR und dem was dabei sonst passiert eher 
schneller und kann falls gewollt die Slots kürzer machen.

von Jasson J. (jasson)


Lesenswert?

Wobei es das Wert ist, dass zu untersuchen, ob sich der ISR-Overhead 
unterbringen lässt, für das gewollte timing.

Bei meiner Lösung wird sich auch ein Jitter ergeben, da der Timer mit 
Fsys läuft und die
>while(TCNT0 < 16);
am Ende auch einliest, vergleicht, conditioned-jumped - kann man gerne 
bis >= 16 zählen

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


Lesenswert?

Meine Lösung kannst du dir hier angucken:
Beitrag "RC-Servoelektronik für DC-Motor"

von Jasson J. (jasson)


Lesenswert?

Hm... interessant, ich sehe, du hast eine
>ISR_NOBLOCK
Anweisung. Die sorgt sicher dafür, dass bei ISR-Entry das globale 
Interrupt Enable wieder gesetzt wird.

: Bearbeitet durch User
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Jasson J. schrieb:
> Die sorgt sicher dafür, dass bei ISR-Entry das globale
> Interrupt Enable wieder gesetzt wird.

So isses. Näheres über die verschiedenen Optionen für ISR gibts in der 
Hilfe für die avrlib.

Beitrag #6163635 wurde von einem Moderator gelöscht.
Beitrag #6260310 wurde von einem Moderator gelöscht.
von Uwe D. (monkye)


Lesenswert?

Du könntest auch das Teil mit PLL auf 16MHz laufen lassen und hast mehr 
Zeit für andere Aufgaben...

Beitrag #6287325 wurde von einem Moderator gelöscht.
Beitrag #6288268 wurde von einem Moderator gelöscht.
Beitrag #6288270 wurde von einem Moderator gelöscht.
Dieser Beitrag ist gesperrt und kann nicht beantwortet werden.