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