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@1:63ceee4bfd05, 2018-04-13 (annotated)
- Committer:
- okini3939
- Date:
- Fri Apr 13 04:05:13 2018 +0000
- Revision:
- 1:63ceee4bfd05
- Parent:
- 0:8d19e2158eb4
- Child:
- 2:13a89fffbb75
supported LTC output;
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 | 0:8d19e2158eb4 | 18 | #define ONE_TIME_MIN 100 |
okini3939 | 0:8d19e2158eb4 | 19 | #define ONE_TIME_MAX 250 |
okini3939 | 0:8d19e2158eb4 | 20 | #define ZERO_TIME_MIN 300 |
okini3939 | 0:8d19e2158eb4 | 21 | #define ZERO_TIME_MAX 500 |
okini3939 | 0:8d19e2158eb4 | 22 | |
okini3939 | 1:63ceee4bfd05 | 23 | #define US_TICKER_TIMER ((LPC_TIM_TypeDef *)LPC_TIM2_BASE) |
okini3939 | 1:63ceee4bfd05 | 24 | #define US_TICKER_TIMER_IRQn TIMER2_IRQn |
okini3939 | 1:63ceee4bfd05 | 25 | |
okini3939 | 1:63ceee4bfd05 | 26 | LTC_SMPTE *LTC_SMPTE::_inst; |
okini3939 | 0:8d19e2158eb4 | 27 | |
okini3939 | 1:63ceee4bfd05 | 28 | void isrTimer () { |
okini3939 | 1:63ceee4bfd05 | 29 | US_TICKER_TIMER->TC = 0; |
okini3939 | 1:63ceee4bfd05 | 30 | US_TICKER_TIMER->IR = 1; |
okini3939 | 1:63ceee4bfd05 | 31 | LTC_SMPTE::_inst->isr_ticker(); |
okini3939 | 1:63ceee4bfd05 | 32 | } |
okini3939 | 1:63ceee4bfd05 | 33 | |
okini3939 | 1:63ceee4bfd05 | 34 | LTC_SMPTE::LTC_SMPTE (PinName pin, enum LTC_TYPE type) { |
okini3939 | 1:63ceee4bfd05 | 35 | |
okini3939 | 1:63ceee4bfd05 | 36 | _inst = this; |
okini3939 | 0:8d19e2158eb4 | 37 | mode = 0; |
okini3939 | 0:8d19e2158eb4 | 38 | count = 0; |
okini3939 | 0:8d19e2158eb4 | 39 | oneflg = 0; |
okini3939 | 0:8d19e2158eb4 | 40 | direction = 0; |
okini3939 | 0:8d19e2158eb4 | 41 | received = 0; |
okini3939 | 0:8d19e2158eb4 | 42 | |
okini3939 | 1:63ceee4bfd05 | 43 | this->type = type; |
okini3939 | 1:63ceee4bfd05 | 44 | if (type == LTC_INPUT) { |
okini3939 | 1:63ceee4bfd05 | 45 | _input = new InterruptIn(pin); |
okini3939 | 1:63ceee4bfd05 | 46 | _input->mode(PullUp); |
okini3939 | 1:63ceee4bfd05 | 47 | _input->fall(this, <C_SMPTE::isr_change); |
okini3939 | 1:63ceee4bfd05 | 48 | _input->rise(this, <C_SMPTE::isr_change); |
okini3939 | 1:63ceee4bfd05 | 49 | |
okini3939 | 1:63ceee4bfd05 | 50 | _timer.start(); |
okini3939 | 1:63ceee4bfd05 | 51 | } else { |
okini3939 | 1:63ceee4bfd05 | 52 | _output = new DigitalOut(pin); |
okini3939 | 1:63ceee4bfd05 | 53 | |
okini3939 | 1:63ceee4bfd05 | 54 | LPC_SC->PCONP |= 1 << 22; // Clock TIMER_2 |
okini3939 | 1:63ceee4bfd05 | 55 | US_TICKER_TIMER->CTCR = 0x0; // timer mode |
okini3939 | 1:63ceee4bfd05 | 56 | uint32_t PCLK = SystemCoreClock / 4; |
okini3939 | 1:63ceee4bfd05 | 57 | US_TICKER_TIMER->TCR = 0x2; // reset |
okini3939 | 1:63ceee4bfd05 | 58 | uint32_t prescale = PCLK / 2400 / 2; // 80bit * 30frame |
okini3939 | 1:63ceee4bfd05 | 59 | US_TICKER_TIMER->PR = prescale - 1; |
okini3939 | 1:63ceee4bfd05 | 60 | NVIC_SetVector(US_TICKER_TIMER_IRQn, (uint32_t)&isrTimer); |
okini3939 | 1:63ceee4bfd05 | 61 | NVIC_EnableIRQ(US_TICKER_TIMER_IRQn); |
okini3939 | 1:63ceee4bfd05 | 62 | } |
okini3939 | 0:8d19e2158eb4 | 63 | } |
okini3939 | 0:8d19e2158eb4 | 64 | |
okini3939 | 0:8d19e2158eb4 | 65 | void LTC_SMPTE::isr_change () { |
okini3939 | 0:8d19e2158eb4 | 66 | int b, t; |
okini3939 | 0:8d19e2158eb4 | 67 | |
okini3939 | 0:8d19e2158eb4 | 68 | t = _timer.read_us(); |
okini3939 | 0:8d19e2158eb4 | 69 | _timer.reset(); |
okini3939 | 0:8d19e2158eb4 | 70 | |
okini3939 | 0:8d19e2158eb4 | 71 | if (t >= ONE_TIME_MIN && t < ONE_TIME_MAX) { |
okini3939 | 0:8d19e2158eb4 | 72 | if (oneflg == 0) { |
okini3939 | 0:8d19e2158eb4 | 73 | oneflg = 1; |
okini3939 | 0:8d19e2158eb4 | 74 | return; |
okini3939 | 0:8d19e2158eb4 | 75 | } else { |
okini3939 | 0:8d19e2158eb4 | 76 | oneflg = 0; |
okini3939 | 0:8d19e2158eb4 | 77 | b = 1; |
okini3939 | 0:8d19e2158eb4 | 78 | } |
okini3939 | 0:8d19e2158eb4 | 79 | } else |
okini3939 | 0:8d19e2158eb4 | 80 | if (t >= ZERO_TIME_MIN && t < ZERO_TIME_MAX) { |
okini3939 | 0:8d19e2158eb4 | 81 | oneflg = 0; |
okini3939 | 0:8d19e2158eb4 | 82 | b = 0; |
okini3939 | 0:8d19e2158eb4 | 83 | } else { |
okini3939 | 0:8d19e2158eb4 | 84 | return; |
okini3939 | 0:8d19e2158eb4 | 85 | } |
okini3939 | 0:8d19e2158eb4 | 86 | |
okini3939 | 0:8d19e2158eb4 | 87 | if (mode == 0) { |
okini3939 | 0:8d19e2158eb4 | 88 | // sync word |
okini3939 | 0:8d19e2158eb4 | 89 | if (b) { |
okini3939 | 0:8d19e2158eb4 | 90 | // 1 |
okini3939 | 0:8d19e2158eb4 | 91 | if (count == 12) { |
okini3939 | 0:8d19e2158eb4 | 92 | // error |
okini3939 | 0:8d19e2158eb4 | 93 | count = 0; |
okini3939 | 0:8d19e2158eb4 | 94 | } else |
okini3939 | 0:8d19e2158eb4 | 95 | if (count == 13) { |
okini3939 | 0:8d19e2158eb4 | 96 | // fwd |
okini3939 | 0:8d19e2158eb4 | 97 | direction = 1; |
okini3939 | 0:8d19e2158eb4 | 98 | count = 0; |
okini3939 | 0:8d19e2158eb4 | 99 | mode = 1; |
okini3939 | 0:8d19e2158eb4 | 100 | bit = 1; |
okini3939 | 0:8d19e2158eb4 | 101 | } else { |
okini3939 | 0:8d19e2158eb4 | 102 | count ++; |
okini3939 | 0:8d19e2158eb4 | 103 | } |
okini3939 | 0:8d19e2158eb4 | 104 | } else { |
okini3939 | 0:8d19e2158eb4 | 105 | // 0 |
okini3939 | 0:8d19e2158eb4 | 106 | if (count == 12) { |
okini3939 | 0:8d19e2158eb4 | 107 | count ++; |
okini3939 | 0:8d19e2158eb4 | 108 | } else |
okini3939 | 0:8d19e2158eb4 | 109 | if (count == 13) { |
okini3939 | 0:8d19e2158eb4 | 110 | // rev |
okini3939 | 0:8d19e2158eb4 | 111 | direction = -1; |
okini3939 | 0:8d19e2158eb4 | 112 | count = 0; |
okini3939 | 0:8d19e2158eb4 | 113 | mode = 1; |
okini3939 | 0:8d19e2158eb4 | 114 | bit = 1; |
okini3939 | 0:8d19e2158eb4 | 115 | } else { |
okini3939 | 0:8d19e2158eb4 | 116 | // error |
okini3939 | 0:8d19e2158eb4 | 117 | count = 0; |
okini3939 | 0:8d19e2158eb4 | 118 | } |
okini3939 | 0:8d19e2158eb4 | 119 | } |
okini3939 | 0:8d19e2158eb4 | 120 | |
okini3939 | 0:8d19e2158eb4 | 121 | } else { |
okini3939 | 0:8d19e2158eb4 | 122 | // time code |
okini3939 | 0:8d19e2158eb4 | 123 | if (b) { |
okini3939 | 0:8d19e2158eb4 | 124 | code[count] |= bit; // 1 |
okini3939 | 0:8d19e2158eb4 | 125 | } else { |
okini3939 | 0:8d19e2158eb4 | 126 | code[count] &= ~bit; // 0 |
okini3939 | 0:8d19e2158eb4 | 127 | } |
okini3939 | 0:8d19e2158eb4 | 128 | bit <<= 1; |
okini3939 | 0:8d19e2158eb4 | 129 | if (bit >= 0x100) { |
okini3939 | 0:8d19e2158eb4 | 130 | bit = 1; |
okini3939 | 0:8d19e2158eb4 | 131 | count ++; |
okini3939 | 0:8d19e2158eb4 | 132 | if (count >= 8) { |
okini3939 | 0:8d19e2158eb4 | 133 | count = 0; |
okini3939 | 0:8d19e2158eb4 | 134 | mode = 0; |
okini3939 | 0:8d19e2158eb4 | 135 | parse_code(); |
okini3939 | 0:8d19e2158eb4 | 136 | } |
okini3939 | 0:8d19e2158eb4 | 137 | } |
okini3939 | 0:8d19e2158eb4 | 138 | } |
okini3939 | 0:8d19e2158eb4 | 139 | } |
okini3939 | 0:8d19e2158eb4 | 140 | |
okini3939 | 0:8d19e2158eb4 | 141 | void LTC_SMPTE::parse_code () { |
okini3939 | 0:8d19e2158eb4 | 142 | frame = (code[1] & 0x03) * 10 + (code[0] & 0x0f); |
okini3939 | 0:8d19e2158eb4 | 143 | sec = (code[3] & 0x07) * 10 + (code[2] & 0x0f); |
okini3939 | 0:8d19e2158eb4 | 144 | min = (code[5] & 0x07) * 10 + (code[4] & 0x0f); |
okini3939 | 0:8d19e2158eb4 | 145 | hour = (code[7] & 0x03) * 10 + (code[6] & 0x0f); |
okini3939 | 0:8d19e2158eb4 | 146 | drop = code[1] & (1<<2) ? 1 : 0; |
okini3939 | 0:8d19e2158eb4 | 147 | received = 1; |
okini3939 | 0:8d19e2158eb4 | 148 | } |
okini3939 | 0:8d19e2158eb4 | 149 | |
okini3939 | 0:8d19e2158eb4 | 150 | void LTC_SMPTE::read (int *hour, int *min, int *sec, int *frame, int *dir) { |
okini3939 | 0:8d19e2158eb4 | 151 | *hour = this->hour; |
okini3939 | 0:8d19e2158eb4 | 152 | *min = this->min; |
okini3939 | 0:8d19e2158eb4 | 153 | *sec = this->sec; |
okini3939 | 0:8d19e2158eb4 | 154 | *frame = this->frame; |
okini3939 | 0:8d19e2158eb4 | 155 | if (dir) *dir = this->direction; |
okini3939 | 0:8d19e2158eb4 | 156 | received = 0; |
okini3939 | 0:8d19e2158eb4 | 157 | } |
okini3939 | 0:8d19e2158eb4 | 158 | |
okini3939 | 1:63ceee4bfd05 | 159 | void LTC_SMPTE::write (int hour, int min, int sec, int frame, int dir) { |
okini3939 | 1:63ceee4bfd05 | 160 | if (type != LTC_OUTPUT) return; |
okini3939 | 1:63ceee4bfd05 | 161 | |
okini3939 | 1:63ceee4bfd05 | 162 | US_TICKER_TIMER->TCR = 0; |
okini3939 | 1:63ceee4bfd05 | 163 | this->hour = hour; |
okini3939 | 1:63ceee4bfd05 | 164 | this->min = min; |
okini3939 | 1:63ceee4bfd05 | 165 | this->sec = sec; |
okini3939 | 1:63ceee4bfd05 | 166 | this->frame = frame; |
okini3939 | 1:63ceee4bfd05 | 167 | this->direction = dir; |
okini3939 | 1:63ceee4bfd05 | 168 | mode = 0; |
okini3939 | 1:63ceee4bfd05 | 169 | count = 0; |
okini3939 | 1:63ceee4bfd05 | 170 | |
okini3939 | 1:63ceee4bfd05 | 171 | US_TICKER_TIMER->TCR = 0x2; // reset |
okini3939 | 1:63ceee4bfd05 | 172 | // set match value |
okini3939 | 1:63ceee4bfd05 | 173 | US_TICKER_TIMER->MR0 = 1; |
okini3939 | 1:63ceee4bfd05 | 174 | // enable match interrupt |
okini3939 | 1:63ceee4bfd05 | 175 | US_TICKER_TIMER->MCR |= 1; |
okini3939 | 1:63ceee4bfd05 | 176 | US_TICKER_TIMER->TCR = 1; // enable = 1, reset = 0 |
okini3939 | 1:63ceee4bfd05 | 177 | |
okini3939 | 1:63ceee4bfd05 | 178 | _timer.reset(); |
okini3939 | 1:63ceee4bfd05 | 179 | _timer.start(); |
okini3939 | 1:63ceee4bfd05 | 180 | __enable_irq(); |
okini3939 | 1:63ceee4bfd05 | 181 | } |
okini3939 | 1:63ceee4bfd05 | 182 | |
okini3939 | 1:63ceee4bfd05 | 183 | void LTC_SMPTE::isr_ticker () { |
okini3939 | 1:63ceee4bfd05 | 184 | if (mode) { |
okini3939 | 1:63ceee4bfd05 | 185 | if (bit) { |
okini3939 | 1:63ceee4bfd05 | 186 | phase = !phase; |
okini3939 | 1:63ceee4bfd05 | 187 | _output->write(phase); |
okini3939 | 1:63ceee4bfd05 | 188 | } |
okini3939 | 1:63ceee4bfd05 | 189 | mode = 0; |
okini3939 | 1:63ceee4bfd05 | 190 | return; |
okini3939 | 1:63ceee4bfd05 | 191 | } |
okini3939 | 1:63ceee4bfd05 | 192 | |
okini3939 | 1:63ceee4bfd05 | 193 | phase = !phase; |
okini3939 | 1:63ceee4bfd05 | 194 | _output->write(phase); |
okini3939 | 1:63ceee4bfd05 | 195 | mode = 1; |
okini3939 | 1:63ceee4bfd05 | 196 | |
okini3939 | 1:63ceee4bfd05 | 197 | if (count >= 0 && count <= 3) { |
okini3939 | 1:63ceee4bfd05 | 198 | bit = (frame % 10) & (1 << count); |
okini3939 | 1:63ceee4bfd05 | 199 | } else |
okini3939 | 1:63ceee4bfd05 | 200 | if (count >= 8 && count <= 9) { |
okini3939 | 1:63ceee4bfd05 | 201 | bit = (frame / 10) & (1 << (count - 8)); |
okini3939 | 1:63ceee4bfd05 | 202 | } else |
okini3939 | 1:63ceee4bfd05 | 203 | if (count >= 16 && count <= 19) { |
okini3939 | 1:63ceee4bfd05 | 204 | bit = (sec % 10) & (1 << (count - 16)); |
okini3939 | 1:63ceee4bfd05 | 205 | } else |
okini3939 | 1:63ceee4bfd05 | 206 | if (count >= 24 && count <= 26) { |
okini3939 | 1:63ceee4bfd05 | 207 | bit = (sec / 10) & (1 << (count - 24)); |
okini3939 | 1:63ceee4bfd05 | 208 | } else |
okini3939 | 1:63ceee4bfd05 | 209 | if (count >= 32 && count <= 35) { |
okini3939 | 1:63ceee4bfd05 | 210 | bit = (min % 10) & (1 << (count - 32)); |
okini3939 | 1:63ceee4bfd05 | 211 | } else |
okini3939 | 1:63ceee4bfd05 | 212 | if (count >= 40 && count <= 42) { |
okini3939 | 1:63ceee4bfd05 | 213 | bit = (min / 10) & (1 << (count - 40)); |
okini3939 | 1:63ceee4bfd05 | 214 | } else |
okini3939 | 1:63ceee4bfd05 | 215 | if (count >= 48 && count <= 51) { |
okini3939 | 1:63ceee4bfd05 | 216 | bit = (hour % 10) & (1 << (count - 48)); |
okini3939 | 1:63ceee4bfd05 | 217 | } else |
okini3939 | 1:63ceee4bfd05 | 218 | if (count >= 56 && count <= 57) { |
okini3939 | 1:63ceee4bfd05 | 219 | bit = (hour / 10) & (1 << (count - 56)); |
okini3939 | 1:63ceee4bfd05 | 220 | } else |
okini3939 | 1:63ceee4bfd05 | 221 | if ((count >= 66 && count <= 77) || count == 79) { |
okini3939 | 1:63ceee4bfd05 | 222 | bit = 1; |
okini3939 | 1:63ceee4bfd05 | 223 | } else { |
okini3939 | 1:63ceee4bfd05 | 224 | bit = 0; |
okini3939 | 1:63ceee4bfd05 | 225 | } |
okini3939 | 1:63ceee4bfd05 | 226 | |
okini3939 | 1:63ceee4bfd05 | 227 | count ++; |
okini3939 | 1:63ceee4bfd05 | 228 | if (count >= 80) { |
okini3939 | 1:63ceee4bfd05 | 229 | count = 0; |
okini3939 | 1:63ceee4bfd05 | 230 | frame ++; |
okini3939 | 1:63ceee4bfd05 | 231 | if (frame >= 30) { |
okini3939 | 1:63ceee4bfd05 | 232 | frame = 0; |
okini3939 | 1:63ceee4bfd05 | 233 | sec ++; |
okini3939 | 1:63ceee4bfd05 | 234 | if (sec >= 60) { |
okini3939 | 1:63ceee4bfd05 | 235 | sec = 0; |
okini3939 | 1:63ceee4bfd05 | 236 | min ++; |
okini3939 | 1:63ceee4bfd05 | 237 | if (min >= 60) { |
okini3939 | 1:63ceee4bfd05 | 238 | min = 0; |
okini3939 | 1:63ceee4bfd05 | 239 | hour ++; |
okini3939 | 1:63ceee4bfd05 | 240 | if (hour >= 24) { |
okini3939 | 1:63ceee4bfd05 | 241 | hour = 0; |
okini3939 | 1:63ceee4bfd05 | 242 | } |
okini3939 | 1:63ceee4bfd05 | 243 | } |
okini3939 | 1:63ceee4bfd05 | 244 | } |
okini3939 | 1:63ceee4bfd05 | 245 | } |
okini3939 | 1:63ceee4bfd05 | 246 | } |
okini3939 | 1:63ceee4bfd05 | 247 | } |
okini3939 | 1:63ceee4bfd05 | 248 | |
okini3939 | 0:8d19e2158eb4 | 249 | int LTC_SMPTE::isReceived () { |
okini3939 | 0:8d19e2158eb4 | 250 | return received; |
okini3939 | 0:8d19e2158eb4 | 251 | } |