Hallo,
kann man alles mit einem Timer machen. 
Auf Grund der sequentiellen Abarbeitung sind zum Bsp. zeitgleiche Einschaltpunkte immer geringfügig versetzt, hier um 4,5µs. Möchte man das noch syncron haben, müßte man direkt auf Registerebene arbeiten und alle Signale auf einen gemeinsamen Port legen. Dann könnte man die Bits entsprechend positionieren woraufhin die Ausgabe automatisch syncron wird. Mehr fällt mir dazu aktuell nicht ein. Ich vermute jedoch das 4,5µs im Audiobereich nicht stören.
Die Zeitbasis pro Timer ISR Count ist aktuell eingestellt auf rechnerisch 80µs.
Resultiert auf Prescaler 64 und CompareMatch 20 was 4µs * 20 ergibt.
Daraus folgt das alle Pulseinstellungen im "Daten struct" Vielfache von 80µs sind.
PERIODENDAUER, pulsTime[ANZAHL] und pulsDelay[ANZAHL] sowie pulsRelativDelay[ANZAHL].
Wobei letzteres aus den anderen berechnet wird.
Das Ergebnis ist wiederum abhängig von der Phaseneinstellung, ob absolut oder relativ zueinander.
Das wird mittels "enum class phase" festgelegt.
bool led.update kann im weiteren Programm genutzt werden um Pulszeitenänderungen erst dann zu erlauben wenn alle Signale einen gemeinsamen Durchlauf absolviert haben. Wenn nicht wichtig werden neue Werte sofort übernommen.
Jetzt muss Matze nur sein(e) Poti(s) auslesen, entsprechend skalieren und ins gewünschte Array schreiben. Entweder led.pulsTime oder led.pulsDelay oder in beide. Wie auch immer.
/*
Doc_Arduino - german Arduino Forum
IDE 1.8.12
avr-gcc 9.2.0
Arduino Mega2560
04.03.2020
License: GNU GPLv3
CombiePin Library hat User combie zur Verfügung gestellt
*/
#include <util/atomic.h>
#include <CombiePin.h>
using namespace Combie::Pin;
OutputPin<41> messPin;
enum class state : byte {RELEASE, DEBUG}; // Serielle aktivieren
const state debugging = state::DEBUG;
enum class phase : byte {ABSOLUT, RELATIV}; // Phasenlage vom Delay festlegen
const phase phaseLage = phase::RELATIV;
class Task // Interface
{
public:
virtual void init() = 0;
virtual void on() = 0;
virtual void off() = 0;
};
template<byte outPin>
class Led: public Task
{
private:
Combie::Pin::OutputPin<outPin> led;
public:
void init() {
led.init();
}
void on() {
led.setHigh();
}
void off() {
led.setLow();
}
};
byte const ANZAHL = 5;
struct Daten
{
unsigned int const PERIODENDAUER = 20; // muss größer gleich der Summe pulsTime + pulsDelay bzw. pulsRelativDelay sein
unsigned int pulsTime[ANZAHL] = {8, 2, 5, 4, 6}; // Pulsdauer
unsigned int pulsDelay[ANZAHL] = {0, 3, 1, 2, 1}; // Pulsverzögerung, abhängig ob Phasenlage absolut oder relativ ist
unsigned int pulsRelativDelay[ANZAHL] = {0}; // Platzhalter, wird berechnet
bool done[ANZAHL] = {false};
unsigned int longestPulsTime = 0;
volatile bool updated = false; // zur freien Verwendung außerhalb der Timer ISR
Task *pin[ANZAHL] = {
new Led<22>(),
new Led<23>(),
new Led<24>(),
new Led<25>(),
new Led<26>(),
};
} led;
void setup(void)
{
if (debugging == state::DEBUG)
{
Serial.begin(250000);
Serial.println("\nStart");
}
messPin.init();
for (Task *i : led.pin) i->init();
if (phaseLage == phase::ABSOLUT)
{
led.longestPulsTime = maxFromArray(led.pulsTime, led.pulsDelay, ANZAHL);
}
if (phaseLage == phase::RELATIV)
{
calcRelativDelay(led.pulsDelay, led.pulsRelativDelay, ANZAHL);
led.longestPulsTime = maxFromArray(led.pulsTime, led.pulsRelativDelay, ANZAHL);
}
if (debugging == state::DEBUG)
{
Serial.print(F("led.longestPulsTime ")); Serial.println(led.longestPulsTime);
Serial.print(F("led.PERIODENDAUER ")); Serial.println(led.PERIODENDAUER);
if (led.longestPulsTime > led.PERIODENDAUER)
{
Serial.println(F("WARNUNG: PERIODENDAUER ist zu kurz!"));
}
}
led.updated = true;
preSetTimer1();
runTimer1();
}
void loop(void)
{
}
// ****** Funktionen ******* //
ISR(TIMER1_COMPB_vect) //
{
messPin.setHigh();
static bool finish = false;
static unsigned int count = 0;
if (!finish) // verkürzt ISR Rechenzeit wenn nichts zu tun ist
{
for (byte i = 0; i < ANZAHL; ++i)
{
// wenn Led innerhalb der Periode noch nicht eingeschalten wurde, dann nach 'pulsDelay' einschalten
if (phaseLage == phase::ABSOLUT)
{
if ( (count >= led.pulsDelay[i]) && (!led.done[i]) ) led.pin[i]->on();
}
if (phaseLage == phase::RELATIV)
{
if ( (count >= led.pulsRelativDelay[i]) && (!led.done[i]) ) led.pin[i]->on();
}
// Led nach Summenwert ausschalten und für die Periode merken
if (phaseLage == phase::ABSOLUT)
{
if (count >= led.pulsTime[i] + led.pulsDelay[i])
{
led.pin[i]->off();
led.done[i] = true;
}
}
if (phaseLage == phase::RELATIV)
{
if (count >= led.pulsTime[i] + led.pulsRelativDelay[i])
{
led.pin[i]->off();
led.done[i] = true;
}
}
}
}
// wenn die längste Zeit von allen Signale abgelaufen ist, dann obige Vergleiche ignorieren
// längste Zeit ist die Summe aus Hightime + absolute/relative Delaytime
if (count >= led.longestPulsTime)
{
finish = true; // verkürzt ISR Rechenzeit wenn nichts zu tun ist
led.updated = true; // zur freien Verwendung außerhalb der Timer ISR
}
count++;
// wenn Periodendauer abgelaufen alles wieder zurücksetzen
if (count >= led.PERIODENDAUER)
{
finish = false;
memset(led.done, false, ANZAHL); // Zustände löschen
count = 0;
}
messPin.setLow();
}
// ****** Funktionen ******
template <typename A, typename S>
unsigned long calcLastPulsTime(A const *puls, S const size)
{
unsigned long sum = 0;
for (S i = 0; i < size; ++i)
{
sum += puls[i];
}
return sum;
}
template <typename A, typename S>
void testAusgabe (A const *arr, S const size)
{
for (S i = 0; i < size; i++)
{
Serial.print(arr[i]);
Serial.print(',');
}
Serial.println();
}
template <typename A, typename S>
void calcRelativDelay (A const *dela, A *rela, S const size)
{
if (debugging == state::DEBUG) Serial.print(F("led.puls Relativwerte "));
rela[0] = dela[0]; // ersten Delayeintrag einfach übernehmen
for (S i = 1; i < size; i++)
{
// aktuellen Wert mit dem vorherigen addieren und im aktuellen Index speichern,
// damit wird jeder Puls relativ zum vorherigen verzögert
rela[i] = rela[i-1] + dela[i];
}
if (debugging == state::DEBUG)
{
for (S i = 0; i < size; i++)
{
Serial.print(rela[i]);
Serial.print(',');
}
}
if (debugging == state::DEBUG) Serial.println();
}
template <typename A, typename S>
A maxFromArray (A const *puls, A const *dela, S const size)
{
A maximal = 0;
if (debugging == state::DEBUG)
{
Serial.print(F("led.puls Summenwerte "));
}
for (S i = 0; i < size; i++)
{
A sum = puls[i] + dela[i];
if ( sum > maximal)
{
maximal = sum;
}
if (debugging == state::DEBUG)
{
Serial.print(sum);
Serial.print(',');
}
}
if (debugging == state::DEBUG) Serial.println();
return maximal;
}
void preSetTimer1 () // Voreinstellungen, läuft noch nicht los
{
cli(); // Interrupts ausschalten
TCCR1B = 0; // Reset
TCCR1A = 0; //
TIMSK1 = 0; //
TCNT1 = 0; //
OCR1A = 20; //
TCCR1B = (1 << WGM12); // CTC Mode
TIMSK1 = (1 << OCIE1B); // enable Compare Match B ISR
sei(); // Interrupts einschalten
}
void runTimer1 ()
{
ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
TCCR1B |= (1 << CS11) | (1 << CS10); // Prescaler 64
}
}
void stopTimer1 ()
{
ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
TCCR1B &= ~( (1 << CS12) | (1 << CS11) | (1 << CS10) );
}
}
Phasenlage absolut:
Phasenlage relativ:
CombiePin.zip (305 KB)