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
--- /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 +}