De software code van keerlusmodule is te vinden in het volgende bestand (download): Keerlusprogramma.txt
Het ontwerp is eigenlijk eenvoudig. Alle in en uitgangen zijn op één van de I/O pinnen van de Nano aangesloten. Door steeds weer de status van de ingangen te lezen kan op basis van de detecties de stand van het relais bepaald worden. Dan kan het relais al of niet bekrachtigd worden via een I/O pin. Daarnaast moet een melding op de detectie ingang A of E aan Koploper worden gestuurd. Hier voor worden de terugmeld uitgangen even hoog gemaakt (een puls).
De test knoppen worden gelezen en simuleren een detectie op de A of E ingang. De rest van de LED's is in feite verfraaiing (handig voor testen).
De hardware opzet is simpel, er zijn geen speciale software bibliotheken nodig.
De structuur van Arduino programma is altijd hetzelfde:
Hieronder de definitie van alle I/O toewijzingen. Er zijn vele manieren van programmeren. Ik vind het belangrijk dat de code gestructureerd en leesbaar is.
Een paar regels:
Hieronder de gebruikte variabelen. Ze kunnen bij de declaratie gelijk geïnitialiseerd worden maar dit kan ook in de Setup procedure bij opstart van het programma.
Voor alle 'basis' handelingen maak ik altijd een procedure. Hier binnen wordt de functionaliteit geïmplementeerd. Hierdoor wordt het hoofdprogramma zo overzichtelijk mogelijk gehouden. Op deze manier documenteer je het ook programma.
Timers moet je realiseren in de Loop omdat dat de enige procedure is die constant wordt aangeroepen. De beste techniek omdat te doen heb ik beschreven op een aparte pagina Timers in Arduino.
Hieronder een overzicht van de procedures met een korte toelichting.
void LeesInverteerRichting()
// Lees de toestand van de strap waarmee de uitgang
// van het relais omgekeerd kan worden.
void LeesDetectieInputs()
// Lees de status van de 4 Hall sensors A B D en E
// Deze zijn aangesloten op analoge ingangen.
void LeesTestButtons()
// Leest de toestand van de test druktoetsen voor
// het handmatig omkeren van de rijrichting op de keerlus.
void HandleBasicTimer()
// Wordt van uit de Loop elke 10ms (instelbaar) aangeroepen
// en voert de volgende procedures uit:
LeesInverteerRichting();
LeesDetectieInputs();
LeesTestButtons();
void HandleOnStateTimers()
// Wordt vanuit de Loop elke 10ms (instelbaar) aangeroepen
// nadat eerst de HandleBasicTimer() is uitgevoerd
// Elke detectie A, B, D of E wordt ge-latched, vast gehouden voor
// een minimum tijd. Deze procedure handelt dit af.
void HandleActionTimer()
// Wordt elke 100ms aangeroepen
// Handelt de aansturing van de LED's en de S88-melding
// en de toestand van het relais:
ZetMeldingLed(A, TimerOnState_A);
ZetMeldingLed(B, TimerOnState_B);
ZetMeldingLed(D, TimerOnState_D);
ZetMeldingLed(E, TimerOnState_E);
ZetS88TerugMelding(S88MeldingTimer);
ZetRichtingsLed(richting);
ZetRelais(richting);
void HandleKnipperTimer()
// Zorgt voor het knipperen van de on-board LED
// en de groene LED op de printplaat
void ZetRelais(Richting richting)
// Zorgt voor de aansturing van het relais (zijn er in feite 2 parallel
// Handelt ook het eventueel inverteren af
void ZetMeldingLed(Sectie sectie,int detectieTimer)
// Stuurt één van de 4 detectiemeldings LED's aan.
void ZetS88TerugMelding(Richting richting, int detectieTimer)
// Stuurt afhankelijk van de rijrichting richting één van de 2
// terugmeldingen A of E aan.
void ZetRichtingsLed(Richting richting)
// Stuurt de 2 richtings LED's aan (Rood en Groen)
void loop()
// Wordt constant door de Arduino kernel aangeroepen
// Handelt alle zaken af van input, verwerking en aansturing.
// Zorgt voor de timing.
// Roept de volgende procedures aan:
HandleBasicTimer();
HandleOnStateTimers();
HandleActionTimer();
HandleKnipperTimer();
void setup()
// Alle initialisatie die moet gebeuren voordat het programma (de Loop) start.
I/O pinnen moeten nog worden ingesteld
Initialisatie van de timers:
BasicTimerValue = 10; // in ms
OnStateTimerValue = 100; // Count down voor signaal ON state (in basic timer intervals, dus 1000ms)
ActionTimerValue = 100; // in ms
KnipperTimerValue = 500; // in ms
//
// IO pin definities
//
const byte PIN_LED_TEST_RICHTING_ROOD = 2; // handmatig richting rood
const byte PIN_LED_TEST_RICHTING_GROEN = 3; // handmatig richting groen
const byte PIN_LED_INDICATOR_RICHTING_GROEN = 4; // richting is groen
const byte PIN_LED_INDICATOR_RICHTING_ROOD = 5; // richting is rood
const byte PIN_LED_MELDING_SECTIE_A = 6; // Detectie A
const byte PIN_LED_MELDING_SECTIE_B = 7; // Detectie B
const byte PIN_LED_MELDING_SECTIE_D = 8; // Detectie D
const byte PIN_LED_MELDING_SECTIE_E = 9; // Detectie E
// Terugmelding S88 bus
const byte PIN_TERUGMELDING_SECTIE_A = 11; // Detectie A
const byte PIN_TERUGMELDING_SECTIE_E = 10; // Detectie E
// Aansturing relais
const byte PIN_RELAIS_AANSTURING = 12; // Detectie A
const byte PIN_ONBOARD_LED_D13 = 13;
//
// Hall sensor inputs (pullup is ook external)
//
const byte ANA_DET_A = A0; // Detectie sectie A
const byte ANA_DET_B = A1; // Detectie sectie B
const byte ANA_DET_D = A2; // Detectie sectie D
const byte ANA_DET_E = A3; // Detectie sectie E
//
// Input voor inversie
//
const byte ANA_INVERTEER_RICHTING = A7;
//
// Enumerations
//
enum Richting { rood = 0, groen = 1 };
enum Sectie { A, B, D, E};
/*
We maken timers op basis van millis()
*/
unsigned long CurrentMillis; // huidige waarde van de millis() timer
unsigned long BasicTimer; // basis milliseconden countdown teller
unsigned long OnStateTimer; // timer voor de aantijd van een detectie
unsigned long ActionTimer; // timer voor afhandeling
unsigned long KnipperTimer; // timer voor knippereren
int BasicTimerValue; // in ms
int OnStateTimerValue; // Count down voor signaal ON state (in basic timer intervals)
int ActionTimerValue; // in ms
int KnipperTimerValue; // in ms
// Detectie inputs
bool Detectie_A = false;
bool Detectie_B = false;
bool Detectie_D = false;
bool Detectie_E = false;
// OnState timer (count down)
int TimerOnState_A = 0;
int TimerOnState_B = 0;
int TimerOnState_D = 0;
int TimerOnState_E = 0;
// S88 melding timer voor vaste lengte van de bezetmelding puls
int S88MeldingTimer = 0;
//
bool InverteerRichting; // met strap mogelijkheid om relais gedrag te inverteren
Richting richting = rood; // Initialiseer de richting op rood
bool KnipperOn = false; // toggle voor knipperen On Board LED
int AnaloogIn; // voor het inlezen van een analoge input
void setup()
{
// Start serial interface
Serial.begin(115200);
Serial.setTimeout(50); // Time out set to 50ms to get faster resonse
Serial.println(F("==================== "));
Serial.println(F("Nano startup"));
Serial.println(F("Versie 1.2 2021-02-13"));
Serial.println(F("- Test LED pin assigment corrected"));
Serial.println(F("- Duration output signal set to 1s (was 2s)"));
/*
I/O pinnen moeten nog worden ingesteld
*/
pinMode(PIN_LED_TEST_RICHTING_ROOD, INPUT_PULLUP);
pinMode(PIN_LED_TEST_RICHTING_GROEN, INPUT_PULLUP);
pinMode(PIN_LED_INDICATOR_RICHTING_GROEN, OUTPUT);
pinMode(PIN_LED_INDICATOR_RICHTING_ROOD, OUTPUT);
pinMode(PIN_LED_MELDING_SECTIE_A, OUTPUT);
pinMode(PIN_LED_MELDING_SECTIE_B, OUTPUT);
pinMode(PIN_LED_MELDING_SECTIE_D, OUTPUT);
pinMode(PIN_LED_MELDING_SECTIE_E, OUTPUT);
pinMode(PIN_TERUGMELDING_SECTIE_A, OUTPUT);
pinMode(PIN_TERUGMELDING_SECTIE_E, OUTPUT);
pinMode(PIN_RELAIS_AANSTURING, OUTPUT);
//
pinMode(PIN_ONBOARD_LED_D13, OUTPUT);
// ingangen voor Hall detectors
pinMode(ANA_DET_A, INPUT_PULLUP);
pinMode(ANA_DET_B, INPUT_PULLUP);
pinMode(ANA_DET_D, INPUT_PULLUP);
pinMode(ANA_DET_E, INPUT_PULLUP);
pinMode(ANA_INVERTEER_RICHTING, INPUT_PULLUP);
/*
* Init de timers
*/
// Timer values
BasicTimerValue = 10; // in ms
OnStateTimerValue =100; // Count down voor signaal ON state (in basic timer intervals)
ActionTimerValue = 100; // in ms
KnipperTimerValue = 500; // in ms
//
LeesInverteerRichting();
}
void loop()
{
CurrentMillis = millis(); // neem eerst een sample van de millis timer
if ((CurrentMillis - BasicTimer) > BasicTimerValue)
{
BasicTimer = CurrentMillis; // Restart timer
HandleBasicTimer();
HandleOnStateTimers();
}
if ((CurrentMillis - ActionTimer) > ActionTimerValue)
{
ActionTimer = CurrentMillis; // Restart timer
HandleActionTimer();
}
if ((CurrentMillis - KnipperTimer) > KnipperTimerValue)
{
KnipperTimer = CurrentMillis; // Restart timer
HandleKnipperTimer();
}
}
void LeesInverteerRichting()
{
AnaloogIn = analogRead(ANA_INVERTEER_RICHTING);
if (AnaloogIn > 512)
{
InverteerRichting = true;
}
else
{
InverteerRichting = false;
}
}
void LeesDetectieInputs()
{
// Aktief als input laag is
// Alleen set van de detectie, reset na afhandeling
AnaloogIn = analogRead(ANA_DET_A);
if (AnaloogIn < 512) {Detectie_A = true;}
AnaloogIn = analogRead(ANA_DET_B);
if (AnaloogIn < 512) {Detectie_B = true;}
AnaloogIn = analogRead(ANA_DET_D);
if (AnaloogIn < 512) { Detectie_D = true; }
AnaloogIn = analogRead(ANA_DET_E);
if (AnaloogIn < 512) { Detectie_E = true; }
}
void LeesTestButtons()
{
if (!digitalRead(PIN_LED_TEST_RICHTING_ROOD))
{
Detectie_A = true;
Serial.println(F("Druktoets rood"));
}
if (!digitalRead(PIN_LED_TEST_RICHTING_GROEN))
{
Detectie_E = true;
Serial.println(F("Druktoets groen"));
}
}
void HandleBasicTimer()
{
LeesInverteerRichting();
LeesDetectieInputs();
LeesTestButtons();
}
void HandleOnStateTimers()
{
// moet de timer (weer) gestart worden?
if (Detectie_A)
{
TimerOnState_A = OnStateTimerValue;
S88MeldingTimer = OnStateTimerValue;
richting = rood;
Detectie_A = false; // reset detectie
}
if (Detectie_B)
{
TimerOnState_B = OnStateTimerValue;
richting = rood;
Detectie_B = false; // reset detectie
}
if (Detectie_D)
{
TimerOnState_D = OnStateTimerValue;
richting = groen;
Detectie_D = false; // reset detectie
}
if (Detectie_E)
{
TimerOnState_E = OnStateTimerValue;
S88MeldingTimer = OnStateTimerValue;
richting = groen;
Detectie_E = false; // reset detectie
}
if (TimerOnState_A > 0) { TimerOnState_A = TimerOnState_A - 1; }
if (TimerOnState_B > 0) { TimerOnState_B = TimerOnState_B - 1; }
if (TimerOnState_D > 0) { TimerOnState_D = TimerOnState_D - 1; }
if (TimerOnState_E > 0) { TimerOnState_E = TimerOnState_E - 1; }
if (S88MeldingTimer > 0) { S88MeldingTimer = S88MeldingTimer - 1; }
}
void HandleActionTimer()
{
//
ZetMeldingLed(A, TimerOnState_A);
ZetMeldingLed(B, TimerOnState_B);
ZetMeldingLed(D, TimerOnState_D);
ZetMeldingLed(E, TimerOnState_E);
//
ZetS88TerugMelding(richting, S88MeldingTimer);
//
ZetRichtingsLed(richting);
ZetRelais(richting);
}
void HandleKnipperTimer()
{
if (KnipperOn)
{
digitalWrite(PIN_ONBOARD_LED_D13, HIGH);
}
else
{
digitalWrite(PIN_ONBOARD_LED_D13, LOW);
}
KnipperOn = !KnipperOn;
}
void ZetRelais(Richting richting)
{
bool AanState = true;
if (InverteerRichting) { AanState = false; }
switch (richting)
{
case rood:
digitalWrite(PIN_RELAIS_AANSTURING, AanState);
break;
case groen:
digitalWrite(PIN_RELAIS_AANSTURING, !AanState);
break;
default:
break;
}
}
void ZetMeldingLed(Sectie sectie,int detectieTimer)
{
bool OnState = false;
if (detectieTimer > 0) { OnState = true; }
switch (sectie)
{
case A:
digitalWrite(PIN_LED_MELDING_SECTIE_A, OnState);
break;
case B:
digitalWrite(PIN_LED_MELDING_SECTIE_B, OnState);
break;
case D:
digitalWrite(PIN_LED_MELDING_SECTIE_D, OnState);
break;
case E:
digitalWrite(PIN_LED_MELDING_SECTIE_E, OnState);
break;
default:
break;
}
}
void ZetS88TerugMelding(Richting richting, int detectieTimer)
{
if (detectieTimer > 0)
{
// melding moet aan
if (richting == rood)
{
digitalWrite(PIN_TERUGMELDING_SECTIE_A, HIGH);
digitalWrite(PIN_TERUGMELDING_SECTIE_E, LOW);
}
else
{
digitalWrite(PIN_TERUGMELDING_SECTIE_A, LOW);
digitalWrite(PIN_TERUGMELDING_SECTIE_E, HIGH);
}
}
else
{
// melding moet uit
digitalWrite(PIN_TERUGMELDING_SECTIE_A, LOW);
digitalWrite(PIN_TERUGMELDING_SECTIE_E, LOW);
}
}
void ZetRichtingsLed(Richting richting)
{
switch (richting)
{
case rood:
digitalWrite(PIN_LED_INDICATOR_RICHTING_ROOD, HIGH);
digitalWrite(PIN_LED_INDICATOR_RICHTING_GROEN, LOW);
break;
case groen:
digitalWrite(PIN_LED_INDICATOR_RICHTING_ROOD, LOW);
digitalWrite(PIN_LED_INDICATOR_RICHTING_GROEN, HIGH);
break;
default:
break;
}
}