/********************************************************** * COG power-up sequence. **********************************************************/ void cogPowerUp(void) { measureTemperature(); GPIO_PinOutSet(EPD_PIN_PANEL_VDD); delayMs(5); pwmEnable(); delayMs(5); GPIO_PinOutSet(EPD_PIN_PANEL_ON); delayMs(10); GPIO_PinOutSet(EPD_PIN_CS); GPIO_PinOutSet(EPD_PIN_BORDER); GPIO_PinOutSet(EPD_PIN_RESET); delayMs(5); GPIO_PinOutClear(EPD_PIN_RESET); delayMs(5); GPIO_PinOutSet(EPD_PIN_RESET); delayMs(5); }
/*----------------------------------------------------------------------------*/ int main(void) { const uint32_t maxPeriod = captureUnitConfig.frequency / pwmUnitConfig.frequency + 1; const uint32_t minPeriod = captureUnitConfig.frequency / pwmUnitConfig.frequency - 1; setupClock(); const struct Pin led = pinInit(LED_PIN); pinOutput(led, false); struct GpTimerPwmUnit * const pwmUnit = init(GpTimerPwmUnit, &pwmUnitConfig); assert(pwmUnit); struct Pwm * const pwm = gpTimerPwmCreate(pwmUnit, OUTPUT_PIN); assert(pwm); pwmSetEdges(pwm, 0, pwmGetResolution(pwm) / 2); struct GpTimerCaptureUnit * const captureUnit = init(GpTimerCaptureUnit, &captureUnitConfig); assert(pwmUnit); struct Capture * const capture = gpTimerCaptureCreate(captureUnit, INPUT_PIN, PIN_RISING, PIN_PULLDOWN); assert(capture); uint32_t previousTime = 0; bool event = false; captureSetCallback(capture, onCaptureEvent, &event); captureEnable(capture); pwmEnable(pwm); while (1) { while (!event) barrier(); event = false; const uint32_t currentTime = captureGetValue(capture); const uint32_t period = currentTime - previousTime; if (period >= minPeriod && period <= maxPeriod) pinReset(led); else pinSet(led); previousTime = currentTime; } return 0; }
/*----------------------------------------------------------------------------*/ int main(void) { setupClock(); const struct Pin led = pinInit(LED_PIN); pinOutput(led, true); struct GpTimerPwmUnit * const pwmUnit = init(GpTimerPwmUnit, &pwmUnitConfig); assert(pwmUnit); struct Pwm * const pwmOutput = gpTimerPwmCreate(pwmUnit, OUTPUT_PIN); assert(pwmOutput); pwmSetDuration(pwmOutput, pwmGetResolution(pwmOutput) / 2); struct Timer * const counter = init(GpTimerCounter, &counterConfig); assert(counter); struct Timer * const timer = init(GpTimer, &timerConfig); assert(timer); timerSetOverflow(timer, 1000); bool event = false; timerSetCallback(timer, onTimerOverflow, &event); timerEnable(counter); pwmEnable(pwmOutput); timerEnable(timer); while (1) { while (!event) barrier(); event = false; const uint32_t period = timerGetValue(counter); timerSetValue(counter, 0); if (period >= 999 && period <= 1001) pinReset(led); else pinSet(led); } return 0; }
/* * Parameter time: Aktuelle Zeit, kann systemTime sein * Es bietet sich an eine Zeit im ms Raster zu benutzen, dies muss jedoch nicht sein. * So könnte die Zeit genauso das Timerraster widerspiegeln, in dem die Routine aufgerufen * wird. Dann müssen die Konstanten für die Wartezeiten natürlich ebenfalls nach dieser Einheit * ausgerichtet sein. * Es gibt eine Wartezeit direkt nach einer Bestromung der Relaisspule. Sie dient dazu, besser den * Ladezustand der Bulkkondensatoren ermitteln zu können. Daraus wird die Anzahl der anschließend möglichen * Schaltvorgänge ermittelt. * Rückgabewert true wenn neue SPI-Daten generiert wurden, also eine Übertragung notwendig wäre. */ bool Relay::DoSwitching(unsigned time, unsigned &RelDriverData) { bool retval = false; IdleDetect(time); // Erst die Verwaltung der SubStates (aktuell laufender Puls, Wartezeit nach Puls...) // ================================= if (SubState == RelSubStates::Pulse) // Es wird gerade eine/mehrere Relaisspulen bestromt { #ifdef RELAYUSEDISCRETETIMING if ((signed)(time-NextPointInTime) >= 0) //if ((time-PulseStartTime) >= RELAYPULSEDURATION) #else if ((signed)(time-NextPointInTime) > 0) //if ((time-PulseStartTime) > RELAYPULSEDURATION) #endif { DriverData = 0; retval = true; SubState = RelSubStates::Delay; NextPointInTime = time + RELAYPOSTDELAY1; } } if (SubState == RelSubStates::Delay) // Wartezeit nach einem Ansteuerpuls für eine korrekte Vermessung { #ifdef RELAYUSEDISCRETETIMING if ((signed)(time-NextPointInTime) >= 0) //if ((time-PulseStartTime) >= RELAYPOSTDELAY) #else if ((signed)(time-NextPointInTime) > 0) //if ((time-PulseStartTime) > RELAYPOSTDELAY) #endif { pwmEnable(false); SubState = RelSubStates::Delay2; NextPointInTime = time + RELAYPOSTDELAY2; if (OpState == RelOperatingStates::MeasMode) { unsigned zw1 = EnergyCalcRefVoltage; zw1 = zw1*zw1; // aktuelle Bulkspannung festhalten unsigned zw2 = GetRailVoltage(); zw2 = zw2*zw2; if (zw1 > zw2) { // Benötigte Energie ausrechnen und abspeichern SingleSwitchEnergy = zw1 - zw2; // Nach Messungen keine Sicherheitsmarge erforderlich } else { // Problem... Einfach eine seeehr große Zahl annehmen. SingleSwitchEnergy = 100000; } } } } if (SubState == RelSubStates::Delay2) // Die noch folgende Wartezeit bis zum nächsten Puls { #ifdef RELAYUSEDISCRETETIMING if ((signed)(time-NextPointInTime) >= 0) //if ((time-PulseStartTime) >= RELAYPOSTDELAY) #else if ((signed)(time-NextPointInTime) > 0) //if ((time-PulseStartTime) > RELAYPOSTDELAY) #endif { SubState = RelSubStates::Idle; } } // Folgend die Verwaltung der Operating States der Relay-Unit // ========================================================== if ((OpState == RelOperatingStates::MeasMode) && (SingleSwitchEnergy)) { // Messung wurde bereits durchgeführt, warten bis erreichen der Mindestreserve für's BusVoltageFailureSwitching if (OpChgReq & (RELREQSTOP | RELREQBUSVFAIL)) { // Wenn währenddessen der Start schon wieder abgeblasen wurde... OpState = RelOperatingStates::Disable; OpChgReq = 0; } else { if (CalcAvailRelEnergy() >= __builtin_popcount(BusVFailMask)) { for (unsigned ch=0; ch<CHANNELCNT; ch++) PulseRepTmr[ch] = time; // Die PulseRepTmr werden auf "fällig" gesetzt OpState = RelOperatingStates::Operating; } } } int RelEnergyAvail=0; bool StartASwitch = false; if (OpState == RelOperatingStates::Disable) { if (OpChgReq & (RELREQSTOP | RELREQBUSVFAIL)) { OpChgReq = 0; // Dann auch einen evtl StartRequest löschen } if (OpChgReq & RELREQSTART) { // Bulkspannung speichern EnergyCalcRefVoltage = GetRailVoltage(); // Wenn Bulkspannung > fester Wert if (EnergyCalcRefVoltage > MINURAILINITVOLTAGE) { // Es wird das erste Relais bestromt mit der alten Schaltrichtung, // d.h. es wird nicht umgeschaltet. Dies dient nur zur Messung, // um wieviel die Bulkspannung dabei einbricht. if (ChRealSwStatus & 1) { DriverData = RELAYPATTERNON; } else { DriverData = RELAYPATTERNOFF; } PulseRepTmr[0] = time + RELAYREPPULSEDELAYLONG; OpState = RelOperatingStates::MeasMode; NextPointInTime = time + RELAYPULSEDURATION; SubState = RelSubStates::Pulse; retval = true; OpChgReq &= 0; } } } if ((OpState == RelOperatingStates::BusVFail) && (SubState == RelSubStates::Idle)) { OpState = RelOperatingStates::Disable; } if ((OpState == RelOperatingStates::Operating) && (SubState == RelSubStates::Idle)) { if ((OpChgReq & RELREQBUSVFAIL)) { if (BusVFailMask) { // Es gibt Kanäle, die für ein BusVoltageFailureSwitching konfiguriert sind DriverData = 0; // Die Routine ist zwar ähnlich wie die "normale" Schaltroutine, doch leider zu unerschiedlich um sie ohne weiteres zu vereinigen. for (unsigned ch=0;ch<CHANNELCNT;ch++) { if (BusVFailMask & (1 << ch)) { //BusVFailMask &= ~(1 << ch); unnötig if (BusVFailData & (1 << ch)) { ChTargetSwStatus |= (1 << ch); ChRealSwStatus |= (1 << ch); DriverData |= (RELAYPATTERNON << (2*ch)); } else { ChTargetSwStatus &= ~(1 << ch); ChRealSwStatus &= ~(1 << ch); DriverData |= (RELAYPATTERNOFF << (2*ch)); } } } NextPointInTime = time + RELAYPULSEDURATION; SubState = RelSubStates::Pulse; OpState = RelOperatingStates::BusVFail; retval = true; } else { // kein BusVoltageFailureSwitching, dann geht es hier ganz einfach OpState = RelOperatingStates::Disable; } OpChgReq = 0; } else { if ((OpChgReq & RELREQSTOP)) { // Es wurde ein Stop verlangt, dann gibt es kein BusVoltageFailureSwitching! OpState = RelOperatingStates::Disable; OpChgReq = 0; } else { if (BuffersNonEmpty()) { // Für wie viele Relais reicht die gespeicherte Energie? RelEnergyAvail = CalcAvailRelEnergy() - __builtin_popcount(BusVFailMask); if (RelEnergyAvail > 0) StartASwitch = true; } else if (IdleDetect(time)) if ((CalcAvailRelEnergy() - __builtin_popcount(BusVFailMask)) > 0) { // Nix los, Elkos voll. // Mal nachgucken, ob eine Pulswiederholung ansteht. // Wenn ein Kanal geschaltet worden ist, wird später noch mal ein Puls nachgelegt. int oldest_ch = -1; int oldest_age = -1; int age; for (int ch=0; ch < CHANNELCNT; ch++) { age = (signed)(time-PulseRepTmr[ch]); if (age >= 0) { if (age > oldest_age) { oldest_ch = ch; oldest_age = age; } } } if (oldest_ch >= 0) { PulseRepTmr[oldest_ch] = time + RELAYREPPULSEDELAYLONG; int mask = 1 << oldest_ch; if (ChRealSwStatus & mask) { DriverData = RELAYPATTERNON << (2*oldest_ch); } else { DriverData = RELAYPATTERNOFF << (2*oldest_ch); } NextPointInTime = time + RELAYPULSEDURATION; SubState = RelSubStates::Pulse; retval = true; } } } } } // Nachfolgend wird das Ansteuermuster für die Relaistreiber generiert. // Die Routine wird aus einem Auftrag so viele Schalthandlungen raushohlen, wie Energie zur Verfügung // steht (wenn gewünscht) oder umgekehrt den Schaltauftrag zusammenhalten und erst ausführen, // wenn genug Energie zur Verfügung steht (wenn RELAYKEEPTASKSTOGETHER definiert). // Die Routine kann auch mehrere Aufträge zusammenfassen, wenn die Energie reicht (unter Beachtung // von RELAYKEEPTASKSTOGETHER). Es ist durch DoEnqueue() sichergestellt, dass ein Kanal nie mehr als // einmal in der Warteschlage vorkommt, deshalb ist das unproblematisch. if (StartASwitch) { int RelEnergyNeeded; bool AnotherLoop = false; DriverData = 0; do { #ifdef RELAYKEEPTASKSTOGETHER RelEnergyNeeded = __builtin_popcount(Buffer[BufRdPtr].Mask); // Zähle gesetzte Bits in .Mask #else RelEnergyNeeded = 1; #endif if (RelEnergyAvail < RelEnergyNeeded) { break; } for (unsigned ch=0;ch<CHANNELCNT;ch++) { if (Buffer[BufRdPtr].Mask & (1 << ch)) { Buffer[BufRdPtr].Mask &= ~(1 << ch); ChForcedSwMsk &= ~(1 << ch); PulseRepTmr[ch] = time + RELAYREPPULSEDELAY; if (Buffer[BufRdPtr].Bits & (1 << ch)) { ChRealSwStatus |= (1 << ch); DriverData |= (RELAYPATTERNON << (2*ch)); } else { ChRealSwStatus &= ~(1 << ch); DriverData |= (RELAYPATTERNOFF << (2*ch)); } if ((--RelEnergyAvail == 0) || (Buffer[BufRdPtr].Mask == 0)) { break; } } } if (Buffer[BufRdPtr].Mask == 0) { AnotherLoop = NextBufEntry(BufRdPtr); } } while (AnotherLoop); if (DriverData != 0) { NextPointInTime = time + RELAYPULSEDURATION; SubState = RelSubStates::Pulse; retval = true; } } if (retval && (DriverData != 0)) pwmEnable(true); RelDriverData = DriverData; return retval; }