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
main.cpp
- Committer:
- AndyA
- Date:
- 2021-01-15
- Revision:
- 0:97661408d0f9
- Child:
- 1:dd1f7e162f91
File content as of revision 0:97661408d0f9:
#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
}