Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: BufferedSerial FatFileSystemCpp mbed
Diff: main.cpp
- Revision:
- 0:97661408d0f9
- Child:
- 1:dd1f7e162f91
diff -r 000000000000 -r 97661408d0f9 main.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Fri Jan 15 11:49:01 2021 +0000
@@ -0,0 +1,415 @@
+#include "mbed.h"
+#include "LTCApp.h"
+
+const int framesToCount = 300;
+const int MaxTimeErrorUS = 150;
+const int timerOverheadTime = 19;
+
+BufferedSerial pc(USBTX, USBRX);
+VIPSSerial VIPS(p28, p27);
+BufferedSerial COM1(p13, p14);
+BufferedSerial COM3(p9, p10);
+
+DigitalOut led1(LED1);
+DigitalOut PPS(p12);
+
+DigitalOut led2(LED2);
+DigitalOut led3(LED3);
+
+DigitalOut frameToggle(LED4);
+DigitalOut second(p23);
+
+LTCDecode LTCInput(p7);
+InterruptIn PPFin(p29);
+InterruptIn Syncin(p8);
+
+// clock to time everything with
+Timer inputTimer;
+
+// Time since last frame event, used for position output interpolation
+Timer TimeSinceLastFrame;
+uint32_t TimeSinceLastFrameWrap;
+
+// used to start PPS at the correct point
+Timeout PPSsyncTimer;
+
+// used to generate PPS edges
+Ticker PPSOutputTimer;
+
+frameRates detectedRate;
+bool ppsRunning = false; // set when PPS start has been scheduled
+volatile bool ppsActive = false; // set when PPS is actuallt going (up to 1 second later)
+
+volatile bool PPSHigh;
+bool resync = false;
+bool resyncDone = false;
+uint32_t resyncPeriod;
+bool OKToCheckSync = false;
+volatile uint32_t VBOXTicks = 0; // time at the NEXT PPS edge
+uint32_t lastPPSSecondStart;
+
+#define _longPPMTrackLen_ 20
+float PPMErrors[_longPPMTrackLen_];
+float PPMTrackTotal;
+int PPMTrackIndex;
+float PPMHighAcc;
+float remainingClockError;
+bool ppmCorrection;
+
+
+void sendPosition(position *posPtr) {
+ if (posPtr) {
+ COM1.write(posPtr, sizeof(position));
+ COM3.write(posPtr, sizeof(position));
+ printf("send\r\n");
+ }
+}
+
+
+void OnPPSEdge() {
+ if (PPSHigh) {
+ PPS = 0;
+ PPSHigh = false;
+ } else {
+ PPS = 1;
+ PPSHigh = true;
+
+ if (resync && !ppmCorrection) {
+ PPSOutputTimer.detach();
+ PPSOutputTimer.attach(callback(OnPPSEdge), resyncPeriod);
+ resync = false;
+ resyncDone = true;
+ } else if (resyncDone) {
+ resyncDone = false;
+ PPSOutputTimer.detach();
+ PPSOutputTimer.attach_us(callback(OnPPSEdge), 5000);
+ remainingClockError = 0;
+ }
+
+ if ((VBOXTicks % 100) == 0) {
+ lastPPSSecondStart = inputTimer.read_us();
+ second = 1;
+ remainingClockError+=PPMHighAcc;
+ int adjustment = (int)(remainingClockError/2);
+ if (!resyncDone && ((adjustment > 2)|| (adjustment <-2))) {
+ PPSOutputTimer.detach();
+ PPSOutputTimer.attach_us(callback(OnPPSEdge), 5000+adjustment-timerOverheadTime);
+ ppmCorrection=true;
+ remainingClockError-=2*adjustment;
+ }
+ } else {
+ if ((VBOXTicks % 100) == 1) {
+ second = 0;
+ if (ppmCorrection) {
+ PPSOutputTimer.detach();
+ PPSOutputTimer.attach_us(callback(OnPPSEdge), 5000);
+ ppmCorrection=false;
+ }
+ }
+ }
+
+ VBOXTicks++;
+ if (VBOXTicks >= (24 * 60 * 60 * 100))
+ VBOXTicks -= (24 * 60 * 60 * 100);
+ }
+}
+
+void OnPPSCync() {
+ PPS = 1;
+ PPSOutputTimer.detach();
+ PPSOutputTimer.attach_us(callback(OnPPSEdge), 5000);
+ PPSHigh = true;
+ VBOXTicks++;
+ ppsActive = true;
+ led1 = 1;
+}
+
+const char *ticksToTime(unsigned long vboxTickCount) {
+ static char timeString[32];
+ int hours = vboxTickCount / 360000;
+ int minutes = (vboxTickCount / 6000) % 60;
+ int seconds = (vboxTickCount / 100) % 60;
+ int ms = 10 * (vboxTickCount % 100);
+ sprintf(timeString, "%02d:%02d:%02d.%03d", hours, minutes, seconds, ms);
+ return timeString;
+}
+
+bool isStartOfSecond(int minutes, int seconds, int frame, bool dropFrame) {
+ if (frame == 0)
+ return true;
+ if (dropFrame && (2 == frame) && (0 == seconds) && (minutes % 10 != 0))
+ return true;
+ return false;
+}
+
+
+int getClosestRateIndex(long int framePeriodUS) {
+ int indexOver = 1;
+ while (framePeriodUS <= frameRateInfo::FramePeriods[indexOver])
+ indexOver++;
+ float amountOver = framePeriodUS - frameRateInfo::FramePeriods[indexOver];
+ float amountUnder = frameRateInfo::FramePeriods[indexOver - 1] - framePeriodUS;
+ if (amountOver > amountUnder)
+ return indexOver - 1;
+ return indexOver;
+}
+
+// frame rate and clock error calculation
+void frameRateMeasure(uint32_t frameStartTime, bool dropFrame=false) {
+ frameToggle =!frameToggle;
+ static int frameRateCount = 0;
+ static uint32_t rateCalcStartTime;
+ if (frameRateCount == 0) {
+ rateCalcStartTime = frameStartTime;
+ frameRateCount = 1;
+ } else {
+ if (frameRateCount == framesToCount) {
+ uint32_t timeTaken = frameStartTime - rateCalcStartTime;
+ long int timePerFrame = timeTaken / framesToCount;
+ detectedRate.setRate(getClosestRateIndex(timePerFrame));
+ float TrueSeconds = framesToCount / detectedRate.currentRate();
+ // +ve PPM means I counted more than real time = my clock is fast.
+ float ppmError = (timeTaken - (1000000 * TrueSeconds)) / TrueSeconds;
+ if ((ppmError<250) && (ppmError>-250)) { // something went very wrong with ppm calculation.
+ if (PPMHighAcc!=0) {
+ PPMTrackTotal -= PPMErrors[PPMTrackIndex];
+ PPMTrackTotal += ppmError;
+ PPMErrors[PPMTrackIndex++] = ppmError;
+ PPMHighAcc = PPMTrackTotal / _longPPMTrackLen_;
+ } else { // first time
+ for (int i=0;i<_longPPMTrackLen_;i++)
+ PPMErrors[i] = ppmError;
+ PPMTrackTotal = ppmError*_longPPMTrackLen_;
+ PPMHighAcc = ppmError;
+ }
+ if (PPMTrackIndex == _longPPMTrackLen_)
+ PPMTrackIndex = 0;
+ printf("Frame rate detected as %s. My clock is %.4f ppm fast\r\n", detectedRate.frameRateString(), PPMHighAcc);
+ } else {
+ printf("Frame rate unclear\r\n");
+ PPMHighAcc=0;
+ detectedRate.setRate(0);
+ }
+ frameRateCount = 0;
+ } else {
+ frameRateCount++;
+ }
+ }
+}
+
+
+//called once per frame to output the current postition
+void framePositionOutput() {
+ sendPosition(VIPS.sendPositionForTime(TimeSinceLastFrame.read_us()));
+ TimeSinceLastFrame.reset();
+}
+
+// called by background loop at the end of each frame
+// frameStartTime = time of inputTimer at the start of the first bit of the
+// frame. dropFrame = true if the protocol includes frameDrops
+void frameComplete(int hours, int minutes, int seconds, int frame,
+ bool dropFrame, uint32_t frameStartTime) {
+ static int lastFrame = 0;
+ static float ppmError;
+ static int outCount = 0;
+ if ((frame != (lastFrame + 1)) && detectedRate.currentRate()) {
+ long timeSinceSecondStart = detectedRate.getOffsetFromSecondStart(minutes, seconds, frame);
+// printf("Time Code %02d:%02d:%02d:%02d - VBOX time %s\r", hours, minutes, seconds, frame, ticksToTime(VBOXTicks));
+ uint32_t ThisSecondStart = frameStartTime - timeSinceSecondStart;
+ if (!ppsRunning) {
+ uint32_t nextSecondStart = ThisSecondStart + 1000000 + (int)(ppmError + 0.5);
+ ppsRunning = true;
+ __disable_irq();
+ uint32_t nextSec = nextSecondStart - inputTimer.read_us();
+ PPSsyncTimer.attach(callback(OnPPSCync), nextSec);
+ __enable_irq();
+ printf("PPS start scheduled for %0.3f seconds.\r\n", nextSec / 1000000.0f);
+ VBOXTicks = (hours * 3600 + minutes * 60 + (seconds + 1)) * 100;
+ } else if (detectedRate.isSyncable()) { // PPS running and from a syncable source
+ long timeError = (lastPPSSecondStart - ThisSecondStart);
+ if (timeError < -500000)
+ timeError += 1000000;
+ timeError = timeError % 1000000;
+// printf("PPS error measured as %ld us.\r", timeError);
+
+ if ((timeError > MaxTimeErrorUS) || (timeError < -MaxTimeErrorUS)) {
+ outCount++;
+ if (outCount > 3) {
+ int newPeriod = 5000 - timeError / 2;
+ if (newPeriod < 4500)
+ newPeriod = 4500;
+ if (newPeriod > 5500)
+ newPeriod = 5500;
+
+ // printf("Resync attempt. Error was %ld. 1 cycle at %d us\r\n", timeError, newPeriod);
+ __disable_irq();
+ resyncPeriod = newPeriod - timerOverheadTime;
+ resync = true;
+ __enable_irq();
+ }
+ } else
+ outCount = 0;
+ } // either no PPS and can't start it yet or running from unsyncable source
+ // so just free run.
+ }
+ lastFrame = frame;
+
+ // frame rate and clock error calculation
+ frameRateMeasure(frameStartTime, dropFrame);
+ framePositionOutput();
+}
+
+// called by background loop at the end of each frame when running from a sync signal not timecode
+void framePulse(uint32_t frameStartTime) {
+ static int outCount = 0;
+ uint32_t patternStartTime;
+ if (detectedRate.isValid()) {
+ uint32_t ThisSecondStart = frameStartTime ;
+ if (!ppsRunning) {
+ uint32_t pulseStartTime = frameStartTime + (int)(detectedRate.currentPeriodUS()*5+0.5f);
+ uint32_t timeTillStart = pulseStartTime - inputTimer.read_us();
+ ppsRunning = true;
+ __disable_irq();
+ PPSsyncTimer.attach_us(callback(OnPPSCync), timeTillStart);
+ __enable_irq();
+ printf("PPS start scheduled for %0.3f seconds.\r\n",
+ timeTillStart / 1000000.0f);
+ VBOXTicks = 0;
+ outCount = 0;
+ } else {
+// if (outCount == 0)
+// patternStartTime = inputTimer.elapsed_time();
+// else {
+// expectedTotalUS = outCount
+// }
+ }
+ }
+ // frame rate and clock error calculation
+ frameRateMeasure(frameStartTime, false);
+ framePositionOutput();
+ }
+
+volatile bool NewFramePulse= false;
+uint32_t FramePulseTime;
+volatile int framesIn = 0;
+void OnPPFInputStartup(void) {
+ framesIn++;
+}
+
+volatile int SyncInCount = 0;
+void OnSyncInputStartup(void) {
+ SyncInCount++;
+}
+
+
+void OnPPFInput(void) {
+ FramePulseTime = inputTimer.read_us();
+ NewFramePulse = true;
+}
+
+
+int main() {
+ pc.baud(115200);
+// COM1.baud(115200);
+// COM3.baud(115200);
+ inputTimer.reset();
+ inputTimer.start();
+
+ pc.printf("Startup\r\n");
+ led1 = 0;
+// LTCInput.setInputTimer(&inputTimer);
+// PPFin.rise(callback(&OnPPFInputStartup));
+
+ while (true) {
+ pc.putc('.');
+ led1=!led1;
+ wait_us(1000*100);
+ }
+
+ for (int i = 0; i < _longPPMTrackLen_; i++)
+ PPMErrors[i] = 0;
+ pc.printf("Startup\r\n");
+ PPMTrackIndex = 0;
+ PPMTrackTotal = 0;
+ remainingClockError = 0;
+ bool GenLockOnlyMode = false;
+ bool GenLockToSync = false;
+
+ TimeSinceLastFrame.reset();
+ TimeSinceLastFrame.start();
+ while (true) {
+ pc.printf("Waiting for sync\r\n");
+ PPFin.rise(callback(&OnPPFInputStartup));
+ Syncin.rise(callback(&OnSyncInputStartup));
+ framesIn = 0;
+ SyncInCount = 0;
+ GenLockOnlyMode = false;
+ GenLockToSync = false;
+ while (!LTCInput.searchForSync()) {
+ if (framesIn == 100) {
+ GenLockOnlyMode = true;
+ break;
+ }
+ if ((SyncInCount == 100) && (framesIn<45)) { // prefer frame input pin, sync may be twice as high for interlaced systems.
+ GenLockOnlyMode = true;
+ GenLockToSync= true;
+ break;
+ }
+
+ int message = VIPS.getStatusMessage();
+ if (message)
+ pc.printf("%s\r\n",VIPSStatusMessages[message-1]);
+
+ }
+ if (GenLockOnlyMode) {
+ if (GenLockToSync) {
+ pc.printf("No LTC detected for 100 frames. Falling back to genlock on sync input\r\n");
+ Syncin.rise(callback(&OnPPFInput));
+ PPFin.rise(NULL);
+ } else {
+ pc.printf("No LTC detected for 100 frames. Falling back to genlock on frame input\r\n");
+ PPFin.rise(callback(&OnPPFInput));
+ Syncin.rise(NULL);
+ }
+ uint32_t lastframeTime = inputTimer.read_us();
+
+ while (true) {
+ if (NewFramePulse) { // running on a frame
+// printf("frame\r");
+ lastframeTime = FramePulseTime;
+ framePulse(lastframeTime);
+ NewFramePulse = false;
+ }
+ if ((inputTimer.read_us()-lastframeTime) > 1000000) {
+ pc.printf("No frames for > 1 second\r\n");
+ break;
+ }
+ int message = VIPS.getStatusMessage();
+ if (message)
+ pc.printf("%s\r\n",VIPSStatusMessages[message-1]);
+ }
+ } else { // not genlock
+ pc.printf("Got LTC sync\r\n");
+ PPFin.rise(NULL);
+ while (LTCInput.synced()) {
+ position *posPtr = VIPS.getWaitingPostion();
+ if (posPtr) {
+ sendPosition(posPtr);
+ }
+ if (LTCInput.readWaitingData()) {
+ if (LTCInput.synced()) {
+ const LTCDecode::LTCData_t *frameData = LTCInput.getLastFrame();
+ detectedRate.setDrop(frameData->frameDrop);
+ frameComplete(frameData->hours, frameData->minutes,
+ frameData->seconds, frameData->frame,
+ frameData->frameDrop, frameData->frameStartTime);
+ } // end if good frame
+ } // end if new data
+ int message = VIPS.getStatusMessage();
+ if (message)
+ pc.printf("%s\r\n",VIPSStatusMessages[message-1]);
+ } // end while synced
+ pc.printf("Sync lost\r");
+ }
+ } // end while true
+}