SMPTE timecode reader with LCD display

Hi Guy's I posted a little while ago on here and you were really helpful. And i've managed to adapt some code to get it working on an lcd display.
What I would like some extra help with is firstly is there a way of defining the one_time_max etc different depending on the high low state of a pin. This way i could use a switch to change the times so it could switch between NTSC and PAL.
Secondly would anyone be kind enough to explain what is happening in the middle part of this code. I understand that there is a volatile boolean which can be true of false. But later in the code you make an IF statement against it and I don't fully understand how that works. Any help would be appreciated.
Here is a picture of how it's going so far. I'll keep you guys up to date as the project continues :slight_smile:

Imgur: The magic of the Internet [1]

// 
//
// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);


#define one_time_max          600 // these values are setup for PA video
#define one_time_min          400 // It's the durstion of a one and zero with a little bit of room for error. 
#define zero_time_max          1050 // 
#define zero_time_min          950 // 


#define icpPin 8      // ICP input pin on arduino
//#define one_time_max      475 // these values are setup for NTSC video
//#define one_time_min      300 // PAL would be around 1000 for 0 and 500 for 1
//#define zero_time_max    875 // 80bits times 29.97 frames per sec
//#define zero_time_min    700 // equals 833 (divide by 8 clock pulses)

#define end_data_position   63
#define end_sync_position   77
#define end_smpte_position     80

volatile unsigned int pin = 13;
volatile unsigned int bit_time;   // volatile instructs the variable to be stored in RAM
volatile boolean valid_tc_word;   // booleon can be either of two values true or false
volatile boolean ones_bit_count;  // booleon can be either of two values true or false
volatile boolean tc_sync;         // booleon can be either of two values true or false
volatile boolean write_tc_out;    // booleon can be either of two values true or false
volatile boolean drop_frame_flag; // booleon can be either of two values true or false

volatile byte total_bits;   //this stores a an 8-bit unsigned number
volatile byte current_bit;  //this stores a an 8-bit unsigned number
volatile byte sync_count;   //this stores a an 8-bit unsigned number

volatile byte tc[8];         //this stores a an 8-bit unsigned number
volatile char timeCode[11];  //this stores a an 8-bit unsigned number


/* ICR interrupt vector */
ISR(TIMER1_CAPT_vect)   //ISR=Interrupt Service Routine, and timer1 capture event
{
  //toggleCaptureEdge
  TCCR1B ^= _BV(ICES1); //toggles the edge that triggers the handler so that the duration of both high and low pulses is measured.

  bit_time = ICR1; //this is the value the timer generates 

  //resetTimer1
  TCNT1 = 0;

  if ((bit_time < one_time_min) || (bit_time > zero_time_max)) // this gets rid of anything that's not what we're looking for
  {
    total_bits = 0;
  }
  else
  {
    if (ones_bit_count == true) // only count the second ones pluse
    ones_bit_count = false;
    else
    {
    if (bit_time > zero_time_min)
    {
      current_bit = 0;
      sync_count = 0;
    }
    else //if (bit_time < one_time_max)
    {
      ones_bit_count = true;
      current_bit = 1;
      sync_count++;
      if (sync_count == 12) // part of the last two bytes of a timecode word
      {
        sync_count = 0;
        tc_sync = true;
        total_bits = end_sync_position;
      }
    }

    if (total_bits <= end_data_position) // timecode runs least to most so we need
    {                       // to shift things around
      tc[0] = tc[0] >> 1;

      for(int n=1;n<8;n++) //creates tc[1-8]
      {
        if(tc[n] & 1)
        tc[n-1] |= 0x80;

        tc[n] = tc[n] >> 1;
      }

      if(current_bit == 1)
        tc[7] |= 0x80;
    }
    total_bits++;
    }

    if (total_bits == end_smpte_position) // we have the 80th bit
    {
    total_bits = 0;
    if (tc_sync)
    {
      tc_sync = false;
      valid_tc_word = true;
    }
    }

    if (valid_tc_word)
    {
    valid_tc_word = false;

    timeCode[10] = (tc[0]&0x0F)+0x30;   // frames  this converst from binary to decimal giving us the last digit
    timeCode[9] = (tc[1]&0x03)+0x30;    // 10's of frames this converst from binary to decimal giving us the first digit
    timeCode[8] =  ':';
    timeCode[7] = (tc[2]&0x0F)+0x30;    // seconds
    timeCode[6] = (tc[3]&0x07)+0x30;    // 10's of seconds
    timeCode[5] =  ':';
    timeCode[4] = (tc[4]&0x0F)+0x30;    // minutes
    timeCode[3] = (tc[5]&0x07)+0x30;    // 10's of minutes
    timeCode[2] = ':';
    timeCode[1] = (tc[6]&0x0F)+0x30;    // hours
    timeCode[0] = (tc[7]&0x03)+0x30;    // 10's of hours

    drop_frame_flag = bit_is_set(tc[1], 2); //detects whether theree is the drop frame bit.

    write_tc_out = true;
    }
  }
}


void setup()
{
  lcd.begin (16, 2);
  pinMode(icpPin, INPUT);           // ICP pin (digital pin 8 on arduino) as input

  bit_time = 0;
  valid_tc_word = false;
  ones_bit_count = false;
  tc_sync = false;
  write_tc_out = false;
  drop_frame_flag = false;
  total_bits =  0;
  current_bit =  0;
  sync_count =  0;

lcd.print("Finished setup");
delay (1000);

  TCCR1A = B00000000; // clear all
  TCCR1B = B11000010; // ICNC1 noise reduction + ICES1 start on rising edge + CS11 divide by 8
  TCCR1C = B00000000; // clear all
  TIMSK1 = B00100000; // ICIE1 enable the icp

  TCNT1 = 0; // clear timer1
}

void loop()
{
    if (write_tc_out)
    {
    write_tc_out = false;
    if (drop_frame_flag)
      lcd.print("TC-[df] ");
    else
      lcd.print("TC-NO DROP FRAME");
lcd.setCursor(0, 1);
    lcd.print((char*)timeCode);
    lcd.print("\r");
lcd.setCursor(11, 1);
lcd.print("......");
delay (40);
lcd.clear(); } }
1 Like

Well, any reason you could have an if statement in the setup portion of the sketch, so that it checks the state of the pin and sets min and max values? The only down side is that you'd have to reset the arduino when you wanted to change them again. So how about using an interrupt to check the state of the switch when it changes?

Hi, would you be able to provide a schematic of the hardware that you've got the LTC plugged into?

I'd like to have play with this, and think that it should be possible to make it auto-detect the Frame rate, so you won't have to worry about switching between NTSC or PAL

Here's the input circuit.

I'm struggling to get the code working though, it seems to hang on "finished setup".

It's on an UNO version3.

Hi,
Im a bit new to arduino, but a i have an interest your project.

I seen in your code that you do not have #include <avr/interrupt.h> in your code. avr-libc: <avr/interrupt.h>: Interrupts. This could be why that function is not being called.

I'd like to revive this topic a bit. Im trying to reproduce it to make a Nixie timecode clock. It looks like both of the bits of code at the beginning are to define NTSC 29.976 and PAL. How did you get these numbers i need to adapt the code for 23.976 NTSC. Also has the schematic for input changed at all from the post above?
thanks!