SMPTE timedode (LTC) decode library for mbed
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@2:13a89fffbb75, 2018-05-14 (annotated)
- Committer:
- okini3939
- Date:
- Mon May 14 08:50:39 2018 +0000
- Revision:
- 2:13a89fffbb75
- Parent:
- 1:63ceee4bfd05
Supported 24, 25fps, Fix algorithm
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
okini3939 | 0:8d19e2158eb4 | 1 | /** |
okini3939 | 0:8d19e2158eb4 | 2 | * SMPTE timedode (LTC) decode library for mbed |
okini3939 | 0:8d19e2158eb4 | 3 | * Copyright (c) 2015 Suga |
okini3939 | 0:8d19e2158eb4 | 4 | * Released under the MIT License: http://mbed.org/license/mit |
okini3939 | 0:8d19e2158eb4 | 5 | */ |
okini3939 | 0:8d19e2158eb4 | 6 | /** @file |
okini3939 | 0:8d19e2158eb4 | 7 | * @brief SMPTE timedode (LTC) decode library for mbed |
okini3939 | 0:8d19e2158eb4 | 8 | */ |
okini3939 | 0:8d19e2158eb4 | 9 | |
okini3939 | 0:8d19e2158eb4 | 10 | #include "LTC_SMPTE.h" |
okini3939 | 0:8d19e2158eb4 | 11 | |
okini3939 | 1:63ceee4bfd05 | 12 | #if ! defined(TARGET_LPC176X) |
okini3939 | 1:63ceee4bfd05 | 13 | #error "this CPU is not supported. use LPC1768" |
okini3939 | 1:63ceee4bfd05 | 14 | #endif |
okini3939 | 1:63ceee4bfd05 | 15 | |
okini3939 | 0:8d19e2158eb4 | 16 | // pulse width: 416.7us(30fps) |
okini3939 | 0:8d19e2158eb4 | 17 | // 80bit x 30frame/s --> 416.7us/bit |
okini3939 | 2:13a89fffbb75 | 18 | #define ONE_TIME_MIN 180 |
okini3939 | 2:13a89fffbb75 | 19 | #define ONE_TIME_MAX 280 |
okini3939 | 2:13a89fffbb75 | 20 | #define ZERO_TIME_MIN 390 |
okini3939 | 2:13a89fffbb75 | 21 | #define ZERO_TIME_MAX 540 |
okini3939 | 2:13a89fffbb75 | 22 | |
okini3939 | 2:13a89fffbb75 | 23 | #define FPS24_REF 521 |
okini3939 | 2:13a89fffbb75 | 24 | #define FPS25_REF 500 |
okini3939 | 2:13a89fffbb75 | 25 | #define FPS30_REF 417 |
okini3939 | 0:8d19e2158eb4 | 26 | |
okini3939 | 1:63ceee4bfd05 | 27 | #define US_TICKER_TIMER ((LPC_TIM_TypeDef *)LPC_TIM2_BASE) |
okini3939 | 1:63ceee4bfd05 | 28 | #define US_TICKER_TIMER_IRQn TIMER2_IRQn |
okini3939 | 1:63ceee4bfd05 | 29 | |
okini3939 | 1:63ceee4bfd05 | 30 | LTC_SMPTE *LTC_SMPTE::_inst; |
okini3939 | 0:8d19e2158eb4 | 31 | |
okini3939 | 1:63ceee4bfd05 | 32 | void isrTimer () { |
okini3939 | 1:63ceee4bfd05 | 33 | US_TICKER_TIMER->TC = 0; |
okini3939 | 1:63ceee4bfd05 | 34 | US_TICKER_TIMER->IR = 1; |
okini3939 | 1:63ceee4bfd05 | 35 | LTC_SMPTE::_inst->isr_ticker(); |
okini3939 | 1:63ceee4bfd05 | 36 | } |
okini3939 | 1:63ceee4bfd05 | 37 | |
okini3939 | 1:63ceee4bfd05 | 38 | LTC_SMPTE::LTC_SMPTE (PinName pin, enum LTC_TYPE type) { |
okini3939 | 1:63ceee4bfd05 | 39 | |
okini3939 | 1:63ceee4bfd05 | 40 | _inst = this; |
okini3939 | 0:8d19e2158eb4 | 41 | mode = 0; |
okini3939 | 0:8d19e2158eb4 | 42 | count = 0; |
okini3939 | 0:8d19e2158eb4 | 43 | oneflg = 0; |
okini3939 | 0:8d19e2158eb4 | 44 | direction = 0; |
okini3939 | 0:8d19e2158eb4 | 45 | received = 0; |
okini3939 | 0:8d19e2158eb4 | 46 | |
okini3939 | 1:63ceee4bfd05 | 47 | this->type = type; |
okini3939 | 1:63ceee4bfd05 | 48 | if (type == LTC_INPUT) { |
okini3939 | 1:63ceee4bfd05 | 49 | _input = new InterruptIn(pin); |
okini3939 | 1:63ceee4bfd05 | 50 | _input->mode(PullUp); |
okini3939 | 1:63ceee4bfd05 | 51 | _input->fall(this, <C_SMPTE::isr_change); |
okini3939 | 1:63ceee4bfd05 | 52 | _input->rise(this, <C_SMPTE::isr_change); |
okini3939 | 1:63ceee4bfd05 | 53 | |
okini3939 | 1:63ceee4bfd05 | 54 | _timer.start(); |
okini3939 | 1:63ceee4bfd05 | 55 | } else { |
okini3939 | 1:63ceee4bfd05 | 56 | _output = new DigitalOut(pin); |
okini3939 | 1:63ceee4bfd05 | 57 | |
okini3939 | 1:63ceee4bfd05 | 58 | LPC_SC->PCONP |= 1 << 22; // Clock TIMER_2 |
okini3939 | 1:63ceee4bfd05 | 59 | US_TICKER_TIMER->CTCR = 0x0; // timer mode |
okini3939 | 1:63ceee4bfd05 | 60 | uint32_t PCLK = SystemCoreClock / 4; |
okini3939 | 1:63ceee4bfd05 | 61 | US_TICKER_TIMER->TCR = 0x2; // reset |
okini3939 | 1:63ceee4bfd05 | 62 | uint32_t prescale = PCLK / 2400 / 2; // 80bit * 30frame |
okini3939 | 1:63ceee4bfd05 | 63 | US_TICKER_TIMER->PR = prescale - 1; |
okini3939 | 1:63ceee4bfd05 | 64 | NVIC_SetVector(US_TICKER_TIMER_IRQn, (uint32_t)&isrTimer); |
okini3939 | 1:63ceee4bfd05 | 65 | NVIC_EnableIRQ(US_TICKER_TIMER_IRQn); |
okini3939 | 1:63ceee4bfd05 | 66 | } |
okini3939 | 0:8d19e2158eb4 | 67 | } |
okini3939 | 0:8d19e2158eb4 | 68 | |
okini3939 | 0:8d19e2158eb4 | 69 | void LTC_SMPTE::isr_change () { |
okini3939 | 0:8d19e2158eb4 | 70 | int b, t; |
okini3939 | 0:8d19e2158eb4 | 71 | |
okini3939 | 0:8d19e2158eb4 | 72 | t = _timer.read_us(); |
okini3939 | 0:8d19e2158eb4 | 73 | _timer.reset(); |
okini3939 | 0:8d19e2158eb4 | 74 | |
okini3939 | 0:8d19e2158eb4 | 75 | if (t >= ONE_TIME_MIN && t < ONE_TIME_MAX) { |
okini3939 | 0:8d19e2158eb4 | 76 | if (oneflg == 0) { |
okini3939 | 0:8d19e2158eb4 | 77 | oneflg = 1; |
okini3939 | 0:8d19e2158eb4 | 78 | return; |
okini3939 | 0:8d19e2158eb4 | 79 | } else { |
okini3939 | 0:8d19e2158eb4 | 80 | oneflg = 0; |
okini3939 | 0:8d19e2158eb4 | 81 | b = 1; |
okini3939 | 0:8d19e2158eb4 | 82 | } |
okini3939 | 0:8d19e2158eb4 | 83 | } else |
okini3939 | 0:8d19e2158eb4 | 84 | if (t >= ZERO_TIME_MIN && t < ZERO_TIME_MAX) { |
okini3939 | 0:8d19e2158eb4 | 85 | oneflg = 0; |
okini3939 | 0:8d19e2158eb4 | 86 | b = 0; |
okini3939 | 2:13a89fffbb75 | 87 | |
okini3939 | 2:13a89fffbb75 | 88 | if (t >= FPS24_REF - 10 && t <= FPS24_REF + 10) { |
okini3939 | 2:13a89fffbb75 | 89 | fps = 0; |
okini3939 | 2:13a89fffbb75 | 90 | } else |
okini3939 | 2:13a89fffbb75 | 91 | if (t >= FPS25_REF - 10 && t <= FPS25_REF + 10) { |
okini3939 | 2:13a89fffbb75 | 92 | fps = 1; |
okini3939 | 2:13a89fffbb75 | 93 | } else |
okini3939 | 2:13a89fffbb75 | 94 | if (t >= FPS30_REF - 10 && t <= FPS30_REF + 10) { |
okini3939 | 2:13a89fffbb75 | 95 | fps = drop ? 2 : 3; // 29.97 / 30 |
okini3939 | 2:13a89fffbb75 | 96 | } |
okini3939 | 0:8d19e2158eb4 | 97 | } else { |
okini3939 | 2:13a89fffbb75 | 98 | oneflg = 0; |
okini3939 | 2:13a89fffbb75 | 99 | count = 0; |
okini3939 | 0:8d19e2158eb4 | 100 | return; |
okini3939 | 0:8d19e2158eb4 | 101 | } |
okini3939 | 0:8d19e2158eb4 | 102 | |
okini3939 | 2:13a89fffbb75 | 103 | for (int i = 0; i < 9; i ++) { |
okini3939 | 2:13a89fffbb75 | 104 | code[i] = (code[i] >> 1) | ((code[i + 1] & 1) ? 0x80 : 0); |
okini3939 | 2:13a89fffbb75 | 105 | } |
okini3939 | 2:13a89fffbb75 | 106 | code[9] = (code[9] >> 1) | (b ? 0x80 : 0); |
okini3939 | 2:13a89fffbb75 | 107 | count ++; |
okini3939 | 0:8d19e2158eb4 | 108 | |
okini3939 | 2:13a89fffbb75 | 109 | if (code[8] == 0xfc && code[9] == 0xbf && count >= 80) { |
okini3939 | 2:13a89fffbb75 | 110 | parse_code(); |
okini3939 | 2:13a89fffbb75 | 111 | count = 0; |
okini3939 | 0:8d19e2158eb4 | 112 | } |
okini3939 | 0:8d19e2158eb4 | 113 | } |
okini3939 | 0:8d19e2158eb4 | 114 | |
okini3939 | 0:8d19e2158eb4 | 115 | void LTC_SMPTE::parse_code () { |
okini3939 | 0:8d19e2158eb4 | 116 | frame = (code[1] & 0x03) * 10 + (code[0] & 0x0f); |
okini3939 | 0:8d19e2158eb4 | 117 | sec = (code[3] & 0x07) * 10 + (code[2] & 0x0f); |
okini3939 | 0:8d19e2158eb4 | 118 | min = (code[5] & 0x07) * 10 + (code[4] & 0x0f); |
okini3939 | 0:8d19e2158eb4 | 119 | hour = (code[7] & 0x03) * 10 + (code[6] & 0x0f); |
okini3939 | 0:8d19e2158eb4 | 120 | drop = code[1] & (1<<2) ? 1 : 0; |
okini3939 | 0:8d19e2158eb4 | 121 | received = 1; |
okini3939 | 0:8d19e2158eb4 | 122 | } |
okini3939 | 0:8d19e2158eb4 | 123 | |
okini3939 | 0:8d19e2158eb4 | 124 | void LTC_SMPTE::read (int *hour, int *min, int *sec, int *frame, int *dir) { |
okini3939 | 2:13a89fffbb75 | 125 | *hour = this->hour | (this->fps << 5); |
okini3939 | 0:8d19e2158eb4 | 126 | *min = this->min; |
okini3939 | 0:8d19e2158eb4 | 127 | *sec = this->sec; |
okini3939 | 0:8d19e2158eb4 | 128 | *frame = this->frame; |
okini3939 | 0:8d19e2158eb4 | 129 | if (dir) *dir = this->direction; |
okini3939 | 0:8d19e2158eb4 | 130 | received = 0; |
okini3939 | 0:8d19e2158eb4 | 131 | } |
okini3939 | 0:8d19e2158eb4 | 132 | |
okini3939 | 1:63ceee4bfd05 | 133 | void LTC_SMPTE::write (int hour, int min, int sec, int frame, int dir) { |
okini3939 | 1:63ceee4bfd05 | 134 | if (type != LTC_OUTPUT) return; |
okini3939 | 1:63ceee4bfd05 | 135 | |
okini3939 | 1:63ceee4bfd05 | 136 | US_TICKER_TIMER->TCR = 0; |
okini3939 | 1:63ceee4bfd05 | 137 | this->hour = hour; |
okini3939 | 1:63ceee4bfd05 | 138 | this->min = min; |
okini3939 | 1:63ceee4bfd05 | 139 | this->sec = sec; |
okini3939 | 1:63ceee4bfd05 | 140 | this->frame = frame; |
okini3939 | 1:63ceee4bfd05 | 141 | this->direction = dir; |
okini3939 | 1:63ceee4bfd05 | 142 | mode = 0; |
okini3939 | 1:63ceee4bfd05 | 143 | count = 0; |
okini3939 | 1:63ceee4bfd05 | 144 | |
okini3939 | 1:63ceee4bfd05 | 145 | US_TICKER_TIMER->TCR = 0x2; // reset |
okini3939 | 1:63ceee4bfd05 | 146 | // set match value |
okini3939 | 1:63ceee4bfd05 | 147 | US_TICKER_TIMER->MR0 = 1; |
okini3939 | 1:63ceee4bfd05 | 148 | // enable match interrupt |
okini3939 | 1:63ceee4bfd05 | 149 | US_TICKER_TIMER->MCR |= 1; |
okini3939 | 1:63ceee4bfd05 | 150 | US_TICKER_TIMER->TCR = 1; // enable = 1, reset = 0 |
okini3939 | 1:63ceee4bfd05 | 151 | |
okini3939 | 1:63ceee4bfd05 | 152 | _timer.reset(); |
okini3939 | 1:63ceee4bfd05 | 153 | _timer.start(); |
okini3939 | 1:63ceee4bfd05 | 154 | __enable_irq(); |
okini3939 | 1:63ceee4bfd05 | 155 | } |
okini3939 | 1:63ceee4bfd05 | 156 | |
okini3939 | 1:63ceee4bfd05 | 157 | void LTC_SMPTE::isr_ticker () { |
okini3939 | 1:63ceee4bfd05 | 158 | if (mode) { |
okini3939 | 1:63ceee4bfd05 | 159 | if (bit) { |
okini3939 | 1:63ceee4bfd05 | 160 | phase = !phase; |
okini3939 | 1:63ceee4bfd05 | 161 | _output->write(phase); |
okini3939 | 1:63ceee4bfd05 | 162 | } |
okini3939 | 1:63ceee4bfd05 | 163 | mode = 0; |
okini3939 | 1:63ceee4bfd05 | 164 | return; |
okini3939 | 1:63ceee4bfd05 | 165 | } |
okini3939 | 1:63ceee4bfd05 | 166 | |
okini3939 | 1:63ceee4bfd05 | 167 | phase = !phase; |
okini3939 | 1:63ceee4bfd05 | 168 | _output->write(phase); |
okini3939 | 1:63ceee4bfd05 | 169 | mode = 1; |
okini3939 | 1:63ceee4bfd05 | 170 | |
okini3939 | 1:63ceee4bfd05 | 171 | if (count >= 0 && count <= 3) { |
okini3939 | 1:63ceee4bfd05 | 172 | bit = (frame % 10) & (1 << count); |
okini3939 | 1:63ceee4bfd05 | 173 | } else |
okini3939 | 1:63ceee4bfd05 | 174 | if (count >= 8 && count <= 9) { |
okini3939 | 1:63ceee4bfd05 | 175 | bit = (frame / 10) & (1 << (count - 8)); |
okini3939 | 1:63ceee4bfd05 | 176 | } else |
okini3939 | 1:63ceee4bfd05 | 177 | if (count >= 16 && count <= 19) { |
okini3939 | 1:63ceee4bfd05 | 178 | bit = (sec % 10) & (1 << (count - 16)); |
okini3939 | 1:63ceee4bfd05 | 179 | } else |
okini3939 | 1:63ceee4bfd05 | 180 | if (count >= 24 && count <= 26) { |
okini3939 | 1:63ceee4bfd05 | 181 | bit = (sec / 10) & (1 << (count - 24)); |
okini3939 | 1:63ceee4bfd05 | 182 | } else |
okini3939 | 1:63ceee4bfd05 | 183 | if (count >= 32 && count <= 35) { |
okini3939 | 1:63ceee4bfd05 | 184 | bit = (min % 10) & (1 << (count - 32)); |
okini3939 | 1:63ceee4bfd05 | 185 | } else |
okini3939 | 1:63ceee4bfd05 | 186 | if (count >= 40 && count <= 42) { |
okini3939 | 1:63ceee4bfd05 | 187 | bit = (min / 10) & (1 << (count - 40)); |
okini3939 | 1:63ceee4bfd05 | 188 | } else |
okini3939 | 1:63ceee4bfd05 | 189 | if (count >= 48 && count <= 51) { |
okini3939 | 1:63ceee4bfd05 | 190 | bit = (hour % 10) & (1 << (count - 48)); |
okini3939 | 1:63ceee4bfd05 | 191 | } else |
okini3939 | 1:63ceee4bfd05 | 192 | if (count >= 56 && count <= 57) { |
okini3939 | 1:63ceee4bfd05 | 193 | bit = (hour / 10) & (1 << (count - 56)); |
okini3939 | 1:63ceee4bfd05 | 194 | } else |
okini3939 | 1:63ceee4bfd05 | 195 | if ((count >= 66 && count <= 77) || count == 79) { |
okini3939 | 1:63ceee4bfd05 | 196 | bit = 1; |
okini3939 | 1:63ceee4bfd05 | 197 | } else { |
okini3939 | 1:63ceee4bfd05 | 198 | bit = 0; |
okini3939 | 1:63ceee4bfd05 | 199 | } |
okini3939 | 1:63ceee4bfd05 | 200 | |
okini3939 | 1:63ceee4bfd05 | 201 | count ++; |
okini3939 | 1:63ceee4bfd05 | 202 | if (count >= 80) { |
okini3939 | 1:63ceee4bfd05 | 203 | count = 0; |
okini3939 | 1:63ceee4bfd05 | 204 | frame ++; |
okini3939 | 1:63ceee4bfd05 | 205 | if (frame >= 30) { |
okini3939 | 1:63ceee4bfd05 | 206 | frame = 0; |
okini3939 | 1:63ceee4bfd05 | 207 | sec ++; |
okini3939 | 1:63ceee4bfd05 | 208 | if (sec >= 60) { |
okini3939 | 1:63ceee4bfd05 | 209 | sec = 0; |
okini3939 | 1:63ceee4bfd05 | 210 | min ++; |
okini3939 | 1:63ceee4bfd05 | 211 | if (min >= 60) { |
okini3939 | 1:63ceee4bfd05 | 212 | min = 0; |
okini3939 | 1:63ceee4bfd05 | 213 | hour ++; |
okini3939 | 1:63ceee4bfd05 | 214 | if (hour >= 24) { |
okini3939 | 1:63ceee4bfd05 | 215 | hour = 0; |
okini3939 | 1:63ceee4bfd05 | 216 | } |
okini3939 | 1:63ceee4bfd05 | 217 | } |
okini3939 | 1:63ceee4bfd05 | 218 | } |
okini3939 | 1:63ceee4bfd05 | 219 | } |
okini3939 | 1:63ceee4bfd05 | 220 | } |
okini3939 | 1:63ceee4bfd05 | 221 | } |
okini3939 | 1:63ceee4bfd05 | 222 | |
okini3939 | 0:8d19e2158eb4 | 223 | int LTC_SMPTE::isReceived () { |
okini3939 | 0:8d19e2158eb4 | 224 | return received; |
okini3939 | 0:8d19e2158eb4 | 225 | } |