/* 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(); } } }