Digitaler Filter für Messuhrsignale

Moin zusammen,

nachdem ich mit Hilfe des Forums eine Ausleseroutine für digitale Messuhren hinbekommen und ein wenig flotter gemacht habe, habe ich nun ein weiteres Problem.

Erstmal mein Code:

/*
 * Data Logger for digital dial gauges.
 * Up to 4 gauge signals can be connected and interpreted.
 * Access to signals will be achieved via direct port manipulation
 * to increase speed and efficiency.
 */

#include <SPI.h>
#include "SdFat.h"

// SD chip select pin
const uint8_t chipSelect = 4;

// Log file base name
#define FILE_BASE_NAME "LOG"
//=======================================================
// File system object
SdFat sd;

// Log file
SdFile file;

// Time in millis for next data record
uint32_t logTime;
//======================================================
// User functions.
//------------------------------------------------------
int i;
int sign;
long value;
int result;
unsigned long tempmicros;

const uint8_t GAUGE_COUNT = 5;
const uint8_t DATA_COUNT = 4;
int RESULTS[DATA_COUNT];

//======================================================
// Write data header
void writeHeader() {
  file.print(F("MILLIS"));
  for (uint8_t i = 1; i < GAUGE_COUNT; i++) {
    file.print(F(",MESSUHR_"));
    file.print(i, DEC);
  }
  file.println();
}
//======================================================
//Funktion zum Decodieren der Messuhr-Signale
int decode(int x, int y, volatile uint8_t *port) {
  sign=1;
  value=0;
  for (i=0;i<23;i++) {
    while ((*port & (1<<x))) {} //wait until clock returns to HIGH- the first bit is not needed
    while ((*port & (1<<x)) == 0) {} //wait until clock returns to LOW
    if ((*port & (1<<y)) == 0){
      if (i<20) {
        value|= 1<<i;
      }
      if (i==20) {
        sign=-1;
      }
    }
  }
  result=(value*sign);    
  return result;
}
//======================================================
// Log a data record
void logData() {
  int data[DATA_COUNT];
  
  for (uint8_t i = 0; i < DATA_COUNT; i++) {
    data[i] = RESULTS[i];
  }
  // Write data to file.  Start with log time in millis.
  file.print(logTime);

  // Write GAUGE data to CSV record
  for (uint8_t i = 0; i < DATA_COUNT; i++) {
    file.write(',');
    file.print(data[i]);
  }
  file.println();
}
//===================================================
// Error messages stored in flash.
#define error(msg) sd.errorHalt(F(msg))
//===================================================

void setup() {
//CLK und DATA-Pins für Messuhren belegen
  DDRD &= ~B11001100;   //D2,3,6,7 als INPUT gesetzt im Data Direction Register
  PORTD |= B11001100;   //D2,3,6,7 auf HIGH (Pullup-Widerstände) im PORT Register
  DDRB &= ~B00000011;   //D8,9 als INPUT gesetzt Data Direction Register
  PORTB |= B00000011;   //D8,9 auf HIGH (Pullup-Widerstände) im PORT Register

  const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
  char fileName[13] = FILE_BASE_NAME "00.csv";

  //Serial.begin(9600);

  // Initialize at the highest speed supported by the board that is
  // not over 50 MHz. Try a lower speed if SPI errors occur.
  if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
    sd.initErrorHalt();
  }
  
  // Find an unused file name.
  if (BASE_NAME_SIZE > 6) {
    error("FILE_BASE_NAME too long");
  }
  while (sd.exists(fileName)) {
    if (fileName[BASE_NAME_SIZE + 1] != '9') {
      fileName[BASE_NAME_SIZE + 1]++;
    } else if (fileName[BASE_NAME_SIZE] != '9') {
      fileName[BASE_NAME_SIZE + 1] = '0';
      fileName[BASE_NAME_SIZE]++;
    } else {
      error("Can't create file name");
    }
  }
  if (!file.open(fileName, O_CREAT | O_WRITE | O_EXCL)) {
    error("file.open");
  }

  // Write data header.
  writeHeader();
}
  
void loop() {
  //Prozedur zur Auswertung der Messuhrsignale (MESSUHR1)
  while ((PIND & (1<<PD2))) {} //if clock is LOW wait until it turns to HIGH
  tempmicros=micros();
  while ((PIND & (1<<PD2)) == 0) {} //wait for the end of the HIGH pulse
  if ((micros()-tempmicros)>500) { //if the HIGH pulse was longer than 500 micros we are at the start of a new bit sequence
  RESULTS[0] = decode(PD2,PD3,&PIND); //decode the bit sequence
  }

  //Prozedur zur Auswertung der Messuhrsignale (MESSUHR2)
  while ((PIND & (1<<PD6))) {} //if clock is LOW wait until it turns to HIGH
  tempmicros=micros();
  while ((PIND & (1<<PD6)) == 0) {} //wait for the end of the HIGH pulse
  if ((micros()-tempmicros)>500) { //if the HIGH pulse was longer than 500 micros we are at the start of a new bit sequence
  RESULTS[1] = decode(PD6,PD7,&PIND); //decode the bit sequence
  }

//Prozedur zur Auswertung der Messuhrsignale (MESSUHR3)
  while ((PINB & (1<<PB0))) {} //if clock is LOW wait until it turns to HIGH
  tempmicros=micros();
  while ((PINB & (1<<PB0)) == 0) {} //wait for the end of the HIGH pulse
  if ((micros()-tempmicros)>500) { //if the HIGH pulse was longer than 500 micros we are at the start of a new bit sequence
  RESULTS[2] = decode(PB0,PB1,&PINB); //decode the bit sequence
  }
 
  logTime = millis();
  /*Serial.print(logTime);
  Serial.print("\t");
  Serial.print(RESULTS[0]);
  Serial.print("\t");
  Serial.print(RESULTS[1]);
  Serial.print("\t");
  Serial.println(RESULTS[2]);*/
  logData();  
  

  // Force data to SD and update the directory entry to avoid data loss.
  if (!file.sync() || file.getWriteError()) {
    error("write error");
  }
}

Das Problem, welches nun auftritt ist, dass ab und zu Störsignale bzw. falsche Werte ausgelesen werden.
Ich habe die Schaltung mit den 3 Messuhren mal laufen lassen ohne die Messköpfe zu bewegen. Überall soll also die 0 stehen.

Tatsächlich sind dabei auf die SD Karte einige wenige Messpunkte geschrieben worden, die immer 2er-Potenzen darstellten. Es wurde also beim Auslesen irgendwo ein Bit HIGH gelesen, obwohl es LOW war.

Ich bin auf diese Doku gestoßen:
http://www.shumatech.com/support/chinese_scales.htm

Dort sind “Glitches” dargestellt. Ich könnte mir vorstellen, dass dies ein Grund für die falschen Werte bei mir sein könnte.
Ebenfalls wird auf digitales Filtern mittels “triple sample and majority average” verwiesen.
Da habe ich überhaup keine Erfahung. Kann mir jemand helfen, wie ich das implementieren könnte?

So wie ich das verstehe empfiehlt der Autor 3 Abtastungen der Datenleitung nach der fallenden Taktflanke, und dann eine Mehrheitsentscheidung für das zu speichernde Bit. Wenn genügend Zeit bleibt, könnte man bei Uneinigkeit nochmal 3 Werte lesen und wenn die immer noch nicht einheitlich sind, die ganze Messung wegwerfen und auf die nächste Übertragung warten.

Aaaaaaah ding ding ding, ich konnte mit diesem "majority average" nichts anfangen. Also frage ich direkt drei mal in Folge ab und wenn zwei mal 0 und einmal 1 dann 0 und andersherum, right?

Ich versuch das morgen mal zu machen und melde mich bei Bedarf nochmal :) Vielen Dank schonmal vorab.

Ich habe auch nur geraten ;-)

Erscheint aber auf jeden Fall sinnvoll :)

Ich hab es jetzt pragmatisch gelöst, indem ich zwei mal in Folge die Datenleitung abfrage mit einer Verzögerung dazwischen. Habe ein bisschen rumgespielt und bekomme mit 20 Mikrosekunden ein Verzögerung ein sauberes Signal bei zwei von drei Schaltungen. Die dritte scheint von der Hardware her nicht sauber zu sein und darüber Störsignale einzuspeisen, da ich alle Uhren an allen Schaltungen ausprobiert habe und nur bei der dritten Schaltung gibt es Probleme.

Die schaue ich mir gesondert nochmal an oder verlöte evtl. neu. Ansonsten funktioniert es soweit wie gewollt :)

Spikes kommen oft von unsauberer Stromversorgung, ein paar Stützkondensatoren können da Wunder wirken. Schau mal nach Leerstellen auf der fehlerhaften Platine.

Was genau meinst du mit Leerstellen?

Die Uhren hängen alle parallel an einer Batterie, die Versorgung sollte für alle also gleich sein.

Hi

Leerstellen: fehlende Bauteile auf der Platine, Die Probleme bereitet - ggf. wurde hier nur ein Kondensator 'vergessen'.

MfG

Kondensatoren sind gar nicht im Schaltkreis mit drin.
Ich schaue mal nach unsauberen Lötstellen.

Im Zweifel mach ich die Platine einfach neu, wollte ohnehin noch 1,2 Änderungen einbringen.