Thursday, June 16, 2011

AVR SMPTE ltc (audio time code) generator

This uses OCRA to generate a SMPTE time code waveform starting at 0 using a 16 bit counter.
The timing is set for a 12Mhz ceramic resonator, ports are set for a ATtiny4313.  Released under GPLv3
Cut Here ---------------- 
 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <string.h>
#define CPUFREQ 12000000 

unsigned char frame = 0, secs = 0, mins = 0, hour = 0;
void incsmpte()
{
    frame++;
    if ((frame & 0x0f) > 9)
        frame += 6;
    if (frame < 0x30)
        return;
    frame = 0;
    secs++;
    if ((secs & 0x0f) > 9)
        secs += 6;
    if (secs < 0x60)
        return;
    secs = 0;
    mins++;
    if ((mins & 0x0f) > 9)
        mins += 6;
    if (mins < 0x60)
        return;
    mins = 0;
    hour++;
    if ((hour & 0x0f) > 9)
        hour += 6;
    if (hour < 0x24)
        return;
    hour = 0;
}

unsigned char user[8] = {2,4,6,8,1,3,5,7};
static unsigned char block[10];
void fillblock()
{
    block[0] = (user[0] << 4) | ( frame & 0xf );
    block[1] = (user[1] << 4) | ( frame >> 4 );
    block[2] = (user[2] << 4) | ( secs & 0xf );
    block[3] = (user[3] << 4) | ( secs >> 4 );
    block[4] = (user[4] << 4) | ( mins & 0xf );
    block[5] = (user[5] << 4) | ( mins >> 4 );
    block[6] = (user[6] << 4) | ( hour & 0xf );
    block[7] = (user[7] << 4) | ( hour >> 4 );
    block[8] = 0xfc;
    block[9] = 0xbf;

    unsigned char par, i;
    par = 1; // last two constants
    for (i = 0; i < 8; i++)
        par ^= block[i];
    par ^= par >> 4;
    par ^= par >> 2;
    par ^= par >> 1;
    if (par & 1)
        block[3] |= 8;
}

unsigned char step = 0;
unsigned char oneflg = 0;
#define WIUONE (CPUFREQ/4800)
ISR(TIMER1_COMPA_vect)
{
    if (oneflg) {
        OCR1A += WIDONE;
        oneflg = 0;
        return;
    }
    if (1 & (block[step >> 3] >> (step & 7)))
        oneflg = 1;
    if (oneflg)
        OCR1A += WIDONE;
    else
        OCR1A += WIDONE*2;
    step++;
    if (step == 80)
        step = 0;
}

unsigned hitime = 0;
ISR(TIMER1_OVF_vect)
{
    hitime++;
}

ISR(TIMER1_CAPT_vect)
{
}

/**************************************************************************/
int main(void)
{
    MCUCR = 0x20;               // enable sleep as idle
    PRR = 0x06;                 // shut down USI and Timer 0
    BODCR = 3;                  // disable brown-out detect during sleep
    BODCR = 2;
    DIDR = 3;
    ACSR = 0x80;

    // Select clock, etc for timer |1 = xtal/1, |2 = xtal/8,,,
    GTCCR = 0x81;               // stop for reset - normally to sync multiples
    TCNT1 = 0;
    TCCR1A = 0xA0;
    TCCR1C = 0xC0;              // force clear
    TCCR1A = 0x50;              // now toggle
    TCCR1B = 0xC0 | 1;          // NoiseC, edge ...  off,1,8,64,256,1024,ext
    GTCCR = 0x80;               // start

    TIFR |= 0xC8;               // reset interrupt triggers
    TIMSK |= 0xC8;              // enable interrupt
    DDRB = 0x18;
 
// Not adjusted for CPU Freq - not using UART yet
    UBRRL = 12;                 //div 13 for 115200 baud from 12MHz
    UBRRH = 0;

    UCSRC = 0x06;               //8N1 (should be this from reset)
    UCSRA = 0xE2;               // clear Interrupts, UART at 2x (xtal/8)
    UCSRB = 0x18;               // oring in 0x80 would enable rx interrupt

    sei();

    for (;;) {
        sleep_mode();           // INT will waken
        if (step)
            continue;
        incsmpte();
        fillblock();
    }
}

No comments:

Post a Comment