Arduino geignet zum Codewandeln?

Hallo Zusammen
danke für die rege Beteiligung an der Problemlösung.

Mir kommt es aber vor, als ob alle versuchen das Problem zu kompliziert, nämlich über Abtastung des Datenstroms, versuchen zu lösen.
Dafür ist aber der Arduino meiner Meinung nach, Hardwaretechnisch doch zu lagsam.

Auf Anregung von Combie habe ich nämlich versucht ein Minimalprogramm für den UNO zu schreiben, das testhalber nur das Signal erzeugt an einem Ausgang.
Hierbei ist lediglich eine minimale Ausgangs-Impulslänge von 4,5 mys möglich.
Würde ich somit als kürzest mögliche Zykluszeit interpretieren(Hardware-bedingt)
Somit liegt für mich der Verdacht nahe,
das ich da mit Abtast-Methoden nur scheitern kann.

Mein Ansatz basiert ja darauf, mittels Interrupt-Programmierung auf den besagten Auswerte-Trigger-Eingang zu reagieren(rot eingezeichnet im letzten Bild).
Daraufhin würde nur noch der dann aktuelle Wert des Daten-Eingangs erfasst
(H oder L) und zwischengespeichert.
Die Weiter-Verarbeitung kann dann später
erfolgen in der langen Pause, wo ja nix passiert am Trigger-Eingang.

Oder ist eine solche Arbeitsweise mit dem Arduino nicht möglich?

So kenne ich Interrupt-Programm-Verarbeitung von anderen Compuern.
Für Ansätze in dieser Richtung wäre ich
ggF. Dankbar.
Gruß

Wow!
Jetzt bin ich schwer beeindruckt....

:japanese_ogre: :japanese_ogre: :japanese_ogre:

Möchtest Du das mit uns teilen, damit wir es gemeinsam optimieren? Oder möchtest Du meine Baustelle für Anregungen haben?

Baustelle mit von mir nicht angepaßten Kommentaren!
/* Example code with timer intyerrutp that will create an interruption each
    500ms using timer1 and prescalar of 256.
  Calculations (for 500ms):
  System clock 16 Mhz and Prescalar 256;
  Timer 1 speed = 16Mhz/256 = 62.5 Khz
  Pulse time = 1/62.5 Khz =  16us
  Count up to = 500ms / 16us = 31250 (so this is the value the OCR register should have)
*/
int8_t zaehler = 0;
void setup()
{
  DDRB |= (1 << PB5);
  DDRB |= (1 << PB4);

  cli();  // disable all interrupts
  /*1. First we reset the control register to amke sure we start with everything disabled.*/
  TCCR1A = 0;                 // Reset entire TCCR1A to 0
  TCCR1B = 0;                 // Reset entire TCCR1B to 0

  /*2. We set the prescalar to the desired value by changing the CS10 CS12 and CS12 bits. */
  TCCR1B |= B00000001;        //Set CS12 to 1 so we get prescalar 8

  /*3. We enable compare match mode on register A*/
  TIMSK1 |= B00000010;        //Set OCIE1A to 1 so we enable compare match A

  /*4. Set the value of register A to 31250*/
  OCR1A = 30;             //Finally we set compare register A to this value
  sei();  // enable all interrupts
}

ISR(TIMER1_COMPA_vect)
{
  TCNT1  = 0;                  //First, set the timer back to 0 so it resets for next interrupt
  PINB = (1 << PB5);
  if (zaehler == 0)
  {
    PINB = (1 << PB4);
  } else
  if (zaehler == 3)
  {
    PINB = (1 << PB4);
  } else
  if (zaehler == 4)
  {
    PINB = (1 << PB4);
  } else
  if (zaehler == 8)
  {
    PINB = (1 << PB4);
    zaehler = -1;
  }
  zaehler++;
}

void loop() {}

Manchmal verstehe ich @combie nicht, weil er offensichtlich Gehirnwindungen hat, die bei mir nicht implementiert wurden. Aber bei der Vorgehensweise bezüglich Datenspender stimme ich mit ihm überein :slightly_smiling_face:

Das habe ich in #14 gemacht, einschließlich Vergleich mit dem mittleren Zeitwert. Das erspart das Lesen des Eingangs und damit Zeit.

Bist Du sicher? Ich kann mir gut vorstellen Implementierungen zu haben, die bei mir unnütz sind und bei anderen gänzlich fehlen; und damit übersichtlicher...
Aber was red' ich... Ich scheitere ja schon an der Periode.

Sorry aber das schien mir nicht nötig, aber wenn ihr da optimieren könnt, lass ich mich gerne vom Gegenteil überzeugen.
Wie könnt ihr die Hardware-Zykluszeit runterbekommen?
Ich mußte da schon mit Zykluszeiten jonglieren.
Aber vielleicht gibt es ja andere Instruktionen, die das anders handeln.

So sah jedenfalls das Simulationsprogramm aus:

// Simulation Datenfolge zum Decodieren mit Arduino
// Läuft bei mir auf Arduino UNO mit annähernd korrektem Timing
// Die Daten sind in den vorderen 17Bits (incl.Trenn-Zeichen)
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
for (int i = 1; i < 7900; i++) {
switch (i){
  case 1:
  case 5:
  case 10:
  case 14:
  case 18:
  case 23:
  case 27:
  case 31:
  case 35:       //Füllbit
  case 38:
  case 46:
  case 51:  
  case 56:  
  case 60:
  case 65:
  case 70:
  case 74: 
  case 78:       //Füllbit
  case 81:
  case 90:
  case 94: 
  case 98: 
  case 102: 
  case 107: 
  case 112: 
  case 116: 
  case 122: 
  case 125:   
digitalWrite(13,0);
break;
  case 2:      //16.Daten-Bit (Wert 2^15 HSB)
  case 6:      //15.Daten-Bit
  case 11:     //14.Daten-Bit
  case 15:     //13.Daten-Bit  
  case 19:     //12.Daten-Bit  
  case 24:     //11.Daten-Bit
  case 28:     //10.Daten-Bit
  case 32:      //9.Daten-Bit 
  case 37:       //Füllbit
  case 43:      //8.Daten-Bit
  case 47:      //7.Daten-Bit
  case 52:      //6.Daten-Bit   
  case 57:      //5.Daten-Bit  
  case 61:      //4.Daten-Bit
  case 66:      //3.Daten-Bit
  case 71:      //2.Daten-Bit
  case 75:      //1.Daten-Bit (Wert 2^0 LSB)  
  case 80:      //Füllbit
  case 86:      //Impulse ab hier Daten nicht relevant
  case 91:
  case 95: 
  case 99: 
  case 103: 
  case 108: 
  case 113: 
  case 117: 
  case 124: 
  case 128: 
digitalWrite(13,1);
break;
}
   }
}

Korrektur!

Vermutlich nur über Timer-Interrupt.

Von meiner Baustelle (mit alten Kommentaren):

Oben Pin 12, unten 13.

Bei Prescaler 1 und OCR1A 30 ist noch etwas Spielraum :slightly_smiling_face:

digitalWrite ( ebenso wie digitalRead ) sind schön, wenn man Zeit genug hat. Wenn es 50 fach schneller gehen soll, darf man nicht jedesmal aus einer Arduino-Pin Nummer das zugehörige Hardware-Register und Bit ausrechnen.

Bei AVR Controllern sind z.B. CombiePins erstaunlich schnell.

Genügt nicht auch PINB = (1 << PB4);, oder ist das auch langsam?

Das ist genau das, was meine Lib auch im schnellsten Fall tut.
Und Nein, schneller gehts nicht.

Danke!

Meinen Versuch von #23 habe ich nicht verbessern können, weshalb ich mit ESP32 und Teensy 3.2 probiert habe:

Signalsimulation mit ESP32
// Quelle: Faster ESP32  https://www.instructables.com/Faster-ESP32/
#define TAKT 12
#define LED 13
//#define TAKT uint32_t(1 << 12)
//#define LED uint32_t(1 << 13)

hw_timer_t *My_timer = NULL;
const uint32_t daten = 0b1111111101010011;
byte pos = 0;
bool hl = LOW;

void IRAM_ATTR onTimer() {
  if (hl)
  {
    digitalWrite(TAKT, LOW);
    digitalWrite(LED, HIGH);
    //GPIO.out_w1tc = TAKT;
    //GPIO.out_w1ts = LED;
    if (daten & (1 << pos))
    {
      timerAlarmWrite(My_timer, 26, true);
    } else {
      timerAlarmWrite(My_timer, 18, true);
    }
    pos++;
    if (pos >= 16)
    {
      digitalWrite(TAKT, HIGH);
      //GPIO.out_w1ts = TAKT;
      timerAlarmWrite(My_timer, 2400, true);
      pos = 0;
    }
  } else {
    digitalWrite(LED, LOW);
    //GPIO.out_w1tc = LED;
    timerAlarmWrite(My_timer, 1, true);
  }
  hl = !hl;
}

void setup() {
  pinMode(TAKT, OUTPUT);
  pinMode(LED, OUTPUT);
  /*gpio_config_t io_conf_TAKT;
  io_conf_TAKT.mode = GPIO_MODE_OUTPUT;
  io_conf_TAKT.pin_bit_mask = TAKT;
  gpio_config(&io_conf_TAKT);

  gpio_config_t io_conf_LED;
  io_conf_LED.mode = GPIO_MODE_OUTPUT;
  io_conf_LED.pin_bit_mask = LED;
  gpio_config(&io_conf_LED);
  */
  My_timer = timerBegin(0, 40, true);
  timerAttachInterrupt(My_timer, &onTimer, true);
  timerAlarmWrite(My_timer, 3, true);
  timerAlarmEnable(My_timer); //Just Enable
}

void loop() {}
Signalsimulation mit Teensy 3.2
#include "Arduino.h"
#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;
OneShotTimer timer1; // generate a timer from the pool (Pool: 2xGPT, 16xTMR(QUAD), 20xTCK)

#define TAKT 12
#define LED 13
const uint32_t daten = 0b1111111101010011;
byte pos = 0;
bool hl = LOW;

void callback()
{
  if (hl)
  {
    digitalWriteFast(TAKT, LOW);
    digitalWriteFast(LED, HIGH);
    if (daten & (1 << pos))
    {
      timer1.trigger(8);  // 12 µs
    } else {
      timer1.trigger(5);  // 9 µs
    }
    pos++;
    if (pos >= 16)
    {
      digitalWriteFast(TAKT, HIGH);
      timer1.trigger(500);
      pos = 0;
    }
  } else {
    digitalWriteFast(LED, LOW);
    timer1.trigger(1);
  }
  hl = !hl;
}

void setup()
{
  pinMode(TAKT, OUTPUT);
  pinMode(LED, OUTPUT);
  timer1.begin(callback);
  timer1.trigger(1);
}

void loop() {}
Signalauswertung mit UNO
#define DATA_LOW 9
#define DATA_HIGH 12.5
#define TRENNZEICHEN 6  // 3.5
const uint32_t SCHWELLE = ((DATA_HIGH - DATA_LOW) / 2 + DATA_LOW + TRENNZEICHEN + 0.5) * 16;
const uint32_t PAUSE = 60 * 16;

const byte inPin = 2;

void setup()
{
  Serial.begin(500000);
  Serial.println(F("\nStart"));
  Serial.println(SCHWELLE);
  pinMode(inPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(inPin), ISR_Daten, FALLING);
  cli();                  // disable all interrupts
  TCCR1A = 0;             // 1. Reset the control register to make sure we start with everything disabled.
  TCCR1B = 0;
  TCCR1B |= B00000001;    // 2. We set the prescalar to the desired value by changing the CS10 CS12 and CS12 bits.
  TIMSK1 |= B00000010;    // 3. We enable compare match mode on register A
  OCR1A = 65000;          // 4. Set the value of register A
  sei();                  // enable all interrupts
}

byte daten[] = {0, 0, 0, 0, 0, 0, 0, 0};
static bool sync = false;
bool hl = HIGH;

void loop() {}

ISR(TIMER1_COMPA_vect)
{
  Serial.println("Überlauf");
  TCNT1 = 0;
}

void ISR_Daten()
{
  static byte bitNum = 0;

  if (TCNT1 > PAUSE)
  {
    bitNum = 0;
    sync = true;
  } else {
    if (TCNT1 > SCHWELLE)
    {
      daten[bitNum] = 1;
    } else {
      daten[bitNum] = 0;
    }
    if (bitNum < sizeof(daten) - 1)
    {
      bitNum++;
    } else {
      if (sync)
      {
        for (byte j = 0; j < sizeof(daten); j++)
        {
          Serial.print(daten[j]);
          daten[j] = 0;
        }
        Serial.println();
      }
      bitNum = 0;
    }
  }
  TCNT1 = 0;
}

ESP32 und Teensy blieben leider unter meinen Erwartungen. Das Trennzeichen war immer etwas zu lang, unter 6 µs habe ich es nicht bekommen. Auch "zappelten" die Flanken. Da müßte man eventuell mehr Zeit investieren. Letztlich habe ich den ESP32 ausgewählt.

Bei der Auswertung hatte ich erwartungsgemäß mit micros() keinen Erfolg, weshalb ich OCR1A von Timer 1 zum Zählen verwendet habe. Der Endwert von 65000 sollte ebenso wie ISR(TIMER1_COMPA_vect) nur bei ausbleibendem Signal erreicht werden, da Signalflanken den Interrupt für die Interrupt Service Routine ISR_Daten() auslösen, wo der Zähler OCR1A zurück auf 0 gesetzt wird. Die "magischen" 16 in der Zeitberechnung ergeben sich aus 16 Takten je µs.

Die Pause zwischen den Daten habe ich lang genug gewählt, um mit Serial.print eine Anzeige realisieren zu können.

Das Programm soll eine Anregung sein, denn zusammen mit den realen Daten erwarte ich weitere Herausforderungen.

Die Programme habe ich für mich zum Lernen geschrieben, sollte jemand grobe Schnitzer finden, bitte ich freundlich um Benachrichtigung.

Hallo agmue

Super vielen Dank für die intensive Unterstützung.
Hat mir erstmal den möglichen Weg aufgezeigt.
Bei deinen Beispielen ist mir die Idee gekommen,
mich evtl. doch gleich mit einem schnelleren Board weiterzubeschäftigen, auch wenn ich derzeit noch z.B. keinen Teensy habe.
Ich habe mich um das Teensy-Angebot gekümmert und habe hierzu eine Frage.
Da der Teensy 3.2 bei meinem Liferanten nicht verfügbar ist
stehen dort Teensy4.0 oder 4.1 zur Auswahl.
Sind die komplexer zu programmieren oder auch mit der Arduino-IDE?
Ich möchte verhindern, mich mit den "langsameren" abmühen zu müssen, wenn mit den schnelleren Boards die Lösung "easy" wäre.
Oder ist zu Befüchten, das ich mir da neue Baustellen einhandle?
Gruß

In der Arduino IDE macht es keinen großen Unterschied, ob du mit einem 3.x Teensy oder einem 4.x arbeitest. In der entsprechenden Erweiterung sind alle Typen enthalten.

Ob die 3.x noch mal lieferbar werden, ist abzuwarten. Der Chiphersteller benutzt die Produktionslinien wohl für ertragreichere Dinge. Eher sind wohl weitere 4.x Typen zu erwarten, sobald die nächste Generation vom verwendeten Controller in Stückzahlen lieferbar ist.

Eine Alternative für deine Anwendungen könnte auch ein Board mit dem Raspberry Pico Controller sein, also der Arduino Nano 2040 oder der originale Raspberry Pi Pico. Da drauf gibt es spezielle programmierbare IO Einheiten, die mit einem speziellen Assembler programmiert werden. Dazu gibt es diverse Doku im Netz, auch z.B. auf Youtube. Eventuell könnte man die dazu bringen, so einen Code zu lesen.

Es gibt eine alternative Arduino Erweiterung für die RP2040 Controller
Arduino Pico Core
Die setzt, im Gegensatz zur offiziellen Arduino Toolchain, direkt auf dem Pico SDK auf, statt auf mbed OS.

Da werden die PIOs z.B. benutzt um sehr viele SoftwareSerial in Hardware umzusetzen. Auch der PIO Assembler ist da wohl dabei. Mir fehlte bisher nur die Zeit, mir das mal intensiver anzusehen.

[Edit]
Ich habe schon gesehen, dass Leute ein CAN Interface mit den Pico PIOs implementiert haben. Es ist also Potential da, recht komplexe serielle Protokolle damit umzusetzen.

Nachtrag zum UNO: Das Makro ISR hat einen Parameter, der nach meinem Verständnis mittels ISR_NAKED die Datensicherung und damit Programmcode einspart. Muß man sicherlich mit Bedacht einsetzen, könnte aber eine kleine Beschleunigung bringen.

Den Teensy 3.2 habe ich vor mehreren Jahren zwecks LED-Animation mit APA102 Lichtbändern erworben. Die Integration in die Arduino-IDE ist m. E. gut gelungen. Grundsätzlich sollte das auch für die Teensy 4.x gelten, nur fehlt mir jegliche Erfahrung, da ich auch aus Kostengründen zum ESP32 gewechselt bin. Der kann auch WLAN, weshalb ich per Funk die Animation wechseln kann.

Für Deine Anwendung dürfte WLAN eher störend sein, funkt Dir im wahrsten Sinne des Wortes dazwischen.

Auch mit der Arduino-IDE läßt sich STM32 programmieren, ohne Funk aber schnell. Auch damit fehlt mir die Erfahrung, habe nur mal hier im Forum auch hinsichtlich LED-Animatioen davon gelesen.

Auch gibt es in der Arduino-Familie schnelle Hardware, allerdings lese ich im deutschsprachigen Teil dieses Forum eher nichts davon. Was in den englischsprachigen Teilen so los ist, lese ich nicht mit.

Neue Baustellen sind Teensy, ESP und STM32 auf jeden Fall. Den ESP32 habe ich jetzt ca. drei Jahre am Wickel und finde immer noch neue Ecken. Wenn es um das Einlesen der Daten geht, ist die Einarbeitung natürlich viel fixer zu erledigen. Aber das liegt auch ein Stück weit an Dir, mit welchen Voraussetzungen Du rangehst. Ich bin nur Hobbyprogrammierer :slightly_smiling_face: