SMPTE timedode (LTC) decode library for mbed

Dependents:   LTC_SMPTE_sample

SMPTE timedode (LTC) decode library

SMPTEタイムコードをデコード(受信)・エンコード(送信)するライブラリです。

平衡/不平衡/サウンド等によるLTC信号は、適当な回路で整形して入力してください。
出力は適当なドライバ回路を設けてください。

簡易的なプログラムのため、細かいフラグなどは無視しています。

LPC1768 専用、Timer 2 を占有します。

Sample

Import programLTC_SMPTE_sample

SMPTE timedode (LTC) decode library for mbed https://developer.mbed.org/users/okini3939/code/LTC_SMPTE/

LTC_SMPTE.cpp

Committer:
okini3939
Date:
2018-05-14
Revision:
2:13a89fffbb75
Parent:
1:63ceee4bfd05

File content as of revision 2:13a89fffbb75:

/**
 * SMPTE timedode (LTC) decode library for mbed
 * Copyright (c) 2015 Suga
 * Released under the MIT License: http://mbed.org/license/mit
 */
/** @file
 * @brief SMPTE timedode (LTC) decode library for mbed
 */

#include "LTC_SMPTE.h"

#if ! defined(TARGET_LPC176X)
#error "this CPU is not supported. use LPC1768"
#endif

// pulse width: 416.7us(30fps)
// 80bit x 30frame/s --> 416.7us/bit
#define ONE_TIME_MIN  180
#define ONE_TIME_MAX  280
#define ZERO_TIME_MIN 390
#define ZERO_TIME_MAX 540

#define FPS24_REF 521
#define FPS25_REF 500
#define FPS30_REF 417

#define US_TICKER_TIMER      ((LPC_TIM_TypeDef *)LPC_TIM2_BASE)
#define US_TICKER_TIMER_IRQn TIMER2_IRQn

LTC_SMPTE *LTC_SMPTE::_inst;

void isrTimer () {
    US_TICKER_TIMER->TC = 0;
    US_TICKER_TIMER->IR = 1;
    LTC_SMPTE::_inst->isr_ticker();
}

LTC_SMPTE::LTC_SMPTE (PinName pin, enum LTC_TYPE type) {

    _inst = this;
    mode = 0;
    count = 0;
    oneflg = 0;
    direction = 0;
    received = 0;

    this->type = type;
    if (type == LTC_INPUT) {
        _input = new InterruptIn(pin);
        _input->mode(PullUp);
        _input->fall(this, &LTC_SMPTE::isr_change);
        _input->rise(this, &LTC_SMPTE::isr_change);

        _timer.start();
    } else {
        _output = new DigitalOut(pin);

        LPC_SC->PCONP |= 1 << 22; // Clock TIMER_2
        US_TICKER_TIMER->CTCR = 0x0; // timer mode
        uint32_t PCLK = SystemCoreClock / 4;
        US_TICKER_TIMER->TCR = 0x2;  // reset
        uint32_t prescale = PCLK / 2400 / 2; // 80bit * 30frame
        US_TICKER_TIMER->PR = prescale - 1;
        NVIC_SetVector(US_TICKER_TIMER_IRQn, (uint32_t)&isrTimer);
        NVIC_EnableIRQ(US_TICKER_TIMER_IRQn);
    }
}

void LTC_SMPTE::isr_change () {
    int b, t;

    t = _timer.read_us();
    _timer.reset();

    if (t >= ONE_TIME_MIN && t < ONE_TIME_MAX) {
        if (oneflg == 0) {
            oneflg = 1;
            return;
        } else {
            oneflg = 0;
            b = 1;
        }
    } else
    if (t >= ZERO_TIME_MIN && t < ZERO_TIME_MAX) {
        oneflg = 0;
        b = 0;

        if (t >= FPS24_REF - 10 && t <= FPS24_REF + 10) {
            fps = 0;
        } else
        if (t >= FPS25_REF - 10 && t <= FPS25_REF + 10) {
            fps = 1;
        } else
        if (t >= FPS30_REF - 10 && t <= FPS30_REF + 10) {
            fps = drop ? 2 : 3; // 29.97 / 30
        }
    } else {
        oneflg = 0;
        count = 0;
        return;
    }

    for (int i = 0; i < 9; i ++) {
        code[i] = (code[i] >> 1) | ((code[i + 1] & 1) ? 0x80 : 0);
    }
    code[9] = (code[9] >> 1) | (b ? 0x80 : 0);
    count ++;

    if (code[8] == 0xfc && code[9] == 0xbf && count >= 80) {
        parse_code();
        count = 0;
    }
}

void LTC_SMPTE::parse_code () {
    frame = (code[1] & 0x03) * 10 + (code[0] & 0x0f);
    sec   = (code[3] & 0x07) * 10 + (code[2] & 0x0f);
    min   = (code[5] & 0x07) * 10 + (code[4] & 0x0f);
    hour  = (code[7] & 0x03) * 10 + (code[6] & 0x0f);
    drop  = code[1] & (1<<2) ? 1 : 0;
    received = 1;
}

void LTC_SMPTE::read (int *hour, int *min, int *sec, int *frame, int *dir) {
    *hour = this->hour | (this->fps << 5);
    *min = this->min;
    *sec = this->sec;
    *frame = this->frame;
    if (dir) *dir = this->direction;
    received = 0;
}

void LTC_SMPTE::write (int hour, int min, int sec, int frame, int dir) {
    if (type != LTC_OUTPUT) return;

    US_TICKER_TIMER->TCR = 0;
    this->hour = hour;
    this->min = min;
    this->sec = sec;
    this->frame = frame;
    this->direction = dir;
    mode = 0;
    count = 0;

    US_TICKER_TIMER->TCR = 0x2;  // reset
    // set match value
    US_TICKER_TIMER->MR0 = 1;
    // enable match interrupt
    US_TICKER_TIMER->MCR |= 1;
    US_TICKER_TIMER->TCR = 1; // enable = 1, reset = 0

    _timer.reset();
    _timer.start();
    __enable_irq();
}

void LTC_SMPTE::isr_ticker () {
    if (mode) {
        if (bit) {
            phase = !phase;
            _output->write(phase);
        }
        mode = 0;
        return;
    }

    phase = !phase;
    _output->write(phase);
    mode = 1;

    if (count >= 0 && count <= 3) {
        bit = (frame % 10) & (1 << count);
    } else
    if (count >= 8 && count <= 9) {
        bit = (frame / 10) & (1 << (count - 8));
    } else
    if (count >= 16 && count <= 19) {
        bit = (sec % 10) & (1 << (count - 16));
    } else
    if (count >= 24 && count <= 26) {
        bit = (sec / 10) & (1 << (count - 24));
    } else
    if (count >= 32 && count <= 35) {
        bit = (min % 10) & (1 << (count - 32));
    } else
    if (count >= 40 && count <= 42) {
        bit = (min / 10) & (1 << (count - 40));
    } else
    if (count >= 48 && count <= 51) {
        bit = (hour % 10) & (1 << (count - 48));
    } else
    if (count >= 56 && count <= 57) {
        bit = (hour / 10) & (1 << (count - 56));
    } else
    if ((count >= 66 && count <= 77) || count == 79) {
        bit = 1;
    } else {
        bit = 0;
    }

    count ++;
    if (count >= 80) {
        count = 0;
        frame ++;
        if (frame >= 30) {
            frame = 0;
            sec ++;
            if (sec >= 60) {
                sec = 0;
                min ++;
                if (min >= 60) {
                    min = 0;
                    hour ++;
                    if (hour >= 24) {
                        hour = 0;
                    }
                }
            }
        }
    }
}

int LTC_SMPTE::isReceived () {
    return received;
}