Page 1 of 1

atmega168 Timer issue

Posted: Mon Aug 17, 2009 12:31 pm
by regomodo
Hi,
I am trying to replicate an infrared trigger for a Nikon dslr as described here.

It basically seems to consist of switching an IR led with these timings.
Code: Select all
ON     off     ON    off    ON    off    ON    off
2250us 27600us 650us 1375us 575us 3350us 650us
Once it's done that it repeats once more after 63ms. Additionally, for an ON state the IR has to cycled at ~40Khz.

I cannot for the life of me get my code to work. It definitely does something as evidenced by a red LED I tried but it won't trigger. Was hoping somebody might have a trawl through my code as i've gone through it over and over and can't see anything wrong.

Here is the code i've got. I'm very new to C and avr's so i'm unaware of bad practices right now.
Code: Select all
// mll3 infrared transmitter

// have a feeling util/delay requires timer0 with no prescale
// making do without it

#include <avr/io.h>
#include <avr/interrupt.h>


// setting up a 1MHz clock signal for creating counters
void realtimeclock_setup() {
	TCCR0A |= (1<<WGM01);
	TCCR0B |= (1<<CS01); // 0.5us or 2Mhz
	OCR0A = 0x01; // a 1MHz signal
	TIMSK0 |= (1<<OCIE0A);
}

volatile uint16_t clk1, clk2;

// what to do on the TIMER0 compares
SIGNAL(SIG_OUTPUT_COMPARE0A) {
  clk1++;
  clk2++; // meh
}


// the util/delay.h seems to be massively
// wrong if I create a timer
int delay_us(uint16_t val) {
	clk1 = 0;
	while ( clk1 < val ){
	}
	return 0;
}

// creates a 38.5KHz signal for
// a specified duration val (us)
int gensig (uint16_t val) {
	clk2 = 0;
	while ( clk2 < val ) {		
		PORTB ^= 0x04;
		delay_us(13);
	}
	return 0;
}


// when the button is pressed perform the trigger signal
int snap(void) {
	int i = 0;
	
	// has to do this twice
	while ( i <= 1 ) {
		gensig(2250); // # of 2250 cycles in 2000us @ 1MHz
		PORTB &= ~0x04;
		delay_us(27600);
		gensig(650); // # 650 of cycles in 650us
		PORTB &= ~0x04;
		delay_us(1375);
		gensig(575); // # 575 of cycles in 575us
		PORTB &= ~0x04;
		delay_us(3350);
		gensig(650); // # 650 of cycles in 650us
		PORTB &= ~0x04;
		i++;
		delay_us(63000);  // 63ms
	}
	return 0;
}

int main (void){
	realtimeclock_setup();
	DDRB |= 0x04; // an LED
	DDRD &= 0xFB;
	PORTD |= 0x04;  //enable pullup
	sei();
	
	while (1) {
		if ( PIND & 0x04 ) 
			PORTB &= ~0x04;		
		else 
			snap();  // perform trigger signal
	}	
	return 0;
}
For reference this is an atmega168 (Arduino) and I have a PIC programmed with the hexfile at the aforementioned website which triggers the camera fine.

Posted: Mon Aug 17, 2009 12:33 pm
by leon_heller
Why not use the PIC, it's much cheaper and smaller.

Leon

Posted: Mon Aug 17, 2009 12:35 pm
by regomodo
leon_heller wrote:Why not use the PIC, it's much cheaper and smaller.

Leon
Because I want to incorporate this design into a much larger one. Also, if I was to board my design i'll have to put a PIC on it in place of just a wire to the LED.

Posted: Mon Aug 17, 2009 12:37 pm
by regomodo
I've just changed the while loop in main to this:
Code: Select all
	while (1) {
		if ( PIND & 0x04 ){ 
			PORTB ^= 0x04;
			delay_us(500000);
		}
		else {
			snap();  // perform trigger signal
		}	
	}
This creates a 0.2Hz signal instead of the 1Hz I was expecting. Therefore my timer seems to be out by a factor of 5.

What the realtimeclock_setup() function is supposed to do is prescale the 16MHz clock signal by 8. This is supposed to now create a 2MHz signal. Now I add a clock compare thingy of 2, i.e. on every other clock signal an interrupt is created. This interrupt increments a variable at a rate of 1MHz.

Now all of that is what I believe is supposed to be happening yet I can't see any evidence against this. Could anybody point anything stupid i've done as i'm confused.

[note] Before somebody points out using 500000 with an unit16_t that's changed at the moment to a 32.

Posted: Tue Aug 18, 2009 6:31 am
by regomodo
Anybody?

Posted: Tue Aug 18, 2009 9:53 am
by theatrus
Do you have an oscilloscope to confirm what your timing is?

Have you broken your code down to the smallest possible factor (such as checking the rate of output compares)?

Are all your routines taking as long as you think?

Are your fuses set correctly for your clock?

Posted: Tue Aug 18, 2009 11:21 am
by regomodo
theatrus wrote:Do you have an oscilloscope to confirm what your timing is?

Have you broken your code down to the smallest possible factor (such as checking the rate of output compares)?

Are all your routines taking as long as you think?

Are your fuses set correctly for your clock?

Unfortunately no about the oscilloscope.

I'm not sure what you mean by "checking the rate of output compares". I have stripped the code right down to try and create a 1HZ LED with the timers i've set up but it's x5 out.

I've seen fuses mentioned elsewhere but i'm not sure what they do or how to change them. When I divided the 16MHz by 1024 and then created a 100Hz interrupt I got an expected 1HZ LED.

Posted: Wed Aug 19, 2009 5:33 am
by n1ist
Sure you are not 8x slower? You may have the CKDIV8 fuse set; this divides the clock by 8...
/mike

Posted: Wed Aug 19, 2009 1:31 pm
by regomodo
n1ist wrote:Sure you are not 8x slower? You may have the CKDIV8 fuse set; this divides the clock by 8...
/mike
Good question. I do believe I stumbled upon this nugget of info at one point but totally forgotten. Is it a default setting on the Arduino?

Is it due to avrdude? I use a script to upload a hexfile and it looks like this:
Code: Select all
#!/bin/sh -e
HEX="$1"
PORT="$2"

avrdude -V -F -c stk500v1 -p m168 -b 19200 -P "$PORT" -U flash:w:"$HEX"

Posted: Mon Aug 24, 2009 12:17 am
by tz
You might need to set the GTCCR explicitly.

This is a basic timing reporter (write to stdout the width of pulses using IC1). On an 8Mhz 328 pro or lilypad it returns counts in microseconds.

You can also set OC1A or B to interrupt N microseconds later and toggle the LED.

EDIT: Oops, I forgot the code:
Code: Select all
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

#define TXBUFLEN (1024)
static unsigned char txbuf[TXBUFLEN];
static unsigned txhead;
static unsigned txtail;

ISR(USART_UDRE_vect)
{
    if (txhead == txtail)
        UCSR0B &= ~0x20;
    else {
        UDR0 = txbuf[txtail++];
        txtail &= TXBUFLEN-1;
    }
}

void txenqueue(unsigned char *dataOut, unsigned length)
{
    while (length--) {
        txbuf[txhead++] = *dataOut++;
        txhead &= TXBUFLEN-1;
    }
    UCSR0B |= 0x20;
}

unsigned char hobuf[20];
unsigned long ltens[12];
static void send(unsigned char polarity, unsigned long width)
{
    unsigned char *h = hobuf, i,j;

    *h++ = polarity ? '/' : '\\';
    i = 1;
    while( ltens[i] < width )
        i++;
    while( i-- ) {
        j = '0';
        while( width >= ltens[i] ) {
            width -= ltens[i];
            j++;
        }
        *h++ = j;
    }
    //    *h++ = '\r';
    if( polarity )
        *h++ = '\n';
    txenqueue(hobuf, h - hobuf);
}

volatile unsigned hitime;
unsigned char edgebuf[256];
volatile unsigned char edgeptrh;
volatile unsigned char edgeptrt;
ISR(TIMER1_CAPT_vect)
{
    TCCR1B ^= 0x40;
    unsigned *ep = (unsigned *) &edgebuf[edgeptrh];
    edgeptrh += 4;
    *ep++ = ICR1;
    *ep++ = hitime;
}

ISR(TIMER1_OVF_vect)
{
    hitime++;
}

int main(void)
{
    DDRB = 0x20;
    PORTB |= 0x20; // LED

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

    TIFR1 |= 0x21;              // reset interrupt triggers
    TIMSK1 |= 0x21;             // enable interrupt

    // Crystal based: xtal/16 or xtal/8 for clk / baud - 1
#define BAUD (57600)
    UBRR0 = 1000000 / BAUD - 1;

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

#if 0
    UDR0 = '[';
    while (!(UCSR0A & 0x40));
    UDR0 = ']';
    while (!(UCSR0A & 0x40));
#endif

    int i;
    unsigned char polarity;
    unsigned long lt = 1;

    for( i = 0 ; i < 12; i++ ) {
        ltens[i] = lt;
        // lt *= 10
        unsigned long lx = lt + lt;
        lx += lx;
        lt += lx;
        lt += lt;
    }

    polarity = 0;
    edgeptrh = edgeptrt = 0;
    txhead = txtail = 0;
    hitime = 0;

    sei();

    unsigned long diff, lastedge = 0, *lep;
    for (;;) {
        while( edgeptrh == edgeptrt )
            sleep_mode(); // edge will waken
        PORTB ^= 0x20;
        lep = (unsigned long *) &edgebuf[edgeptrt];
        edgeptrt += 4;
        diff = *lep - lastedge;
        lastedge = *lep;
        send( polarity, diff );
        polarity ^= 1;
    }
}


Posted: Fri Aug 28, 2009 2:54 am
by regomodo
tz wrote:You might need to set the GTCCR explicitly.

This is a basic timing reporter (write to stdout the width of pulses using IC1). On an 8Mhz 328 pro or lilypad it returns counts in microseconds.

You can also set OC1A or B to interrupt N microseconds later and toggle the LED.
Not sure what an GTCCR is so i'll have to check on that. Cheers.

Posted: Sat Aug 29, 2009 3:56 pm
by deadsy
General comments:

1) an interrupt at 1MHz on a system that runs at 8/16/20 (?) MHz. Doesn't leave many cpu cycles for other stuff. ie- You are getting interrupted every 8/16/20 instructions. How much of that is the interrupt overhead?

2) Your biggest problem is the 38.5 kHz signal. That's a 26 usec period. Can you get the PWM hardware to help you out with this. ie- generate it in hardware without software supervision?

3) The rest of the pulse timing is relatively coarse and could be handled by a state machine that pokes various values into the output compare register. You may need to use two timers. ie- one to generate the 38.5 kHz and the other to turn it on/off at the right time.

3) The clk1 variable is 16 bit, and this is an 8 bit processor. It is being written by an ISR. You are reading it with interrupts turned on. You will
(upon occasion) read strange values from it.

Jason H.

tvbgone

Posted: Sat Aug 29, 2009 4:44 pm
by tz
You might also want to check out how TVbGone did it since it also uses a very similar carrier wave:

http://www.ladyada.net/make/tvbgone/
http://www.tvbgone.com/

Source available. And circuit diagrams. Different AVR chip, but close enough.

You should set the OCs for the 38khz carrier. You can get IR 38khz receivers/detectors (at Radio Shack or other places) which will get the carrier going.