Peter Dannegger´s ( PeDa ) Entprellroutine in ein Arduino Sketch einbinden

OP (Firma: Anscheinend Corner-Cases ;-)) #7491769
Lesenswert?

Hallo zusammen,

wie geht das?

Hier sind 3-Dateien, mit denen es gehen sollte :

Beitrag "Re: Tasterverarbeitung mit ARDUINO-C"

Habe mal alles in eine Datei im Anhang zu sehen gepackt.

Ich bekomme jedoch diese Fehlermeldung :

In file included from C:\Users\Ich\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino/Arduino.h:30:0, from G:\AVR-Projekte\Arduino\MEGA2560 Board\GLCD_ST 7920\Entprellen\GLCD_entprellen\GLCD_entprellen.ino:1: G:\AVR-Projekte\Arduino\MEGA2560 Board\GLCD_ST 7920\Entprellen\GLCD_entprellen\GLCD_entprellen.ino: In function 'void loop()': GLCD_entprellen:76:1: error: expected unqualified-id before string constant ISR( TIMER1_COMPA_vect ) { // every 10ms ^ GLCD_entprellen:76:26: error: a function-definition is not allowed here before '{' token ISR( TIMER1_COMPA_vect ) { // every 10ms ^ GLCD_entprellen:96:26: error: a function-definition is not allowed here before '{' token void init_entprell(void) { ^ GLCD_entprellen:112:43: error: a function-definition is not allowed here before '{' token uint8_t get_key_press( uint8_t key_mask ) { ^ GLCD_entprellen:128:41: error: a function-definition is not allowed here before '{' token

Bernd_Stein

Angehängte Dateien:
Beitrag #7491776 wurde von einem Moderator gelöscht.
Gast #7491778
Lesenswert?

Erstmal ordentlich einrücken, dann sieht man den Fehler auch:

1
#include <Arduino.h>
2
#include <stdint.h>
3
#include <avr/interrupt.h>
4

5
uint8_t get_key_press(uint8_t key_mask);
6
uint8_t get_key_rpt(uint8_t key_mask);
7
uint8_t get_key_state(uint8_t key_mask);
8
uint8_t get_key_short(uint8_t key_mask);
9
uint8_t get_key_long(uint8_t key_mask);
10
void init_entprell(void);
11

12
#define KEY_DDR DDRB
13
#define KEY_PORT PORTB
14
#define KEY_PIN PINB
15
#define KEY0 0
16
#define KEY1 1
17
#define KEY2 2
18
#define ALL_KEYS(1 << KEY0 | 1 << KEY1 | 1 << KEY2)
19

20
#define REPEAT_MASK(1 << KEY1 | 1 << KEY2) // repeat: key1, key2
21
#define REPEAT_START 50 // after 500ms
22
#define REPEAT_NEXT 20 // every 200ms
23
#define F_TIMER1 100 // Timer 1 frequency /Hz
24

25
extern volatile uint8_t key_state; // debounced and inverted key state:
26
// bit = 1: key pressed
27
extern volatile uint8_t key_press; // key press detect
28

29
extern volatile uint8_t key_rpt; // key long press and repeat
30

31
#define LED_DDR DDRC
32
#define LED_PORT PORTC
33
#define LED0 0
34
#define LED1 1
35
#define LED2 2
36

37
void setup(void) {
38
  LED_PORT = 0; // LED outputs
39
  LED_DDR = 0xFF;
40

41
  init_entprell();
42
} //Ende setup
43

44
void loop(void) {
45
  if (get_key_short(1 << KEY0)) LED_PORT ^= 1 << LED0; // reaction on key release
46
  if (get_key_long(1 << KEY1)) LED_PORT ^= 1 << LED1; // reaction after REPEAT_START ms
47

48
  // single press and repeat, running light
49
  if (get_key_press(1 << KEY2)) { // reaction on key press
50
    uint8_t i = LED_PORT;
51
    i = (i & 0x03) | ((i & ~0x03) << 1);
52
    if ((i & ~0x03) == 0) i |= (1 << LED2);
53
    LED_PORT = i;
54
  }
55

56
  volatile uint8_t key_state; // debounced and inverted key state:
57
  // bit = 1: key pressed
58
  volatile uint8_t key_press; // key press detect
59

60
  volatile uint8_t key_rpt; // key long press and repeat
61

62
  ISR(TIMER1_COMPA_vect) { // every 10ms
63
    static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt;
64
    uint8_t i;
65

66
    i = key_state ^ ~KEY_PIN; // key changed ?
67
    ct0 = ~(ct0 & i); // reset or count ct0
68
    ct1 = ct0 ^ (ct1 & i); // reset or count ct1
69
    i &= ct0 & ct1; // count until roll over ?
70
    key_state ^= i; // then toggle debounced state
71
    key_press |= key_state & i; // 0->1: key press detect
72

73
    if ((key_state & REPEAT_MASK) == 0) { // check repeat function
74
      rpt = REPEAT_START; // start delay
75
    }
76
    if (--rpt == 0) {
77
      rpt = REPEAT_NEXT; // repeat delay
78
      key_rpt |= key_state & REPEAT_MASK;
79
    }
80
  }
81

82
  void init_entprell(void) {
83
    KEY_DDR &= ~ALL_KEYS; // configure key port for input
84
    KEY_PORT |= ALL_KEYS; // and turn on pull up resistors
85

86
    // Timer 1, CTC mode 4, prescaler 256  
87
    TCCR1A = 0;
88
    TCCR1B = (1 << WGM12) | (1 << CS12);
89
    OCR1A = (F_CPU / (256 L * F_TIMER1)) - 1;
90
    TIMSK1 |= (1 << OCIE1A); // enable timer interrupt
91
  }
92

93
  /*
94
   * check if a key has been pressed. Each pressed key is reported
95
   * only once
96
   */
97

98
  uint8_t get_key_press(uint8_t key_mask) {
99
    cli(); // read and clear atomic !
100
    key_mask &= key_press; // read key(s)
101
    key_press ^= key_mask; // clear key(s)
102
    sei();
103
    return key_mask;
104
  }
105

106
  /*
107
   * check if a key has been pressed long enough such that the
108
   * key repeat functionality kicks in. After a small setup delay
109
   * the key is reported being pressed in subsequent calls
110
   * to this function. This simulates the user repeatedly
111
   * pressing and releasing the key.
112
   */
113

114
  uint8_t get_key_rpt(uint8_t key_mask) {
115
    cli(); // read and clear atomic !
116
    key_mask &= key_rpt; // read key(s)
117
    key_rpt ^= key_mask; // clear key(s)
118
    sei();
119
    return key_mask;
120
  }
121

122
  /*
123
   * check if a key is pressed right now
124
   */
125

126
  uint8_t get_key_state(uint8_t key_mask) {
127
    key_mask &= key_state;
128
    return key_mask;
129
  }
130

131
  /*
132
   * Get short key press
133
   */
134

135
  uint8_t get_key_short(uint8_t key_mask) {
136
    cli(); // read key state and key press atomic !
137
    return get_key_press(~key_state & key_mask);
138
    sei();
139
  }
140

141
  /*
142
   * Get long key press
143
   */
144

145
  uint8_t get_key_long(uint8_t key_mask) {
146
    return get_key_press(get_key_rpt(key_mask));
147
  }
148

149
} //Ende Loop ***

Unter LED_PORT = i; gehört eine zweite geschlossene Klammer "}". Danach bekommst du die nächsten Fehler angezeigt.

OP (Firma: Anscheinend Corner-Cases ;-)) #7491805
Lesenswert?

Und immer schön hierdrann denken, so wie in diesem Fall, an Punkt 4.

"Die fünf universellen Forenregeln ;-)"

  1. Threadüberschrift lesen
  2. Evtl. Datum beachten
  3. Posting verstehen
  4. Falls nicht drittens, dann nachfragen oder Klappe halten.
  5. Auf die Fragen eingehen und / oder Alternativen bzw. Verbesserungen nennen.

Die Punkte kann man an einer Hand abzählen, man sollte also in der Lage sein, bis fünf zählen zu können. Deshalb sind sie evtl. gut zu merken, falls nicht dann nur Punkt 3 und Punkt 4 merken.

Bernd_Stein

OP (Firma: Anscheinend Corner-Cases ;-)) #7496096
Lesenswert?

Es geht um einen Hand-Encoder in Verbindung mit PeDa's Komfort-Entprell-Routine.

Warum wird der Text " Spur_A & Spur_B " angezeigt, wenn doch nur Spur_A eine 1 vorweist?

Wie muss ich es richtig schreiben, damit nur bei Spur_A & Spur_B = 1 der Text angezeigt wird?

Bernd_Stein

Angehängte Dateien:
Beitrag #7496114 wurde von einem Moderator gelöscht.
#7496152
Lesenswert?

Bernd S. schrieb:

Ich hab es so wie hier gelöst, aber danke für deine Hilfestellung die ich nicht verstehe, aber egal ich hab ja eine Lösung.

Du hast keine Lösung, du hast auch keine Ahnung. Und daran wird sich auch zu deinen Lebzeiten nichts mehr ändern. Du stolperst nur vollkommen planlos durch die Gegend in der irrigen Annahme, auch nur ansatzweise was Sinnvolles zu programmieren.

#7496168
Lesenswert?

Bernd S. schrieb:

Michael P. schrieb:

Versuch mal "&" (binär) anstatt von "&&" (logic)

Ich hab es so wie hier gelöst, aber danke für deine Hilfestellung die ich nicht verstehe, aber egal ich hab ja eine Lösung.

Beitrag "Re: Abfrage ob bits gesetzt in "C""

Bernd_Stein

Nei, in deiner originalen Abfrage testet du ob eine Variable gleich einem Wert ist und gleichzeitig (logisches &&) ob das Ergebnis einer Schiebeoperation ungleich Null ist. Letzteres ist bei dir immer der Fall.

Im zitierten Beitrag wird getestet ob zwei Bits einer Variable (binär 'ver-und-tete'(&) mit Bitmaske) gleichzeitig (logisches &&) gesetzt sind.

#7496256
Lesenswert?

Moin,

bei mir lese ich die Key Pins in der Arduino TIMER0 ISR mit einem Huckepack Compare A Interrupt.

Dazu habe ich diese ISR hinzugefügt die im Hintergrund (Schmarotzerisch) mit der Arduino TIMER0 ISR mitläuft:

ISR(TIMER0_COMPA_vect) { _keyreg = .... }

// Der TIMER0 ist schon fuer millis() gebraucht. Man triggert den Interrupt // einfach in der Mitte des T0 Timers und ruft die COMPARE A Funktion auf/ OCR0A = 0xAF; TIMSK0 |= _BV(OCIE0A);

Der Rest ist der normale Einsatz des Standard Beispiels von Peter.

_keyregs ist natuerlich Global deklariert und übermittelt den Portzustand der KEYS in Peters Code .

Das ganze hat den Vorteil den AVR kaum zu belasten und die Keys werden bequem von der schon existierenden ARDUINO TIMER0 ISR mitgezogen. die rund 1ms T0 Rate lässt sich mit einem Zähler wie gewünscht anpassen. Bei mir werden die Pins dann alle 10ms abgetastet.

Im konkreten Fall eigentlich über einen PCF8575 IO Expander. Aber das tut nichts zur Sache.

Jedenfalls funktioniert es bei mir so ausgezeichnet. Läuft wie ein Uhrwerk.

Gerhard

OP (Firma: Anscheinend Corner-Cases ;-)) #7496260
Lesenswert?

Ich möchte die Variable tmp bei jedem " key_press " jeweils um eine Stelle nach links schieben.

Wie geht so etwas?

1
void loop(void)
2
{
3
  if( key_press )  { 
4
    static byte tmp;
5
    tmp = ( H_ENCODER_MSK ) & ( key_press );
6
    
7

8
  Serial.println( );
9
  Serial.println( );
10
  Serial.println( );
11
  Serial.print  ( "key_press       = "   );
12
  Serial.println(  key_press,    BIN   );
13

14

15
    byte temp = tmp;       // Dient dazu tmp..
16
    tmp = (temp<<1) | (temp>>7);    //..einmal links zu schieben
17
 
18
  //Serial.println( );
19
  Serial.print  ( "tmp             = "   );
20
  Serial.println(  tmp,    BIN           );
21

22
      if( tmp & (1<<6) ) {
23
        
24
  Serial.println( );
25
  Serial.print  ( "Rechts          = "    );
26
  Serial.println( tmp, BIN );
27
  Serial.println( );
28
      
29
      tmp = 0;
30
      }
31
      if( tmp & (1<<4) )  {
32
        
33
  Serial.println( );
34
  Serial.print  ( "Links           = "    );
35
  Serial.println( tmp, BIN );
36
  Serial.println( );
37

38
      tmp = 0;
39
      }
40
      get_key_press ( key_press );  // key_press loeschen
41
  }  
42

43
} //Ende Loop ***

Bernd_Stein

Angehängte Dateien:
#7496280
Lesenswert?

Gerhard O. schrieb:

Moin,

bei mir lese ich die Key Pins in der Arduino TIMER0 ISR mit einem Huckepack Compare A Interrupt.

Gerhard, du verschwendest dein Zeit und Energie und fütterst einen lernresistenten Narzistzen! Meine Aussage war kein Witz!

Beitrag "Re: Peter Dannegger´s ( PeDa ) Entprellroutine in ein Arduino Sketch einbinden"

Der OP hat schon 10 Jahre (!) versucht, AVR Assembler zu kapieren, mit SEHR begrenztem Ergebnis. Nun murkst er planlos mit 1/8 Wissen mit C rum!

Don't feed the troll/fool!

OP (Firma: Anscheinend Corner-Cases ;-)) #7498807
Lesenswert?

Ich habe einen Hand-Encoder der 20 Rastungen hat. Pro Rastung werden 4 Signaländerungen erzeugt, also der typische 2-Bit-Graycode => 00; 01; 11; 10

Könnte jemand mal den Code im Anhang mit so wenig Codezeilen wie möglich ändern, damit pro Klick bzw. Rastung nur eine Zahl ausgegeben wird und nicht 4 ?

Aber bitte nicht gleich die Universallösung für 1, 2, 4 Rastungen.

Ist wieder mal ein Code von PeDa :

Beitrag "Re: Drehgeber auslesen"

Ich gebe es ja zu, so ganz bin ich da noch nicht durchgestiegen.

Bernd_Stein

Angehängte Dateien:
OP (Firma: Anscheinend Corner-Cases ;-)) #7499002
Lesenswert?

Veit D. schrieb:

Die Anpassung ans Raster pro Änderung kann man über das Shiften des Zählerwertes machen.

Ich vermute durch 2 maliges Rechtsschieben, was ja einer Teilung durch 4 entspricht.

Verstehe leider nur nicht, warum dies nicht klappt. Packe ich enc_delta >>= 2; an die eine Stelle, passiert das Eine und ein weiterdrehen bewirkt nichts mehr.

Genauso, wenn ich es an die andere Stelle kopiere ( siehe Screenshots ).

Bernd_Stein

Angehängte Dateien:
OP (Firma: Anscheinend Corner-Cases ;-)) #7499332
Lesenswert?

Ich habe PeDa`s solide Lösung für einen ATmega 2560-Board angepasst, wobei der CTC-Modus des Timer1 genutzt wird.

Könnte mir jemand einen Code zeigen oder schreiben, wo pro Klick, also pro Rastung nur einmal ein Wert über die RS232 geschickt wird?

An den LED`s kann ich sehen, dass es generell funktioniert.

Veit D. schrieb:

Es fällt ihm scheinbar sehr schwer und er quält sich durch.

Dies stimmt leider. Ich bin kein Autodidakt und C ist noch sehr kryptisch für mich.

1
void loop() 
2
{
3
 val += encode_read(4);          // read a single step encoder
4
    
5
Serial.print  ( "val                             = "  );
6
Serial.println(  val, DEC );
7
//Serial.println( );
8
    
9
    LEDS = val;
10
    
11
  
12
} // Ende void loop()

Bernd_Stein

Angehängte Dateien:
OP (Firma: Anscheinend Corner-Cases ;-)) #7500010
Lesenswert?

Veit D. schrieb:

Hallo,

val entfernt, dafür struct Counter erstellt, kannste auch umbenennen.

Vielen dank für deinen Code, der auch funktioniert, den ich aber leider nicht nachvollziehen konnte.

Vielleicht kann mir ja jemand erklären warum meine Lösung auch funktioniert.

Selbst meine Lösung verstehe ich nicht. Habe sie durch Zufall gefunden, als ich wenigstens nur einen Wert, nämlich die 1 ausgeben wollte. Beim weiterdrehen kam dann 2 usw. Die Gegenrichtung funktionierte nicht, da hab ich einfach mal ...

1
void loop() 
2
{
3
  if( enc_delta == 4 | enc_delta == -4 )  {
4
    val += encode_read(4);          // 4 Signaländerungen pro Rastung
5
    
6
Serial.print  ( "val = "  );
7
Serial.println(  val, DEC );
8

9
  }  
10
  LEDS = val;
11
} // Ende void loop()

Ach, anbei noch eine gute Erklärung zu PeDa's Hand-Encoder-Lösung. Ab Kapitel 5, Seite 11 bzw. 20.

Bernd_Stein

#7500306
Lesenswert?

Harald K. schrieb:

Wilhelm M. schrieb:

Es geht ja nur im den Variablennamen new, in C++ ist das ein Schlüsselwort.

Du kannst den Text auch ändern, das ist ein Wiki.

Echt? War mir noch gar nicht aufgefallen ;-)

Ich zitier' mich mal selbst:

Niemand hält Dich davon ab, es zu tun. Bring' Dich ein!

Die Frage ist, ob es überhaupt Sinn macht. Zwar bin ich davon überzeugt, dass meine Lösung tatsächlich die flexibelste und effektivste ist, jedoch scheitert es ja regelmäßig daran, dass die Leute hier sich beschweren, dass bspw. für avr-g++ keine libstdc++ verfügbar ist, und dass deswegen kein std::array<> oder std::make_index_sequence<>, ... verfügbar sei. Aber auf die Idee, das selbst zu erstellen, kommt irgendwie keiner. Und wenn man die Sachen hinzufügt, dann kommt die Beschwerde, es sei zu umfangreich. Obwohl das natürlich Bestandteil einer Bibliothek ist, damit gar keine Rolle spielt und der eigentliche Code zur Lösung der Aufgabe recht kurz. Weil er so kurz ist, kommt die Beschwerde, dass zu viel hinter Bibliotheken versteckt sei und man deswegen nichts verstehe bzw. Meta-Programmierung, funktionale Ansätze und Closures nicht geeignet seien. Für die meisten größeren Controller (etwa STM) macht es dann auch wieder wenig Sinn, weil die ja oft einen Quadratur-Encoder als interne Peripherie dabei haben. Daher verspüre ich wenig Drang dazu, das in das Wiki zu schreiben.

#7500362
Lesenswert?

Wilhelm M. schrieb:

Die Frage ist, ob es überhaupt Sinn macht. Zwar bin ich davon überzeugt, dass meine Lösung tatsächlich die flexibelste und effektivste ist,

Kann sein, ist beim Thema Arduino und seiner großen Zielgruppe aber nicht das wesentliche Kriterium. Es muss einfach anwendbar und robust sein. Das reicht. Selbst Einschränkungen in der Funktionalität sind in vielen Fällen akzeptabel und werden teileise sogar aktiv betrieben, um die Einfachheit zu erreichen. Der Wurm muss dem Fisch schmecken und nicht dem Angler!

#7500488
Lesenswert?

Bernd S. schrieb:

Wieso meine Lösung auch funktioniert weiß wohl keiner hier oder ?

if( enc_delta == 4 | enc_delta == -4 )

Doch, natürlich.

https://en.cppreference.com/w/cpp/language/operator_precedence

Wie du siehst, bindet == stärker sowohl als | (bitwise OR) als auch als || (logical OR). Du führst also eine bitweise Veroderung zweier Vergleichsresultate aus. Vergleichsresultate sind immer entweder 0 oder 1. Und deren bitweise Veroderung ergibt dasselbe Resultat wie deren logische Veroderung.

Dennoch solltest du dir natürlich angewöhnen, in logischen Ausdrücken die logischen Operatoren zu verwenden, und nicht die Bitoperatoren.

LG, Sebastian

OP (Firma: Anscheinend Corner-Cases ;-)) #7500591
Lesenswert?

Sebastian W. schrieb:

Dennoch solltest du dir natürlich angewöhnen, in logischen Ausdrücken die logischen Operatoren zu verwenden, und nicht die Bitoperatoren.

Ich versuch es ja. Ich versteh es trotzdem nicht. Wenn ich beispielsweise 5 habe ist dies weder == 4 noch == -4.

Jetzt was anderes. Habe schon sämtliche Datentypen durch und weiß nicht wie ich es schaffen soll, dass wenn der Wert von val < 0 wird, val = 0 gesetzt wird. Oder wenn val >= 65535 wird val auf 65535 begrenzt wird (siehe Screenshot ).

Bernd_Stein

Angehängte Dateien:
#7500611
Lesenswert?

Bernd S. schrieb:

val >= 65535

Kann val denn überhaupt größer als 65535 werden? Wenn es ein uint16 ist, dann wird dieser Wert nie erreicht. Der Zähler wenn er immer um 4 inkrementiert wird) erreicht erst den Wert 65.532 und läuft dann über und steht auf dem Wert 1 anstatt 65.536 zu erreichen.

Konkret sieht es so aus: val wird um 4 inkrementiert und hat den Wert 65.532. Dies erfüllt deine Bedinungung noch nicht. Nächster Durchlauf, val wird wieder um 4 inkrementiert und anstatt 65536 zu erreichen, hat val den Wert 1. Und vergleicht man, ob 1 >= 65535 ist, kommt man zu dem Schluss, dass dies nicht der Fall ist.

Was aber außerhalb deines Schnipsels mit val passiert liegt wieder im Dunkeln...

#7500709
Lesenswert?

Wilhelm M. schrieb:

Die Frage ist, ob es überhaupt Sinn macht. Zwar bin ich davon überzeugt, dass meine Lösung tatsächlich die flexibelste und effektivste ist, jedoch scheitert es ja regelmäßig daran, dass die Leute hier sich beschweren, dass bspw. für avr-g++ keine libstdc++ verfügbar ist, und dass deswegen kein std::array<> oder std::make_index_sequence<>, ... verfügbar sei. Aber auf die Idee, das selbst zu erstellen, kommt irgendwie keiner. Und wenn man die Sachen hinzufügt, dann kommt die Beschwerde, es sei zu umfangreich. Obwohl das natürlich Bestandteil einer Bibliothek ist, damit gar keine Rolle spielt und der eigentliche Code zur Lösung der Aufgabe recht kurz. Weil er so kurz ist, kommt die Beschwerde, dass zu viel hinter Bibliotheken versteckt sei und man deswegen nichts verstehe bzw. Meta-Programmierung, funktionale Ansätze und Closures nicht geeignet seien. Für die meisten größeren Controller (etwa STM) macht es dann auch wieder wenig Sinn, weil die ja oft einen Quadratur-Encoder als interne Peripherie dabei haben. Daher verspüre ich wenig Drang dazu, das in das Wiki zu schreiben.

Das liest sich alles nach Ausrede. Man muss ja nicht immer mit der größten Keule jedes Problem erschlagen. Wenn man die libstdc++ nicht hat, löst man das Problem eben ohne. Da finden sich immer Wege. Und wenn du jemanden eine praktische Lösung vorschlagen möchtest ist es irgendwie vermessen und gar komisch, von den gleichen Leuten denen man mit einer Lösung ihres Problems helfen möchte gleichzeitig zu verlangen das die gefälligst vorher sich ihre libstdc++ selbst schreiben. Das passt ja nun nicht zusammen. Wer in der Lage ist seine libstdc++ zu schreiben, der benötigt sicherlich keine fremde Hilfe mehr für irgendwas anderes. Für diese Encoderauswertung hier benötigt man zudem keine libstdc++. Ich wüßte nicht wofür. Der nächste Gedanke ist, wenn jeder seine libstdc++ schreibt ist das bestimmt nie zu 100% zur echten libstdc++ kompatibel. Da gibt es zu viele Fallstricke der Umsetzung, nehme ich an. Du wirst deine sicherlich auch immer mal wieder anpassen müssen, weil irgendwo ein Problem auftauchte. Wenn du wirklich helfen möchtest, dann kannst du den Leuten hier helfen ihre libstdc++ aktuell zu halten. https://github.com/modm-io/avr-libstdcpp Das ist die Beste die ich kenne, läuft aber manchmal noch nicht "ganz rund". Ich formuliere es einmal anders. Du musst nicht jeden Tag den Leuten unter die Nase reiben das du deine libstdc++ hast und sie alle gefälligst nachprogrammieren sollen. Die meisten werden davon noch nie etwas gehört haben und kamen auch ohne gut klar. Viele Dinge der libstdc++ kann man auf dem AVR sowieso nicht nutzen wegen Speicherallokationen. Da muss man sowieso wissen was man tut. Von daher ist die komplette libstdc++ in meinen Augen witzlos.

Laut meines Wissens wird die libstdc++ für C++23 in größerem Umfang "umgebaut". Um das Problem zu bekämpfen das man eigentlich gar nicht weiß welche Bibliothek man einbinden muss um irgendwas passendes anwenden zu können. Die ist also wie der gcc selbst nie fertig. Der Pflegeaufwand muss sehr hoch sein. Das ist in meinen Augen noch ein Grund warum ich mir bis jetzt nur paar Type Traits davon nachgebaut habe aber nie wirklich angefangen habe mich mit der libstdc++ als solches zu befassen. Ich will ja meine Aufgaben lösen und mir nicht künstlich neue Probleme schaffen. Das Ding heißt ja nicht umsonst Standard STL und nicht Wilhelm-STL oder Veit-STL. Die ist ja nur einfachen Nutzung da und nichts anderes und ausgiebig getestet.

Wenn du für den Encoder eine praktische Lösung hast ohne libstdc++ kannste die zeigen. Vielleicht haste ja eine bessere Lösung als meine für meinen Nano Every. Aber deine ständige Wiederholung nach dem Motto "warum habt ihr Idioten keine eigene libstdc++" geht mir langsam aber sicher auf den Zeiger. Weil das hilft niemanden weiter. Denn wenn ich was habe was ich sowieso niemanden anderem zeige, dann erzähle ich davon erst gar nicht.

#7500896
Lesenswert?

Veit D. schrieb:

Für diese Encoderauswertung hier benötigt man zudem keine libstdc++. Ich wüßte nicht wofür.

Wenn man etwas mit TMP macht, so benötigt man eigentlich immer std::index_sequence<>, std::integral_constant<>, std::make_index_sequence<>. Und für integrierte Test oder LUTs dann auch std::array<>.

Veit D. schrieb:

https://github.com/modm-io/avr-libstdcpp Das ist die Beste die ich kenne, läuft aber manchmal noch nicht "ganz rund".

Na, das ist ja großartig. Es ist schon wirklich Jahre her, dass ich mir das Projekt mal angesehen hatte. Aber der Hinweis ist natürlich sehr wichtig, als es nun gar keinen Grund mehr gibt, Dinge abzulehnen, die für AVR auch die stdlibc++ benutzen.

Veit D. schrieb:

Viele Dinge der libstdc++ kann man auf dem AVR sowieso nicht nutzen wegen Speicherallokationen. Da muss man sowieso wissen was man tut. Von daher ist die komplette libstdc++ in meinen Augen witzlos.

Wenn die o.g. Realisierung nun tatsächlich einen std::vector<> oder std::string<> mit Heap-Alloc anbietet, dann ist das natürlich Murks. Vielleicht doch nicht so gut, was Du da erwähnst. Entweder man hat so etwas wie FixedVector<> oder man benutzt einen static-pool-allocator.

Veit D. schrieb:

Um das Problem zu bekämpfen das man eigentlich gar nicht weiß welche Bibliothek man einbinden muss um irgendwas passendes anwenden zu können.

Du meinst "modules". Man wird aber weiterhin header benutzen können. Der Vorteil der "modules" liegt woanders.

Veit D. schrieb:

Aber deine ständige Wiederholung nach dem Motto "warum habt ihr Idioten keine eigene libstdc++" geht mir langsam aber sicher auf den Zeiger.

Es hat aber nun endlich Deinen Hinweis zu Tage gebracht, dass es tatsächlich eine stdlibc++ für AVR gibt. Das finde ich schon mal sehr wichtig.

#7500930
Lesenswert?

Wilhelm M. schrieb:

Veit D. schrieb:

Für diese Encoderauswertung hier benötigt man zudem keine libstdc++. Ich wüßte nicht wofür.

Wenn man etwas mit TMP macht, so benötigt man eigentlich immer std::index_sequence<>, std::integral_constant<>, std::make_index_sequence<>. Und für integrierte Test oder LUTs dann auch std::array<>.

Ein Bsp. von mir ohne stdlibc++. Beitrag "Drehgeber und Tastenentprellung für Arduino"

Veit D. schrieb:

Viele Dinge der libstdc++ kann man auf dem AVR sowieso nicht nutzen wegen Speicherallokationen. Da muss man sowieso wissen was man tut. Von daher ist die komplette libstdc++ in meinen Augen witzlos.

Wenn die o.g. Realisierung nun tatsächlich einen std::vector<> oder std::string<> mit Heap-Alloc anbietet, dann ist das natürlich Murks. Vielleicht doch nicht so gut, was Du da erwähnst. Entweder man hat so etwas wie FixedVector<> oder man benutzt einen static-pool-allocator.

Ist es nicht der Sinn eines Standards das Standardverhalten für alle zu haben? Ich kann doch niemanden einen Vorwurf machen wenn er das Standardverhalten für AVR nachbaut. Der es dann nutzt muss wissen was er auf einem AVR sinnvoll einsetzen kann und was nicht. Soweit muss man da gar nicht schauen. Das fängt schon mit der String Lib an, ohne zu wissen was man tut kann die nach hinten losgehen. Das Problem mit dem Speicher ist auch nicht zwingend auf AVR begrenzt. Alle die µC programmieren und die libstdc++ verwenden müssen wissen was sie ihrem stark begrenzten Speicher im Vergleich zur Desktop CPU/RAM zumuten können und was nicht.

Veit D. schrieb:

Um das Problem zu bekämpfen das man eigentlich gar nicht weiß welche Bibliothek man einbinden muss um irgendwas passendes anwenden zu können.

Du meinst "modules". Man wird aber weiterhin header benutzen können. Der Vorteil der "modules" liegt woanders.

Ja richtig, hing mit dem kommenden Modules zusammen.

Sorry Bernd für die Abschweifung in deinem Thread.

OP (Firma: Anscheinend Corner-Cases ;-)) #7501083
Lesenswert?

Veit D. schrieb:

Sorry Bernd für die Abschweifung in deinem Thread

Danke, das zeigt dass du Anstand hast. Ich finde hier seid ihr mit eurer Diskusion besser aufgehoben :

Beitrag "Drehgeber und Tastenentprellung für Arduino"

Stefan S. schrieb:

Kann val denn überhaupt größer als 65535 werden? Wenn es ein uint16 ist, dann wird dieser Wert nie erreicht. ...

Auch int32_t brachte kein Erfolg, aber jetzt mal ganz von Anfang. val wurde in Einer Schritten gezählt. Zuerst vorwärts bis 2, dann rückwärts um die Bedingung val < 0 zu prüfen. Hierbei erwartete ich -1, aber stattdessen wird die Bedingung val >= 65535 erfüllt ( siehe Screenshot im Link ).

Beitrag "16-Bit Wertbegrenzungen 0 und 65535 setzen"

Das Programm ist im Anhang ( ..._Original ) und die Folge für val ist " *2 + 102 ", was eigentlich 164346 ergeben müsste. Jetzt dürfen wir raten, warum 38810 herauskommt ( siehe Screenshot ).

1
if( val >= 65535 ) val = 65535; 
2
if( val  <  0    ) val = 0;

Bernd_Stein

#7501127
Lesenswert?

Hallo,

warum soll für val 164.346 rauskommen wenn du den Wert nach oben auf 65.535 begrenzt?

1
if( val > 65535 ) val = 65535; 
2
    if( val <     0 ) val = 0;

Das ist in der Reihenfolge quatsch.

1
void loop() 
2
{
3
  if( enc_delta == 4 || enc_delta == -4 )  {
4
    val += encode_read(4);          // 4 Signaländerungen pro Rastung
5
    val += val +100;  // Zum Testen
6
    cli( );
7
    OCR3A = val;                    // An Rechteckgenerator Wert übergeben
8
    sei( );
9
    if( val > 65535 ) val = 65535; 
10
    if( val <     0 ) val = 0; 
11
  
12
Serial.print  ( "val   = "  );
13
Serial.println(  val, DEC   );
14
Serial.print  ( "OCR3A = "  );
15
Serial.println(  OCR3A, DEC );

Bei der Zuweisung kommt es zum Überlauf. OCR3A hat nur den uint16_t Wertebereich.

1
OCR3A = val;

Das muss nach der Limitüberprüfung zugewiesen werden. Nicht davor.

Hier ist eine doppelte Addition drin.

1
val += val +100;

Das macht zum testen eher Sinn. 'DEC' kannste übrigens weglassen.

1
void loop() 
2
{
3
  if( enc_delta == 4 || enc_delta == -4 )  {
4
    val += encode_read(4);          // 4 Signaländerungen pro Rastung
5
    Serial.print  ( "val = " );
6
    Serial.println( val );
7
    val = val + 100;  // Zum Testen
8
    Serial.print  ( "+100 = " );
9
    Serial.println( val );
10
    if( val > 65535 ) val = 65535; 
11
    if( val <     0 ) val = 0; 
12
    Serial.println( "Limits" );  
13
    Serial.print  ( "val = " );
14
    Serial.println( val );
15
    cli( );
16
    OCR3A = val;                    // An Rechteckgenerator Wert übergeben
17
    sei( );
18
    Serial.print  ( "OCR3A = " );
19
    Serial.println( OCR3A );
#7501159
Lesenswert?

Jaja, programmieren mit Unlogik . . . Ich weiß zwar nicht warum ich das hier mache, ich mach's trotzdem. Siehe Anhang.

1
volatile int8_t enc_delta;
2
static uint8_t last;

Diese beiden Variabelen sind TABU!!! Die faßt man an Anwender NIE an! Die einzige Funktion, die man benutzt, ist

int8_t encode_read( uint8_t klicks );

Die liefert die Anzahl der Schritte seit dem letzten Aufruf mit Vorzeichen! Nur DAMIT arbeitet man.

Dann ist es auch leicht, zu erkennen, wann am Rad gedreht wurde. Nämlich dann, wenn der Rückgabewert ungleich 0 ist! WOW! Der Rest ist trivial^2. Oder doch nicht? Naja, man sollte schon VOR der Benutzung einer Variablen deren Grenzen prüfen und nicht DANACH! Du hast es genau anders herum gemacht! (Sind wir überrascht? Nicht wirklich)

Und interen Pull-Ups schaltet man auch anders ein.

Angehängte Dateien:
OP (Firma: Anscheinend Corner-Cases ;-)) #7501428
Lesenswert?

1
    cli( );
2
    OCR3A = val;                    // An Rechteckgenerator Wert übergeben
3
    sei( );

Bin mir gar nicht mehr sicher ob das mit cli und sei überhaupt nötig ist. Und wenn nein - warum ?

Falk B. schrieb:

Ich weiß zwar nicht warum ich das hier mache, ich mach's trotzdem.

Wahrscheinlich, weil du es kannst ;-)

1
case  4: enc_delta = val & 3; val >>= 2; break;

Diese Logik verstehe ich nicht. val ist zu Begin = enc_delta. Man maskiert Bit 0 & 1 aus, dann schiebt diese so, dass enc_delta immer Null ergibt.

1
////////////////////////////////////////////////////////////////////////////////
2
void loop() 
3
{
4
  int8_t delta;
5

6
  delta = encode_read(4);          // 4 Signaländerungen pro Rastung
7
  if( delta != 0 )  {
8
    pwm_wert += delta;
9
    if( pwm_wert > 65535 ) pwm_wert = 65535; 
10
    if( pwm_wert <     0 ) pwm_wert = 0; 
11
    OCR3A = pwm_wert;                    // An Rechteckgenerator Wert übergeben

Warum erzeugt man zwei weitere Variablen ( delta, pwm_wert ) und arbeitet nicht einfach mit val weiter ?

Bernd_Stein

#7501473
Lesenswert?

Bernd S. schrieb:

cli( ); OCR3A = val; // An Rechteckgenerator Wert übergeben sei( ); Bin mir gar nicht mehr sicher ob das mit cli und sei überhaupt nötig ist. Und wenn nein - warum ?

Weil das Register durch den Compiler schon richtig beschrieben wird und in deinem Programm kein Zugriff darauf in einem Interrupt passiert.

Wahrscheinlich, weil du es kannst ;-)

case 4: enc_delta = val & 3; val >>>= 2; break;

Diese Logik verstehe ich nicht. val ist zu Begin = enc_delta. Man maskiert Bit 0 & 1 aus, dann schiebt diese so, dass enc_delta immer Null ergibt.

Das muss du gar nicht verstehen, denn du willst den Drehgeber nur benutzen. Fertig.

//////////////////////////////////////////////////////////////////////////////// void loop() { int8_t delta; delta = encode_read(4); // 4 Signaländerungen pro Rastung if( delta != 0 ) { pwm_wert += delta; if( pwm_wert > 65535 ) pwm_wert = 65535; if( pwm_wert < 0 ) pwm_wert = 0; OCR3A = pwm_wert; // An Rechteckgenerator Wert übergeben Warum erzeugt man zwei weitere Variablen ( delta, pwm_wert ) und arbeitet nicht einfach mit val weiter ?

Weil es so besser ist. Klar kann man sich auch den alten Wert merken und prüfen, ob eine Änderung vorliegt. Ist aber doppelt gemoppelt. Außerdem ist delta eine kleine 8 Bit Zahl, pwm_wert ja nach Anwendung 16 oder 32 Bit. Mehr Speicher, mehr CPU-Last, mehr Aufwand für einen Vergleich.

OP (Firma: Anscheinend Corner-Cases ;-)) #7501633
Lesenswert?

Falk B. schrieb:

Das muss du gar nicht verstehen, denn du willst den Drehgeber nur benutzen. Fertig.

1
case  4: h_encoder_delta = val & 3; val >>= 2; break;

Wär aber schön, denn ich verstehe auch nicht, wieso h_encoder_delta in der ISR nur bis 4 bzw. -4 zählt. Der Ausdruck rechts vom Gleichheitszeichen ergibt ja entweder 1 oder -1. Also wird doch eigentlich h_encoder_delta bis zum Überlauf hoch,- bzw. runtergezählt

Was sorgt dafür, dass h_encoder_delta nur bis 4 hochzählt, wenn doch immer 1 addiert wird ?

1
volatile  int8_t h_encoder_delta;
2

3
h_encoder_delta += (diff & 2) - 1;   // bit 1 = direction (+/-)

Bernd_Stein

#7501655
Lesenswert?

Bernd S. schrieb:

Falk B. schrieb:

Das muss du gar nicht verstehen, denn du willst den Drehgeber nur benutzen. Fertig. case 4: h_encoder_delta = val & 3; val >>= 2; break;

Wär aber schön, denn ich verstehe auch nicht, wieso h_encoder_delta in der ISR nur bis 4 bzw. -4 zählt.

Bei jedem Aufruf der Funktion passiert folgendes

h_encoder_delta = val & 3; nur die unteren 2 Bit von h_encoder_delta bleiben übrig, die oberen Bits werden gelöscht. 3 = 0b00000011, siehe Bitmanipulation.

val >>= 2; break; Die Variable val, welche den Wert von h_encoder_delta enthält, wird um 2 Bits nach rechts geschoben. Damit werden die unteren 2 Bits rausgeschoben und die Variable praktisch durch 4 dividiert. Denn genau das will man bei einem Encoder, der 4 elektrische Pulse / mechanischer Raststufe besitzt.

Der Ausdruck rechts vom Gleichheitszeichen ergibt ja entweder 1 oder -1. Also wird doch eigentlich h_encoder_delta bis zum Überlauf hoch,- bzw. runtergezählt

Nö.

Was sorgt dafür, dass h_encoder_delta nur bis 4 hochzählt, wenn doch immer 1 addiert wird ?

Die Tatsache, daß dein Programm die Funktion encode_read(4) sehr oft aufruft, mit mehreren Dutzend kHz. In der Zeit kann sich die Variable gar nicht schneller ändern, denn die ISR, welche die Variable verändert, läuft nur mit 1kHz. In einem richtigen Programm wird die Funktion deutlich langsamer aufgerufen, vielleicht mit 1-10 Hz. In der Zeit kann der Drehgeber aber mehrere Schritte machen. Die werden alle korrekt gezählt.

Mach einfach mal einen Test. Füge ein

delay(1000);

ans Ende deiner Funktion loop() ein und staune.

OP (Firma: Anscheinend Corner-Cases ;-)) #7502014
Lesenswert?

Falk B. schrieb:

Bei jedem Aufruf der Funktion passiert folgendes ...

Danke, dass du es mir doch noch erklärt hast.

Die einfachsten Dinge bereiten mir Probleme, es ist schon fast verflucht. In der rechteckgenerator_init, weise ich OCR3A den Wert 10.000 zu. Es hat aber seltsamerweise den Wert 16. Mache ich dies an einer anderen Stelle, so stimmt alles.

Was läuft hier falsch?

Bernd_Stein

Angehängte Dateien:
#7502025
Lesenswert?

10.000 scheint nicht in ein 8 Bit Register zu passen. 10.000 modulo 256 ergibt 16. Du musst mit den Grundlagen vom ersten Kapitel an anfangen.

Edit: Ok, es ist ein 16 Bit Register. Das ist es nicht. Der Wert steht vermutlich richtig im Register, aber im PrintLn wird der Zugriff nicht richtig gemacht. Auslesen über 16 Bit Hilfsvariable könnte gehen.

(Firma: Gast) #7502033
Lesenswert?

Bernd S. schrieb:

Was läuft hier falsch?

Ich stelle fest: Arduino Mega Also werden alle Timer für 8 Bit PWM vorbereitet und Timer0 ist zusätzlich für millis() und Freunde vorbereitet.

Das kann die lustigsten Fallen stellen!

Darum grundsätzlich die Timer Control Register erst zurücksetzen, bevor man irgend etwas anderes damit anstellt. (Ausnahme Timer0)

Es gab mal einen Thread hier, über das sonst entstehende undokumentierte Durcheinander. Denn, entweder hat der Timer einen Bug, oder das Datenblatt ist unvollständig.

Beispiel:

1
void rechteckgenerator_init ( void )  {
2
  TCCR3A  = 0;               
3
  TCCR3B  = 0; 
4

5
   
6
  TCCR3A  = 1<< COM3A0;                 // OC3A Toggeln
7
  TCCR3B  = 1<< WGM12 | 1<< CS10;   // Mode 4 & Vorteiler = 1
8

9
  OCR3A  = 10000;                         // OCR3A bestimmt die Frequenz
10
         
11
Serial.print  ( "OCR3A init = "  );
12
Serial.println(  OCR3A              );
13

14
    DDRE |= 1<<3;                          // OC3A-Pin als Ausgang konfigurieren
15
}

Ich empfehle diese Reihenfolge einzuhalten.

#7502059
Lesenswert?

Hallo,

man kann nicht einfach wahllos irgendein Register ändern und danach den gesamten Timer neu konfigurieren. Bei Arduino sind die Timer mit PWM Einstellungen für analogWrite vorkonfiguriert. Wenn man noch nicht ganz genau weiß was macht macht, immer erstmal alle relevanten Timer Register reseten. Zuerst TCCRnB, damit steht der Timer und kann keinen Unsinn mehr machen. Dann den Rest reseten, dann neu einstellen und zum Schluss den Prescaler setzen. Dann läuft der Timer sauber an. Wenn der vorher schon läuft und die Konfig noch nicht fertig, kann und wird er Mist machen, auch wenn es ggf. nur kurzeitig ist.

1
void rechteckgenerator_init ( void )  {
2
  // Reset
3
  TCCR3B = 0;             
4
  TCCR3A = 0;
5
  TCNT3  = 0;       
6
  TIMSK3 = 0;
7
  // Neukonfiguration 
8
  TCCR3A = _BV(COM3A0);              // OC3A Toggeln
9
  DDRE   = _BV(3);                   // OC3A-Pin als Ausgang konfigurieren
10
  OCR3A  = 10000;                    // OCR3A bestimmt die Frequenz
11
  TCCR3B = _BV(WGM12) | _BV(CS10);   // Mode 4, Prescaler 1
12
  
13
  Serial.print  ( "OCR3A init = " );
14
  Serial.println(  OCR3A          );
15
}
1
// Timer 1, CTC mode 4, prescaler  64
2
  // Reset
3
  TCCR1B = 0;              
4
  TCCR1A = 0;
5
  TCNT1  = 0;       
6
  TIMSK1 = 0;
7
  // Neukonfiguration 
8
  OCR1A  = (F_CPU * F_TIMER1 / 256UL)-1;
9
  TIMSK1 = _BV(OCIE1A);
10
  TCCR1B = _BV(WGM12) | _BV(CS11) | _BV(CS10);

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren