/*
 * $Id: main.c,v 1.5 2007/08/02 01:27:33 bsd Exp $
 */

/*
 * Simple threads example demonstrating how to create threads, start
 * threads, and stop them.
 */

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

#include <inttypes.h>
#include <stdio.h>

#include "threads.h"


#define DEF_UART 0          /* use UART0 by default for output */


/*
 * THREAD delcarations
 */

#define DEFAULT_STACK 80

THREADS(2, DEFAULT_STACK);
THREAD_DEF(thread1, DEFAULT_STACK);
THREAD_DEF(thread2, DEFAULT_STACK);


/* 
 * CPU clock frequency information and baud rate register calculations
 */
#define BAUD     9600       /* use 9600 baud */
#define BAUD_RR  ((CPU_FREQ/(16L*9600L) - 1))  /* buad rate register value */


/*
 * function prototypes
 */
int  def_putc      (char ch, FILE * f);

void thread1_entry (void);

void thread2_entry (void);



#define CPU_FREQ 16000000L  /* set to clock frequency in Hz */

#if CPU_FREQ == 16000000L
#define OCR_1MS 125
#elif CPU_FREQ == 14745600L
#define OCR_1MS 115
#endif


volatile uint16_t ms_count;

/*
 * ms_sleep() - delay for specified number of milliseconds
 */
void ms_sleep(uint16_t ms)
{
  TCNT0  = 0;
  ms_count = 0;
  while (ms_count != ms)
    ;
}


/* 
 * millisecond counter interrupt vector 
 */
SIGNAL(SIG_OUTPUT_COMPARE0)
{
  ms_count++;
  thread_tick();
}


/*
 * Initialize timer0 to use the main crystal clock and the output
 * compare interrupt feature to generate an interrupt approximately
 * once per millisecond to use as a general purpose time base.
 */
void init_timer0(void)
{
  TCCR0 = 0;
  TIFR  |= _BV(OCIE0)|_BV(TOIE0);
  TIMSK |= _BV(TOIE0)|_BV(OCIE0);         /* enable output compare interrupt */
  TCCR0  = _BV(WGM01)|_BV(CS02)|_BV(CS00); /* CTC, prescale = 128 */
  TCNT0  = 0;
  OCR0   = OCR_1MS;                     /* match in aprox 1 ms */
}




/*
 * main entry point
 */
int main(void)
{
  init_timer0();

  /* enable UART0 */
  UBRR0H = (BAUD_RR >> 8) & 0xff;
  UBRR0L = BAUD_RR & 0xff;
  UCSR0B = _BV(TXEN); /* enable transmitter only */

  /* initialize threads - from this point forward, we run as a thread */
  main_thread = threads_init(N_THREADS, DEFAULT_STACK, DEFAULT_STACK, 10, 1);

  /* interrupts are ok now */
  sei();

  /* initialize stdio */
  fdevopen(def_putc, NULL);

  printf("creating thread 1 ...\n");
  thread1_thread = thread_create(thread1_entry, thread1_stack, 
                                 sizeof(thread1_stack), 10, NULL);

  printf("creating thread 2 ...\n");
  thread2_thread = thread_create(thread2_entry, thread2_stack, 
                                 sizeof(thread2_stack), 10, NULL);

  /*
   * thread1 and thread2 now run independently of the main thread and
   * share the CPU based on their priority 
   */

  while (1) {
    printf("main\n");         /* print our name */
    thread_stop(main_thread); /* stop ourselves and switch to another
                                 thread; thread1 & thread2 wake us up
                                 periodically */
  }

}



/*
 * thread1 entry point - simply print our name, wake up the main
 * thread, and then go to sleep for 1 second, repeat forever 
 */
void thread1_entry(void)
{
  while (1) {
    printf("thread1\n");        /* print our name */
    thread_start(main_thread);  /* wake up the main thread */
    thread_sleep(1024);         /* sleep for 1 second */
  }
}



/*
 * thread2 entry point - simply print our name, wake up the main
 * thread, and then go to sleep for 1 second, repeat forever.  Offset
 * our start time by .5 seconds so that our output is interleaved with
 * that of thread1's.
 */
void thread2_entry(void)
{
  thread_sleep(512);  /* sleep for 0.5 seconds */

  while (1) {
    printf("thread2\n");        /* print our name */
    thread_start(main_thread);  /* wake up the main thread */
    thread_sleep(1024);         /* sleep for 1 second */
  }
}



/*
 * UART character output routine (polled)
 */
void uart_putc(uint8_t uart, char c)
{
  if (uart == 0) {
    while ((UCSR0A & _BV(UDRE)) == 0)
      ;
    UDR0 = c;
  }
  else if (uart == 1) {
    while ((UCSR1A & _BV(UDRE)) == 0)
      ;
    UDR1 = c;
  }
}


/*
 * character output routine for default UART called by stdio routines
 */
int def_putc(char ch, FILE * f)
{
  uart_putc(DEF_UART, ch);
  return 0;
}

