Hallo
ich habe ein etwas kurioses Problem, bei mir scheint millis() zu spinnen. Irgendwann nach ~1min, Start des Arduino, wird es schneller oder ungenau.
Also ich benutzt einen Arduino Nano(+5V), mit neuem Bootloader. Daran hängen
-
2X Taster
-
2X Status LED
-
2X Fototransistor
-
1X FRAM Chip MB85RC256V
-
1X 0.96 Zoll OLED Display SSD1306 I2C 128x64
Elektrisch und Mechanisch läuft alles.
FRAM und Display laufen über I2C.
Für den FRAM benutzte ich Adafruit_FRAM_I2C.h von Adafruit.
Für den Display verwende ich SSD1306Ascii.h von William Greiman (glaube ich).
Also das Projekt soll am Ende mal ein Betrieb-Stunden/Zeit-Zähler für einen CO2 Lasercutter-[K40] werden.
Kurz: der Arduino stellt über die 2 Fototransistoren fest ob die Laserröhre zündet/strahlt, zählt das in Millisekunden, speichert den Wert im FRAM (hält Werte auch ohne Strom und jede Zelle/Byte kann bis zu 1-Billion Mal beschrieben werden) und zeigt den Wert im Display an.
Das ganze soll einmal die wahre Laufzeit der Röhre dokumentieren, wichtiger ist aber das ich ungefähr zählen kann wie lange der Laser wirklich läuft, damit ich weiß wann ich die Spiegel wieder mal putzen muss.
So nun zu meinem Problem. Mir ist schon früher beim Programmieren und Testen ist mir aufgefallen das die Laufschrift die ich verwende, irgendwann schneller wird. Was mich nicht sonders gestört hat (ist mir auch kaum aufgefallen)
Die Laufschrift wird über eine Schleife und einem millis() Wert gesteuert.
#define DELAY_Display 80 // Aktualisierungsintervall für Bildschirm
void loop() {
if (int_tick_Display <= millis()) {
int_tick_Display = millis() + DELAY_Display;
F_TickerText();
}
...
..
.
void F_TickerText() {
// Should check for error. rtn < 0 indicates error.
rtn = oled.tickerTick(&state);
// See above for definition of RTN_CHECK.
if (rtn <= RTN_CHECK) {
// Should check for error. Return of false indicates error.
oled.tickerText(&state, &str_Text[(n++) % 3]);
}
}
Richtig schwer ist der Fehler jetzt in einer anderen Funktion zu sehen. 1 der Taster ist ein Reset-Taste, setzt den Wert des Speichers auf NULL. (Wenn man mal die Röhre tauscht o.ä.)
Der Taster sitzt auf der Platine und ich hatte mir vorgestellt das man die Taste 1X drückt und dann ein Countdown von 10 bis 0 (in Sekunden) gezählt wird, in der das Programm auf einen Bestätigung (noch mal drücken) zur Bestätigung wartet.
void F_Reset() {
unsigned int int_Delay_Run = 0;
int int_Runs = 10;
oled.clear();
while (digitalRead(PIN_Reset_Switch) == LOW) {
}
do {
if (digitalRead(PIN_Reset_Switch) == LOW) {
while (digitalRead(PIN_Reset_Switch) == LOW) {
}
oled.clear();
oled.set2X();
oled.print("");
oled.println(" Delete!!");
delay(DELAY_Delay * 2);
break;
}
if (int_Delay_Run <= millis()) {
oled.clear();
oled.set1X();
oled.println("Zum loeschen,");
oled.println(" Taste druecken.");
oled.set2X();
oled.print(" ");
oled.println(int_Runs);
int_Delay_Run = millis() + 1001;
int_Runs = int_Runs - 1 ;
}
} while (int_Runs >= 0);
F_Display_Normal();
}
Kurz nach dem Start des Arduino zählt die Schleife auch korrekt die Sekunden runter (es sind genau genommen auch 1Sekunde +1Millisekunde [1001ms]. Nach einer Weile, so ~1 Minute zählt die Schleife aber keine Sekunden mehr, eher 1/2 oder noch weniger. (Es wird wirklich schnell).
Leider weiß ich nicht wie und ob man hier ein Video hochladen kann, sonst würde ich es vorführen.. also müsst ihr mir einfach glauben.
Hat jemand eine Idee wo der Fehler liegen könnte oder wo ich danach suchen soll?
Ich spiele auch nicht mit irgendwelchen tieferen Registern oder so rum, nur einen Hardware Interrupt brauche ich, PIN 3. Er stellt fest ob die Spannungsversorgung ausfällt, damit die letzten Werte in den FRAM geschrieben werden kann. Der Arduino und der FRAM werden währenddessen von 2 größeren Kondensatoren gestützt.
Zum Verständnis hier der aktuelle Code. Er ist A) noch nicht vollständig B) noch in Entwicklung C) in einigen Teilen bestimmt nicht schön!!!!!!
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include "Adafruit_FRAM_I2C.h"
//
#define RTN_CHECK 1
//
#define I2C_ADDRESS_Display 0x3C // I2C Adresse des Displays
#define I2C_ADDRESS_FRAM 0x50 // I2C Adresse des FRAMs
//
#define PIN_Power_Interrupt 3 // Interrupt PIN, falling/fallend, führt FRAM->Speichern aus bevor der Strom weg geht
#define PIN_Reset_Switch 7 // Setzt die Laufzeit auf 0 und speichert sie
#define PIN_Housing_Switch 5 // Schalter für die Abdeckung, für den Fall das es Streulicht gibt.
#define PIN_Fototransistor_1 A3 //
#define PIN_Fototransistor_2 A2 //
#define PIN_LED_Rot 9 // Status LED Grün
#define PIN_LED_Gruen 8 // Status LED Rot
//
#define DELAY_Display 80 // Aktualisierungsintervall für Bildschirm
#define DELAY_Delay 1200 // Verzögerung Schrift
#define DELAY_Fram_Write 1000 // Aktualisierungsintervall für Schreiben in FRAM
#define DELAY_Werte 12000 // Aktualisierungsintervall für Aufbau der Laufschrift-/-String
//
#define FRAM_ADDRESS_J 0x00 // Adresse in FRAM pro Byte
#define FRAM_ADDRESS_T1 0x01 //
#define FRAM_ADDRESS_T2 0x02 //
#define FRAM_ADDRESS_Std 0x03 //
#define FRAM_ADDRESS_Min 0x04 //
#define FRAM_ADDRESS_Sek 0x05 //
#define FRAM_ADDRESS_Ms1 0x06 //
#define FRAM_ADDRESS_Ms2 0x07 //
//
struct Zeit {
byte byt_Jahr;
unsigned int int_Tag;
byte byt_Stunde;
byte byt_Minuten;
byte byt_Sekunde;
unsigned int int_Millisekunde;
};
//
SSD1306AsciiWire oled;
TickerState state;
int8_t rtn ;
unsigned int int_tick_Display;
unsigned int int_tick_Fram_Write;
unsigned int int_tick_Wert;
//
Adafruit_FRAM_I2C fram = Adafruit_FRAM_I2C();
//
int n;
char str_Text[34];
Zeit stru_Zeit = {99, 364, 23, 59, 59, 999};
//
byte byt_firstByte ;
byte byt_secondByte ;
//
void setup() {
rtn = 0 ;
int_tick_Display = 0;
n = 0;
//Eingänge
pinMode(PIN_LED_Rot, OUTPUT);
pinMode(PIN_LED_Gruen, OUTPUT);
//Ausgänge
pinMode(PIN_Power_Interrupt, INPUT);
pinMode(PIN_Reset_Switch, INPUT);
pinMode(PIN_Housing_Switch, INPUT);
pinMode(PIN_Fototransistor_1 , INPUT);
pinMode(PIN_Fototransistor_2 , INPUT);
//Interrupt für Power-Los -> Save to FRAM
attachInterrupt(digitalPinToInterrupt(PIN_Power_Interrupt), F_Interrupt, CHANGE);
//Schalte LED Aus
digitalWrite(PIN_LED_Rot, HIGH);
digitalWrite(PIN_LED_Gruen, HIGH);
//I2C AN
Wire.begin();
Wire.setClock(400000L);
//LCD
oled.begin(&Adafruit128x64, I2C_ADDRESS_Display);
//FRAM
fram.begin(I2C_ADDRESS_FRAM);
//
Serial.begin(115200); //Serial Port mit 115200 Speed
//
F_FRAM_Read();
delay(DELAY_Delay);
//Laufschrift
F_Display_Normal();
}
void loop() {
if (int_tick_Display <= millis()) {
int_tick_Display = millis() + DELAY_Display;
F_TickerText();
}
if (int_tick_Wert <= millis()) {
int_tick_Wert = millis() + DELAY_Werte;
F_MakeString();
}
if (digitalRead(PIN_Reset_Switch) == LOW) {
F_Reset();
}
}
void F_Reset() {
unsigned int int_Delay_Run = 0;
int int_Runs = 10;
oled.clear();
while (digitalRead(PIN_Reset_Switch) == LOW) {
}
do {
if (digitalRead(PIN_Reset_Switch) == LOW) {
while (digitalRead(PIN_Reset_Switch) == LOW) {
}
oled.clear();
oled.set2X();
oled.print("");
oled.println(" Delete!!");
delay(DELAY_Delay * 2);
break;
}
if (int_Delay_Run <= millis()) {
oled.clear();
oled.set1X();
oled.println("Zum loeschen,");
oled.println(" Taste druecken.");
oled.set2X();
oled.print(" ");
oled.println(int_Runs);
int_Delay_Run = millis() + 1001;
int_Runs = int_Runs - 1 ;
}
} while (int_Runs >= 0);
F_Display_Normal();
}
void F_Display_Normal() {
oled.clear();
oled.tickerInit(&state, ZevvPeep8x16, 4, false, 0, 255);
oled.clear();
oled.set1X();
oled.setFont(ZevvPeep8x16);
oled.println(" Betriebszeit");
oled.println();
}
void F_Interrupt() {
F_FRAM_Write();
}
void F_Debug_Serial() {
Serial.print("Jahr = ");
Serial.println(stru_Zeit.byt_Jahr);
Serial.print("Tag = ");
Serial.println(stru_Zeit.int_Tag);
Serial.print("Stunde = ");
Serial.println(stru_Zeit.byt_Stunde);
Serial.print("Minute = ");
Serial.println(stru_Zeit.byt_Minuten);
Serial.print("Sekunde = ");
Serial.println(stru_Zeit.byt_Sekunde);
Serial.print("Millisekunde = ");
Serial.println(stru_Zeit.int_Millisekunde);
}
void F_FRAM_Read() {
detachInterrupt(digitalPinToInterrupt(PIN_Power_Interrupt));
//
oled.clear();
oled.setFont(ZevvPeep8x16);
oled.println();
oled.println(" Lese FRAM");
oled.println();
delay(DELAY_Delay);
//
stru_Zeit.byt_Jahr = fram.read(FRAM_ADDRESS_J);
//
byt_firstByte = fram.read(FRAM_ADDRESS_T1);
byt_secondByte = fram.read(FRAM_ADDRESS_T2);
stru_Zeit.int_Tag = int(byt_firstByte << 8) + int(byt_secondByte);
byt_firstByte = 0;
byt_secondByte = 0;
//
stru_Zeit.byt_Stunde = fram.read(FRAM_ADDRESS_Std);
stru_Zeit.byt_Minuten = fram.read(FRAM_ADDRESS_Min);
stru_Zeit.byt_Sekunde = fram.read(FRAM_ADDRESS_Sek);
//
byt_firstByte = fram.read(FRAM_ADDRESS_Ms1);
byt_secondByte = fram.read(FRAM_ADDRESS_Ms2);
stru_Zeit.int_Millisekunde = int(byt_firstByte << 8) + int(byt_secondByte);
byt_firstByte = 0;
byt_secondByte = 0;
oled.clear();
oled.setFont(ZevvPeep8x16);
oled.println();
oled.println(" FERTIG!");
oled.println();
delay(DELAY_Delay * 2);
attachInterrupt(digitalPinToInterrupt(PIN_Power_Interrupt), F_Interrupt, CHANGE);
}
void F_FRAM_Write() {
detachInterrupt(digitalPinToInterrupt(PIN_Power_Interrupt));
byt_firstByte = 0;
byt_secondByte = 0;
//
fram.write(FRAM_ADDRESS_J, stru_Zeit.byt_Jahr);
//
byt_firstByte = byte(stru_Zeit.int_Tag >> 8);
byt_secondByte = byte(stru_Zeit.int_Tag & 0x00FF);
fram.write(FRAM_ADDRESS_T1, byt_firstByte);
fram.write(FRAM_ADDRESS_T2, byt_secondByte);
//
fram.write(FRAM_ADDRESS_Std, stru_Zeit.byt_Stunde);
fram.write(FRAM_ADDRESS_Min, stru_Zeit.byt_Minuten);
fram.write(FRAM_ADDRESS_Sek, stru_Zeit.byt_Sekunde);
//
byt_firstByte = 0;
byt_secondByte = 0;
byt_firstByte = byte(stru_Zeit.int_Millisekunde >> 8);
byt_secondByte = byte(stru_Zeit.int_Millisekunde & 0x00FF);
fram.write(FRAM_ADDRESS_Ms1, byt_firstByte);
fram.write(FRAM_ADDRESS_Ms2, byt_secondByte);
attachInterrupt(digitalPinToInterrupt(PIN_Power_Interrupt), F_Interrupt, CHANGE);
}
void F_TickerText() {
// Should check for error. rtn < 0 indicates error.
rtn = oled.tickerTick(&state);
// See above for definition of RTN_CHECK.
if (rtn <= RTN_CHECK) {
// Should check for error. Return of false indicates error.
oled.tickerText(&state, &str_Text[(n++) % 3]);
}
}
void F_MakeString() {
n = 0;
//stru_Zeit.int_Jahr
// 1 -> 99
//stru_Zeit.int_Tag
// 1 -> 99
//stru_Zeit.byt_Stunde
// 1 -> 23
//stru_Zeit.byt_Minuten
// 1 -> 59
//stru_Zeit.byte_Sekunde
// 1 -> 59
//stru_Zeit.int_Millisekunde
// 1 -> 999
//" J99:T365:Std24:Min59:Sek59:Ms999 ";
if (stru_Zeit.byt_Jahr != 0) {//1
sprintf(str_Text, " J%02d:T%03d:Std%02d:Min%02d:Sek%02d:Ms%03d ", stru_Zeit.byt_Jahr, stru_Zeit.int_Tag , stru_Zeit.byt_Stunde , stru_Zeit.byt_Minuten , stru_Zeit.byt_Sekunde , stru_Zeit.int_Millisekunde);
}
else if (stru_Zeit.int_Tag != 0) {//2
sprintf(str_Text, " T%03d:Std%02d:Min%02d:Sek%02d:Ms%03d ", stru_Zeit.int_Tag , stru_Zeit.byt_Stunde , stru_Zeit.byt_Minuten , stru_Zeit.byt_Sekunde , stru_Zeit.int_Millisekunde);
}
else if (stru_Zeit.byt_Stunde != 0) {//3
sprintf(str_Text, " Std%02d:Min%02d:Sek%02d:Ms%03d ", stru_Zeit.byt_Stunde , stru_Zeit.byt_Minuten , stru_Zeit.byt_Sekunde , stru_Zeit.int_Millisekunde);
}
else if (stru_Zeit.byt_Minuten != 0) {//4
sprintf(str_Text, " Min%02d:Sek%02d:Ms%03d ", stru_Zeit.byt_Minuten , stru_Zeit.byt_Sekunde , stru_Zeit.int_Millisekunde);
}
else if (stru_Zeit.byt_Sekunde != 0) {//5
sprintf(str_Text, " Sek%02d:Ms%03d Sek%02d:Ms%03d ", stru_Zeit.byt_Sekunde , stru_Zeit.int_Millisekunde, stru_Zeit.byt_Sekunde , stru_Zeit.int_Millisekunde);
}
else {//6
sprintf(str_Text, " Ms%03d Ms%03d Ms%03d Ms%03d ", stru_Zeit.int_Millisekunde, stru_Zeit.int_Millisekunde, stru_Zeit.int_Millisekunde, stru_Zeit.int_Millisekunde);
}
}
Es Fehlen noch wichtige Ding, in der Reset Funktion werden zur Zeit nicht mal die Werte geändert!!!
Wäre es nur ein Fehler oder etwas Unschönes im Display, wäre es mir egal. Wenn millis() aber falsche Werte liefert, dann führt das das ganze Projekt (Zeit Zählen) ad absurdum!!!
Ich vermute den Fehler in einer der Bibiotheken und da die für den Display. Ich würde die aber ungerne wechseln, da genau die die coole Laufschrift bietet :: und mir fehlt das Fachwissen die Lib jetzt Zeile für Zeile zuverstehen.
Schon mal Danke fürs Nachdenken usw.