/* FILE / DATE:     dcf_sound_selection_0502.c / 2010-05-02 
   DESCRIPTION:     Sound selector logics for DCF simulator

   MICRO-PROCESSOR: PIC16F690
   COMPILER:        B Knudsen Cc5x C-compiler - not ANSI-C

   EXTERNAL HW:     Digital potentiometer X9C503
                    Rotary encoder
                    3 pins on PIC16F628 (Sound generator)

   DESIGNER:        Hans Sundgren
   CREDIT:          William Sandqvist for rotary encoder reading

   RAM usage:       10 bytes (7 local), 246 bytes free
   CODE WORDS:      238 code words (5 %)


CHIP CONNECTIONS ***********************************************
                      ________  _______           
                     |        \/       |         
                     |     16F690      |   
              +5V ---|Vdd  1    28  Vss|---GND 
        Encoder.B ->-|RA5  2    27  RA0|---
        Encoder.A ->-|RA4  3    26  RA1|---      
                  ---|RA3  4    25  RA2|---
                  ---|RC5  5    24  RC0|---
                  ---|RC4  6    23  RC1|--- 
 Amplifier on/off -<-|RC3  7    22  RC2|---        
  Digital pot.INC -<-|RC6  8    21  RB4|->- Sound mode.1 to PIC 
  Digital pot.U/D -<-|RC7  9    20  RB5|->- Sound mode.2 to PIC    
                  ---|RB7 10    19  RB6|->- Sound mode.3 to PIC
                     |_________________|         


INPUT: ROTATING DIAL type A/B/Ground 
 __________________________________________________________
| Type                | Function                 | Pin     |
|---------------------|------------------------------------|
| Rotary encoder A    | Sound selection          | RA4     |
| Rotary encoder B    | Sound selection          | RA5     |
|_____________________|__________________________|_________|


OUTPUT: AMPLIFIER ON/OFF
 __________________________________________________________
| Type                | Function                 | Pin     |
|---------------------|------------------------------------|
| Digital output      | Voltage to amplifier     | RC3     |
|_____________________|__________________________|_________|


OUTPUT: DIGITAL POTENTIOMETER (to amplifier)
 __________________________________________________________
| Type                | Function                 | Pin     |
|---------------------|------------------------------------|
| Dig. potentiometer  | Change signal (INC)      | RC6     |
| Dig. potentiometer  | Up/Down change (U/D)     | RC7     |
|_____________________|__________________________|_________|


OUTPUT: SOUND GENERATOR INTERFACE TO PIC16F628
 __________________________________________________________
| Type                | Function                 | Pin     |
|---------------------|------------------------------------|
| Sound mode bit 1    | Interface (RB0)          | RB4     |
| Sound mode bit 2    | Interface (RB1)          | RB5     |
| Sound mode bit 3    | Interface (RB2)          | RB6     |
|_____________________|__________________________|_________|

         
PROGRAM OVERVIEW  *******************************************

    Interrupt: Rotary encoder ------------------------------
   |  Read encoder                                          |
   |     If clockwise                                       |
   |        increase = 1                                    |
   |     If counter-clockwise                               |
   |        decrease = 1                                    |
    --------------------------------------------------------

    Main ---------------------------------------------------
   |  Initialization                                        |
   |  Loop                                                  |
   |     If increase                                        |
   |        if (fast)                                       |
   |           quick_up = 1                                 |
   |        else                                            |
   |           increase signal to digital potentiometer     |
   |     If decrease                                        |
   |        if (quick_up = 1)                               |
   |           if (rotation_timeout = 0)                    |
   |              increment sound mode                      |
   |        else                                            |
   |           decrease signal to digital potentiometer     |
   |     If (rotation timeout = 0)                          |
   |        if (time_since_rotation = 170 ms)               |
   |        rotation timeout = 1                            |
    --------------------------------------------------------
   */

/* _______________________________ INCLUDE & CONFIGURATION ________________ */

#include "16f690.h"
#include "int16CXX.H"                    // Required for interrupt
#pragma config |= 0x00D4                 // Use internal 4MHz oscillator


/* _________________________ GLOBAL VARIABLES _____________________________ */

bit quick_up;       // Indicator for fast clockwise rotation
bit rot_timeout;    // Timeout for "fast" has expired
bit increase;       // Clockwise rotation
bit decrease;       // Counter-clockwise rotation
char mode;          // Sound mode 1 -> 2 -> 3 -> 0 -> 1 -> ...
char transit;       // Store transitions for encoder


/* _______________________________ FUNCTIONS ______________________________ */

interrupt int_server(void);
void inc_pulse (void);
void delay250us ( char millisec);
void delay( char millisec);


/* _______________________________ I/O PIN DEFINITIONS ____________________ */

#pragma bit AMPL    @ PORTC.3    // Amplifier voltage supply
#pragma bit INC     @ PORTC.6    // Step change digital potentiometer
#pragma bit U_D     @ PORTC.7    // Up/Down digital potentiometer

#pragma bit ROT_A   @ PORTA.5    // Rotating encoder A
#pragma bit ROT_B   @ PORTA.4    // Rotating encoder B


/* _______________________________ INTERRUPT FUNCTION _____________________ */

#pragma origin 4                 // Special interrupt instruction CC5x

interrupt int_server(void)
{
   int_save_registers            // W, STATUS (and PCLATH)

   if( RBIF == 1 )               // Rotary encoder interrupt
   {
      delay(1);                  // Debounce rotary switches
      transit.0 = ROT_A;         // Read rotary encoder value
      transit.1 = ROT_B;
      if( transit == 0b00.01 )   // Compare value
      {
         decrease = 1;
      }
      if( transit == 0b01.00 )
      {
         increase = 1;
      }
      transit.2 = transit.0;     // Replace old with new
      transit.3 = transit.1;
      RBIF = 0;                  // Reset interrupt flag
   }
   int_restore_registers         // W, STATUS (and PCLATH)
}


/* _______________________________ MAIN FUNCTION start _____________________ */

void main(void)
{
   ANSEL=0;                      // Disable analog signals on port C
   ANSELH=0;

   TRISA = 0b0011.0000;          // A.4 and A.5 inputs encoder
   TRISB = 0b0000.0000;          // B.4, B.5 and B.6 outputs
   TRISC=  0b0000.0000;          // C3, C.6 and C.7 outputs

   OPTION.7 = 0;
   WPUA.4 = 1;                   // Weak pullup input Encoder.A
   WPUA.5 = 1;                   // Weak pullup input Encoder.B
   IOCA.4  = 1;                  // Enable interrupt on Encoder.A
   IOCA.5  = 1;                  // Enable interrupt on Encoder.B

/* Definition of interrupt 1 Hz
   00.xx.x.x.x.x --
   xx.11.x.x.x.x Prescale 1/8    // 1,000,000 / 8 = 125,000
   xx.xx.1.x.x.x TMR1-oscillator is on
   xx.xx.x.1.x.x - (clock input synchronization)
   xx.xx.x.x.0.x Use internal clock 4.000.000/4 = 1.000.000
   xx.xx.x.x.x.0 TIMER1 is ON
   
*/
   T1CON = 0b00.11.0.1.0.1 ;
   /* CCPR = CLOSC * Timer1Period = 32768*1 = 32768 */
   /* Desired interval check = 200 ms = 5 Hz --> 125.000/25.000 = 5 Hz*/
   /* CCPR = 32768 = 0x8000 CCPR1H = 0x80, CCPR1L = 0x00 */
   /* CCPR = 25000 = 0x61A8 CCPR1H = 0x61, CCPR1L = 0xA8 */

   transit = 0;                  // Set start values
   quick_up = 0;
   rot_timeout = 1;
   mode = 0;
   increase = 0; decrease = 0;

   PORTB= 0b0000.0000;           // Sound mode 0 = silent to start with
   AMPL = 0;                     // Amplifier off

   RABIE   = 1;                  /* local enable  */

   TMR1H = 0x00;                 // Reset timer
   TMR1L = 0x00;
   GIE     = 1;

   while (1)                     // Infinite loop
   {
      if (increase == 1)
      {
         U_D = 1;                // Upwards direction
         inc_pulse ();
         increase = 0;

         if (rot_timeout == 0)   // If short time since last
         {
            quick_up = 1;
         }
         rot_timeout =0;
         TMR1ON = 0;             // Stop timer
         TMR1H = 0x00;           // Reset timer
         TMR1L = 0x00;
         TMR1ON = 1;             // Start timer
      }

      if (decrease == 1)
      {
         if (quick_up == 1)
         {
            quick_up = 0;
            decrease = 0;

            if (rot_timeout ==0)
            {
               mode = mode +1;
               if (mode == 5)
               {
                  mode = 0;
               }
               if (mode ==0)
               {
                  AMPL = 0;
                  PORTB= 0b0000.0000;
               }
               if (mode ==1)
               {
                  AMPL = 1;
                  PORTB= 0b0001.0000;
               }
               if (mode ==2)
               {
                  PORTB= 0b0010.0000;
               }
               if (mode ==3)
               {
                  PORTB= 0b0011.0000;
               }
               if (mode ==4)
               {
                  PORTB= 0b0100.0000;
               }
            }
            else
            {
               U_D = 0;
               delay250us(2);
               inc_pulse ();
            }
         }
         else
         {
            U_D = 0;
            delay250us(2);
            inc_pulse ();
            decrease = 0;
            rot_timeout = 0;
            TMR1ON = 0;          // Stop timer
            TMR1H = 0x00;        // Reset timer
            TMR1L = 0x00;
            TMR1ON = 1;          // Start timer
         }
      }
      if (rot_timeout == 0)
      {
         if (TMR1H > 0x55)       // Check if 170 ms expired since last rotation
                                 // 0x55 = 01010101
                                 // 01010101.00000000 = 21760
                                 // 125,000 / 21,760 = 5.7 = 170 ms
         {
            rot_timeout = 1;
            TMR1ON = 0;          // Stop timer
            TMR1H = 0x00;        // Reset timer
            TMR1L = 0x00;
         }
      }
   }
}

void inc_pulse (void)
{
   INC = 1;
   delay250us(1);
   INC = 0;
}

/* _______________________________ DELAY functions _____________________ */

void delay ( char millisec)
/*
  Delays a multiple of 1 milliseconds at 4 MHz
  using the TMR0 timer 
*/


{
   OPTION = 2;  /* prescaler divide by 8        */
   do  {
       TMR0 = 0;
       while ( TMR0 < 125)   /* 125 * 8 = 1000  */
            ;
   } while ( -- millisec > 0);
}


void delay250us ( char millisec)
{
   char m;
   char k;
   for (m=0; m<millisec; m++)
   {
         for (k=0; k<250; k++)
         {
         nop();
         }
   }
}