/* PICSEL Version 1.5 */ /* (c) C.Schirp released for non-commercial use */ /* IR-routines copyright by Jos van Eijndhoven */ /* debouncing copyright by Peter Dannegger */ /* */ /* V1.4->V1.5: Bei Tasten+/- auch sperrzeit */ /* Sperr- und Nachlaufzeit getrennt */ /* */ /* V1.3->V1.4: Tape-Relais ausblenden, wenn */ /* Tape-Gruppe inaktiv */ /* */ /* V1.2->V1.3: FB-Tasten 5&6 umrangiert,Nachlauf */ /* verkürzt */ #include <E:\projekte\picsel\cc5\16F628.h> #include <E:\projekte\picsel\cc5\int16CXX.h> #pragma config |= 0x3F18 /* Defines */ #define KEY_INPUT PORTA #define RCLK RB5 #define SCLK RB2 #define SER RB1 #define M_DIR RB4 #define M_PWM RB3 #define INPUT 1 #define OUTPUT 0 #define NL_TIME 10; #define SP_TIME 20; unsigned char key_state; //debounced and inverted key state: unsigned char key_press; unsigned char key_collected; uns16 ircode; unsigned char irled; // 1 if light is on unsigned char old_ircode; unsigned char nachlauf; unsigned char sperrzeit; unsigned char tmr0cpy; //bit Monitor; // Shadow fuer PB6 bit irledn @ PORTB.0; /*------------------------------------------------------------------*/ /* */ /* */ /* */ /* */ /* */ /*------------------------------------------------------------------*/ #pragma origin 4 interrupt int_server( void) { int_save_registers // W, STATUS (and PCLATH) if ( INTF) { /* RB0/INT interrupt */ irled.0 = !irledn; INTF = 0; if (irled) // request interrupt on next edge (up or down) INTEDG = 1; // if 1, then interrupt on rising edge else INTEDG = 0; // is part of OPTION_REG, is 1 on reset /* finish and restart TMR0 */ tmr0cpy = TMR0; TMR0 = 0; T0IF = 1; // end polling wait-loop, similar to TRM0 wraparound } /* NOTE: GIE is AUTOMATICALLY cleared on interrupt entry and set to 1 on exit (by RETFIE). Setting GIE to 1 inside the interrupt service routine will cause nested interrupts if an interrupt is pending. Too deep nesting may crash the program ! */ int_restore_registers // W, STATUS (and PCLATH) } /*------------------------------------------------------------------*/ /* II N N II TTTTT */ /* II NN N II T */ /* II N N N II T */ /* II N NN II T */ /* II N N II T */ /*------------------------------------------------------------------*/ void init () { /* PORT A Config */ CMCON = 0x07; // kein Komparator TRISA = 0xFF; // alle input PORTA = 0; // alle low /* PORT B Config */ TRISB = 0b1000.0001; // PB0,7 input, rest output T1CON = 0x00; // Timer disabled RB6 = 0; // Monitor-LED high=aus RCLK = 0; // seriell reset state SCLK = 0; SER = 0; M_DIR = 0; // Motorsignale M_PWM = 0; /*sonstiges*/ OPTION_REG = 0x04;/* pull-up portB, falling edge int, no WDT prescaler, prescale=32 */ /* Variablen initialisieren */ key_state = 0xFF; //Monitor = 0; irled = 0; old_ircode= 0; nachlauf = 0; INTCON = 0; // reset and disable all interrupts by default INTE = 1; GIE = 1; // Enable Interrupts } /*------------------------------------------------------------------*/ /* EEEE EEEE PPPP RRRR OOO M M */ /* E E P P R R O O MM MM */ /* EEE EEE PPPP RRRR O O M M M M */ /* E E P R R O O M M M */ /* EEEE EEEE P R R OOO M M */ /*------------------------------------------------------------------*/ void write_eeprom( unsigned char adr, unsigned char data) { bit prev_gie = GIE; GIE = 0; // disable interrupts EEIF = 0; // clear eprom interrupt flag while (WR) ; // wait if previous write is still busy EEADR = adr; // valid range is from 00h to 7Fh (128 bytes) EEADR &= 0x7f; // prevent bugs to overwrite program memory EEDATA = data; WREN = 1; EECON2 = 0x55; EECON2 = 0xAA; WR = 1; // start the actual write WREN = 0; // protect against unintended writes GIE = prev_gie; // restore interrupts } unsigned char read_eeprom( unsigned char adr) { EEADR = adr; // valid range is from 00h to 7Fh (128 bytes) EEADR &= 0x7f; // prevent bugs to exceed program memory RD = 1; return EEDATA; } /*------------------------------------------------------------------*/ /* K K EEEE Y Y SSS */ /* K K E Y Y S */ /* KK EE Y SSS */ /* K K E Y S */ /* K K EEEE Y SSS */ /*------------------------------------------------------------------*/ void debounce(void) { static unsigned char ct0, ct1; unsigned char i; i = key_state ^ ~KEY_INPUT; // key changed ? ct0 = ~( ct0 & i ); // reset or count ct0 ct1 = ct0 ^ (ct1 & i); // reset or count ct1 i &= ct0 & ct1; // count until roll over key_state ^= i; // then toggle debounced state key_press |= key_state & i; // 0->1: key pressing detect } void do_keys(void) { switch (key_press) { case 0x01: RA0 = 0; TRISA0 = OUTPUT; TRISA |= 0x0E; // TRISA 1,2,3 setzen //TRISA1 = INPUT; //TRISA2 = INPUT; //TRISA3 = INPUT; break; case 0x02: RA1 = 0; TRISA1 = OUTPUT; TRISA |= 0x0D; // TRISA 0,2,3 setzen //TRISA0 = INPUT; //TRISA2 = INPUT; //TRISA3 = INPUT; break; case 0x04: RA2 = 0; TRISA2 = OUTPUT; TRISA |= 0x0B; // TRISA 0,1,3 setzen //TRISA0 = INPUT; //TRISA1 = INPUT; //TRISA3 = INPUT; break; case 0x08: RA3 = 0; TRISA3 = OUTPUT; TRISA |= 0x07; // TRISA 0,1,2 setzen //TRISA0 = INPUT; //TRISA1 = INPUT; //TRISA2 = INPUT; break; case 0x10: RA4 = 0; TRISA4 = OUTPUT; TRISA7 = INPUT; TRISA6 = INPUT; break; case 0x20: RA5 = 0; TRISA5 = INPUT; if (RB6) // Monitor { RB6 = 0; //Monitor = 0; } else { RB6 = 1; //Monitor = 1; } break; case 0x40: RA6 = 0; TRISA6 = OUTPUT; TRISA7 = INPUT; TRISA4 = INPUT; break; case 0x80: RA7 = 0; TRISA7 = OUTPUT; TRISA6 = INPUT; TRISA4 = INPUT; break; default: break; } } void collect (void) { key_collected = TRISA; if (!RB6) // Monitor key_collected &= 0xDF; } void restore_state(void) { unsigned char tester; key_collected = read_eeprom(0); tester = read_eeprom(1); key_press = read_eeprom(2); // 2 aus 3 if (key_collected==tester) { } else if (key_collected==key_press) { } else if (tester == key_press) { key_collected = tester; } key_press = 0; // Variable initialisieren // gueltige Bitkombinationen fuer TAPE testen tester = key_collected; tester &= 0b1101.0000; switch (tester) { case 0b0101.0000: case 0b1001.0000: case 0b1100.0000: break; default: key_collected |= 0b.1101.0000; break; } // gueltige Bitkombinationen fuer Phono/CD/Tuner/AUX testen tester = key_collected; tester &= 0b0000.1111; switch (tester) { case 0b0000.0111: case 0b0000.1011: case 0b0000.1101: case 0b0000.1110: break; default: key_collected |= 0b.0000.1111; break; } // Tape/Monitor Port und Shadow setzen if (key_collected & 0x20) { RB6 = 1; //Monitor = 1; } else { RB6 = 0; //Monitor = 0; } key_state = ~key_collected; // Tape/Monitor-Bit setzen (immer Eingang!) key_collected |= 0x20; TRISA = key_collected; } void set_relais(void) { unsigned char temp; unsigned char i; temp = ~key_collected; // kleiner Stromspartrick: // Wenn Tape/Monitor nicht aktiv ist, Tape-Relais-Bits ausblenden if (key_collected & 0x20) { temp &= 0b0010.1111; } // Bits per SW austoggeln for ( i = 0; i>= 1; } // alles wieder auf Idle zurueck SCLK = 0; SER = 0; // D-Latch clock betaetigen RCLK = 1; RCLK = 1; RCLK = 0; } /* void pause(unsigned char ms) // ms Milisekunden warten (fosc=4MHz) { while(ms) // Schleife verlassen wenn ms=0 ist { OPTION = 2; // Vorteiler auf 8 einstellen TMR0 = 131; // [256-131=125]: 125 * 8 = 1000 (=1ms) while (TMR0); // abwarten einer Milisekunde ms--; // "ms" mit jeder Milisekunde ernidrigen } OPTION = 4; // Zustand für TMRWAIT herstellen } */ /*------------------------------------------------------------------*/ /* RRR CCC 55555 */ /* R R C C 5 */ /* RRR C 5555 */ /* R R C C 5 */ /* R R CCC 5555 */ /*------------------------------------------------------------------*/ // 4MHz crystal, 1MHz cycle time, TMR0 prescale=32: timer units of 32 usec. // TRM0 expires at 8 msec. extern uns8 tmr0wait( void) { tmr0cpy = 0xff; // potential longest waiting time, reduced by irled while (!T0IF) // wait for TMR0 wrap-around or IRled ; T0IF = 0; return tmr0cpy; } void ir_bogus( void) { // just eat irled input, as long as we have fast pulses uns8 t; t = 0; while (t < 200) t = tmr0wait(); ircode = 0xffffL; // bogus code; } void ir_philips_rc5(void) // now assuming no 'extended' RC5, so S2==1 { // 1 is sequence dark,light: 2x 900us == 2x28 // 0 is sequence light,dark: 2x 900us == 2x28 // frame is: one,one,toggle,11xdata unsigned char t; unsigned char i; t = 0; i = 0; ircode = 0L; // starting now in the 2nd (light) half S2 while (t < 80) { // last half of current bit still to be measured, its timer is running ircode <<=1; // insert current value if (irled) // irled input is active low ircode.0 = 1; i++; t = tmr0wait(); if (t < 38) // new codebit is same as current, edge at bit boundary { t=tmr0wait(); // skip first half of new bit } } ircode.high8 &= 0x07; // clear start bits S2 and T if (i < 13) { ircode = 0xffffL; // bogus } } void get_ir( void) { unsigned char ton; unsigned char toff; ton = tmr0wait(); // measure first light toff = tmr0wait(); // following first dark if (ton > 20 && ton < 36 && // nominal 900/32 = 28 toff > 20 && toff < 36) // nominal 900/32 = 28 { ir_philips_rc5(); } else { ir_bogus(); } } void do_remote(void) { unsigned char h; // systemadresse testen h = ircode.high8 & 0b.0000.0111; if (h != 4) { return; } h = ircode.low8 & 0b.1100.0000; if (h != 0) { return; } // okay, kommando gilt uns: Commando extrahieren h= ircode.low8 & 0b.0011.1111; // Quellenumschaltung nur bei neuem IR-Code if (h != old_ircode) { // Quellenwahltasten = 1..8 if ((0<h) && (h<9)) { // Tasten 5 und 6 tauschen if (5==h) { h=6; } else if (6==h) { h=5; } h-=1; // Taste 1 entspricht Bit 0 key_press = 1; key_press <<= h; h+=1; // code wiederherstellen // Tasten 5 und 6 tauschen if (5==h) { h=6; } else if (6==h) { h=5; } } if (h == 16) // Volume + { M_DIR = 1; M_PWM = 0; nachlauf = NL_TIME; sperrzeit = SP_TIME; } else if (h == 17) // Volume - { M_DIR = 0; M_PWM = 1; nachlauf = NL_TIME; sperrzeit = SP_TIME; } } old_ircode = h; // bei Taste if (h == 5) sperrzeit = SP_TIME; } /*------------------------------------------------------------------*/ /* M M A II N N */ /* MM MM A A II NN N */ /* M M M M A A II N N N */ /* M M M AAAAA II N NN */ /* M M A A II N N */ /*------------------------------------------------------------------*/ void main (void) { unsigned char tmp; init(); restore_state(); collect(); set_relais(); while (1) { // Schalter lesen und entprellen debounce(); // wenn Taste, dann EEPROM und Relais schreiben if (key_press) { // Auswerten, LEDs setzen do_keys(); collect(); write_eeprom(0,key_collected); write_eeprom(1,key_collected); write_eeprom(2,key_collected); set_relais(); } // zustand zuruecksetzen key_press = 0; tmp = tmr0wait(); // wrap-around or interrupt, wrap-around takes 8ms //pause(10); if (irled) //came out of wait by interrupt: light on, start of new code { get_ir(); do_remote(); } // Motornachlauf? if (!nachlauf) { M_DIR = 0; // Motor stoppen M_PWM = 0; } else { nachlauf--; } // Sperrzeit fuer Taste 5 (tape/Monitor) if (!sperrzeit) { old_ircode = 0; // Fuer Tape/Monitor } else { sperrzeit--; } } /* while (1) */ }