/* ========================================================================== */
/*   ClockPIC.c                                                               */
/*   (c) 2007 St                                                              */
/*                                                                            */
/*   Program for nixie clock controlled by PIC16F627A or PIC16F628A           */
/*   Xtall frequency: 32.768 kHz (External clock for Timer1)                  */
/*   Compiler: HI-TEC PICC-Lite                                               */
/* ========================================================================== */
#include <pic.h>

#define word unsigned int
#define byte unsigned char

// Definition of PIN connection
#define POSITION0 RA7  // digit anode switches
#define POSITION1 RA6
#define POSITION2 RA0
#define POSITION3 RB5
#define POSITION4 RB4
#define POSITION5 RB2

#define DIGIT0 RB1     // outputs for nixie driver 74141
#define DIGIT1 RA2 
#define DIGIT2 RA3 
#define DIGIT3 RB0

#define BUZZER RB3     // alarm buzzer
#define KEYINP RA4     // button input

// Connection of function buttons to digit positions
#define KEYMENU  4 
#define KEYUP    3 
#define KEYENTER 2 

// Global variables used in main loop and in the timer interrupt
// counter for 2 miliseconds ticks
word milisec = 0;
// arrays for real time, alarm and auxiliary time during setting
byte time[6],alarm[6],auxtime[6];
// pressed button code, half second flag (for alarm and blink), blinking position 
byte button = 0, halfsec = 0, blink = 0xF;
// pointer which array to display
byte *p_digit = time;

// wait for button pressed or 20 second time-out
void waitbutton(void) {
  word i;
  byte n;
  
  for(n=0; n<200; n++) {
    // Wait for about 100 msec
    for(i=0; i<10000; i++) {} 
    if(button) break;
  }
}

// wait for all buttons released
void endbutton(void) {
  word i;

  while(button) {
    button = 0;
    for(i=0; i<10000; i++) {} 
  }
}

// function for setting array "setdata" with a new time
// seconds are not updated, just only hours and minutes
byte settime(byte *setdata) {
  byte posit = 2, updated = 0, next = 1;

  while(next) {
    blink = posit;   // updated position blinks
    waitbutton();
    // Test buttons and do 1 of 4 possibilities
    switch(button) {
      case KEYMENU : next = 0; break; // Stop updating time
      case KEYENTER: {  // Move to next digit
        posit++;
        if(posit == 6) posit = 2;
      } break;
      case KEYUP   : {  // Increment digit on this position
        setdata[posit]++;
        if(((posit == 2) || (posit == 4)) && (setdata[posit] == 10)) setdata[posit]=0;
        if((posit == 3) && (setdata[posit] == 6)) setdata[posit]=0;
        if((posit == 5) && (setdata[posit] == 3)) setdata[posit]=0;
        updated = 1;
      } break;
      default      : next = 0;  // Time-out expired
    }
    endbutton();
  }
  // Hours above 23 are not allowed
  if((setdata[5] == 2) && (setdata[4] > 3)) {
    setdata[5] = 0; setdata[4] = 0;
  }
  blink = 0xF;
  return(updated);
}
// ****************************
//    Main program function  //
// ****************************
void main(void) {

  // Default values for start
  time[4] = 7; time[3] = 3;
  alarm[4] = 7; alarm[3] = 4;
  alarm[1] = 0xF; alarm[0] = 0xF;

  // Turn comparators off
  CMCON = 0x07;
  // Set Port I/O
  TRISA = 0b00110000;
  TRISB = 0b10000000;

  /* Init TIMER1 for 2 milisec period - external 32 kHz clock*/
  T1CON = 0b00001011; // Timer control register
  TMR1L = 0;    // About 1 sec to start
  TMR1H = 0x80; // start Timer
  TMR1IE = 1; // Enable interrupt from Timer1
  PEIE = 1;

  GIE=1;	 /* enable interrupts */

  /***** Main loop of program *****/
  for(;;) {
    // Set Alarm Buzzer if time == alarm time
    if((time[5] == alarm[5]) && (time[4] == alarm[4]) && (time[3] == alarm[3]) && (time[2] == alarm[2]) && halfsec) BUZZER = 0; else BUZZER = 1;
    
    if(button == KEYMENU) {
    // Menu button enters time updating
      endbutton();
      p_digit = alarm;
      if(!settime(alarm)) {
        // Alarm was not updated, try Time
        GIE = 0;  // Stop interrupt during copy 
        // We must not destroy real Time, copy it to auxiliary array
        auxtime[5] = time[5];auxtime[4] = time[4];auxtime[3] = time[3];auxtime[2] = time[2];
        GIE = 1;
        p_digit = auxtime;
        if(settime(auxtime)) {
          // Time was updated - copy it back
          GIE = 0;
          time[5] = auxtime[5];time[4] = auxtime[4];time[3] = auxtime[3];
          time[2] = auxtime[2];time[1] = 0;time[0] = 0;
          GIE = 1;
        }
      }
      p_digit = time;
    } else button = 0;
      
  } /* loop forever */
  /* please make sure that you never leave main */
}

/*
** ===================================================================
**     Interrupt handler
** ===================================================================
*/
static void interrupt
isr(void)
{
  static const byte tabledigit[] = {3,2,7,4,1,9,8,0,5,6,0xF,0xF,0xF,0xF,0xF,0xF};
  static byte position = 0;
  static byte auxdigit;

  TMR1L = 0xC1;   // New value for next
  TMR1H = 0xFF;   // 2 milisecond period
  // Increment Time
  milisec++;
  if(milisec == 256) halfsec = 1;
  if(milisec == 512) {
    halfsec = 0;                 // half second flag
    milisec = 0;                 // miliseconds
    time[0]++;
    if(time[0] == 10) {          // seconds
      time[0] = 0;
      time[1]++;
      if(time[1] == 6) {
        time[1] = 0;
        time[2]++;
        if(time[2] == 10) {      // minutes
          time[2] = 0;
          time[3]++;
          if(time[3] == 6) {
            time[3] = 0;
            time[4]++;
            if(time[4] == 10) {  // hours
              time[4] = 0;
              time[5]++;
            }
            if((time[4] == 4) && (time[5] == 2)) {
              time[4] = 0;
              time[5] = 0;
            }
          }
        }
      }
    }
  }
  // Clear display (all digits off)
  POSITION0 = 0; POSITION1 = 0; POSITION2 = 0; POSITION3 = 0; POSITION4 = 0; POSITION5 = 0;
      
  // New digit to display
  // Test for blinking digit (and set it according "blink" variable)
  if((blink == position) && halfsec) {
    DIGIT0 = 1; DIGIT1 = 1; DIGIT2 = 1; DIGIT3 = 1;
  } else {
    // Set new digit to display
    auxdigit = tabledigit[*(p_digit + position)];
    DIGIT0 = 0; DIGIT1 = 0; DIGIT2 = 0; DIGIT3 = 0;
    if(auxdigit & 0x1) DIGIT0 = 1;
    if(auxdigit & 0x2) DIGIT1 = 1;
    if(auxdigit & 0x4) DIGIT2 = 1;
    if(auxdigit & 0x8) DIGIT3 = 1;
  }

  // Switch ON new digit
  switch(position) {
    case 0: POSITION0 = 1; break;
    case 1: POSITION1 = 1; break;
    case 2: POSITION2 = 1; break;
    case 3: POSITION3 = 1; break;
    case 4: POSITION4 = 1; break;
    case 5: POSITION5 = 1; break;
  }

  // Test if pressed button with this position  
  if((!button) && (KEYINP == 1)) button = position;
  
  // Update position for next digit
  position++;
  if(position == 6) position = 0;
  TMR1IF = 0;			// Clear the interrupt flag 
}

/* END */
