/* * This file contains the low-level interface to the hardware driving * the substation bus. It talks to a Motorola 68230. */ static station_addr[70] = { 0x0f, 0x17, 0x1b, 0x1d, 0x1e, 0x27, 0x2b, 0x2d, 0x2e, 0x33, 0x35, 0x36, 0x39, 0x3a, 0x3c, 0x47, 0x4b, 0x4d, 0x4e, 0x53, 0x55, 0x56, 0x59, 0x5a, 0x5c, 0x63, 0x65, 0x66, 0x69, 0x6a, 0x6c, 0x71, 0x72, 0x74, 0x78, 0x87, 0x8b, 0x8d, 0x8e, 0x93, 0x95, 0x96, 0x99, 0x9a, 0x9c, 0xa3, 0xa5, 0xa6, 0xa9, 0xaa, 0xac, 0xb1, 0xb2, 0xb4, 0xb8, 0xc3, 0xc5, 0xc6, 0xc9, 0xca, 0xcc, 0xd1, 0xd2, 0xd4, 0xd8, 0xe1, 0xe2, 0xe4, 0xe8, 0xf0 }; static station_h1[70] = { 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x04, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x04, 0x01, 0x01, 0x02, 0x01, 0x02, 0x04, 0x01, 0x02, 0x04, 0x08, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x04, 0x01, 0x01, 0x02, 0x01, 0x02, 0x04, 0x01, 0x02, 0x04, 0x08, 0x01, 0x01, 0x02, 0x01, 0x02, 0x04, 0x01, 0x02, 0x04, 0x08, 0x01, 0x02, 0x04, 0x08, 0x10 }; static station_h2[70] = { 0x02, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x04, 0x04, 0x02, 0x04, 0x04, 0x08, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x04, 0x04, 0x08, 0x08, 0x08, 0x02, 0x04, 0x04, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x04, 0x04, 0x08, 0x08, 0x08, 0x02, 0x04, 0x04, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x02, 0x04, 0x04, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20 }; static station_h3[70] = { 0x04, 0x04, 0x08, 0x08, 0x08, 0x04, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x04, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x04, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 }; static station_h4[70] = { 0x08, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }; static uchar curr_data[NSTATION][8]; /* current output bits (0..7), ctl */ /* * return true if a station is alive */ bool io_probe( /* FALSE if station is dead */ register int station) /* addr, 0..NSTATION-1 */ { register Pit *pit = (Pit *)PIT; register bool alive; register int spl; spl = spl7(); pit->pbd = 0xff; /* disable everything */ pit->padd = 0xff; /* data out for station addr */ pit->pad = station_addr[station]; /* set station address */ pit->pbd = 0xfe; /* enable station bus driver */ pit->pbd = 0xfc; /* select station */ pit->pbd = 0xfe; /* end of selection */ alive = !(pit->ps & 0x10); pit->pad = 0xff; /* deselect addr */ pit->pbd = 0xfc; /* deselect station */ pit->pbd = 0xfe; /* end of deselection */ pit->pbd = 0xff; /* deactivate bus */ splx(spl); return(alive); } /* * send a byte and control bits to a station */ bool io_setbyte( register int station, /* addr, 0..NSTATION-1 */ register int ctl, /* station port address 0..7 */ register int data) /* data byte to send */ { register Pit *pit = (Pit *)PIT; register bool alive; register int code; register int spl; spl = spl7(); curr_data[station][ctl] = data; code = station_addr[station] & ~station_h1[station] & (ctl & 1 ? 0xff : ~station_h2[station]) & (ctl & 2 ? 0xff : ~station_h3[station]) & (ctl & 4 ? 0xff : ~station_h4[station]); pit->pbd = 0xff; /* disable everything */ pit->padd = 0xff; /* data out for station addr */ pit->pad = station_addr[station]; /* set station address */ pit->pbd = 0xfe; /* enable station bus driver */ pit->pbd = 0xfc; /* select station */ pit->pbd = 0xfe; /* end of selection */ alive = !(pit->ps & 0x10); pit->pad = code; /* reselect with ctl bits */ pit->pbd = 0xfc; /* reselect station */ pit->pbd = 0xfe; /* end of reselection */ pit->pad = data; /* set data byte */ pit->pbd = 0xf6; /* write mode */ pit->pbd = 0xfe; /* end of write mode */ pit->pad = 0xff; /* deselect address */ pit->pbd = 0xfc; /* deselect station */ pit->pbd = 0xfe; /* end of deselection */ pit->pbd = 0xff; /* deactivate bus */ splx(spl); return(alive); } /* * Set or clear an output or control bit of a station. Bits are numbered 0..7 * for data bits, and 8..10 for control bits. Bit numbers 0x100..0x177 are * bits in register locations All bits are initialized with 1. */ bool io_setbit( register int station, /* addr, 0..NSTATION-1 */ register int ctl, /* station port address 0..7 */ register int bit, /* bit 0..10 */ register bool value) /* set or clear */ { register bool alive; register int spl; spl = spl7(); if (value) curr_data[station][ctl] |= 1 << bit; else curr_data[station][ctl] &= ~(1 << bit); alive = io_setbyte(station, ctl, curr_data[station][ctl]); splx(spl); return(alive); } /* * Return the last value of a bit set on a station. This doesn't really * reflect what the hardware is doing, but what the software think it does. */ uchar io_pollbyte( register int station, /* addr, 0..NSTATION-1 */ register int ctl) /* station port address 0..7 */ { return(curr_data[station][ctl]); } bool io_pollbit( register int station, /* addr, 0..NSTATION-1 */ register int ctl, /* station port address 0..7 */ register int bit) /* bit 0..10 */ { return(curr_data[station][ctl] & (1 << bit) ? TRUE : FALSE); } /* * return a byte or bit from a station. Bits are numbered 0..7. This * changes the control bits. */ int io_getbyte( register int station) /* addr, 0..NSTATION-1 */ { register Pit *pit = (Pit *)PIT; register int data; register int spl; spl = spl7(); pit->pbd = 0xff; /* disable everything */ pit->padd = 0xff; /* data out for station addr */ pit->pad = station_addr[station]; /* set station address */ pit->pbd = 0xfe; /* enable station bus driver */ pit->pbd = 0xfc; /* select station */ pit->pbd = 0xfe; /* end of selection */ pit->padd = 0x00; /* data in for data byte */ pit->pbd = 0xfa; /* enable drivers, read mode */ data = pit->pad; /* get data byte */ pit->pbd = 0xfe; /* deactivate bus */ pit->padd = 0xff; /* data out for deselection */ pit->pad = 0xff; /* deselect address */ pit->pbd = 0xfc; /* deselect station */ pit->pbd = 0xfe; /* end of deselection */ pit->pbd = 0xff; /* deactivate bus */ splx(spl); return(data); } bool io_getbit( register int station, /* addr, 0..NSTATION-1 */ register int bit) /* bit 0..10 */ { return(io_getbyte(station) & (1 << bit) ? TRUE : FALSE); } /*--------------------------------------------------------------------------*/ /* * Here is an example for a ui-level routine, the one that controls the * bedroom panel (see next posting for schematics). Note that German * abbreviations are used for weekdays: Mo, Di, Mi, Do, Fr, Sa, So. * Conveniently, 74247 BCD->LED decoders can print "c" (for centigrade). */ /* * functions for driving the time panel * * init_timepanel() * irq_timepanel() * timepanel_settime(t,c) * timepanel_setstring(s) */ #include "common.h" #define NMODES 3 /* # of modes button cycles through */ extern Time time; /* current time from RTC */ extern int alarm_suspend; /* nz: no alarm/blinds for a while */ extern int alarm_status; /* 0=before, 1=alarm, 2=late, 3=after*/ extern int alarm_snooze; /* snooze 10ms remaining if stat=1 */ static uchar mode; /* 0=date, 1=temp, 2=suspend */ static bool last_key; /* previous status of panel button */ static bool last_colon; /* previous status of colon LEDs */ static uchar last_second; /* previous status of second */ /* * initialize time/alpha display (station 2) */ init_timepanel() { io_setbyte(2, 0, 0x00); /* reset alpha */ io_setbyte(2, 4, 0x00); /* select alpha ctl */ io_setbyte(2, 1, 0x80); /* clear alpha display */ io_setbyte(2, 1, 0x19); /* 25% brightness, underlines flash */ io_setbyte(2, 5, 0x0c); /* all special lamps off */ io_setbyte(2, 2, 0xff); /* digits 1,2 off */ io_setbyte(2, 3, 0xff); /* digits 3,4 off */ mode = 255; last_key = FALSE; last_colon = FALSE; last_second = 255; } /* * function called at a frequency of HZ by the timer interrupt. Several * things happen once a second, use last_second for timing. The small * button on the time panel turns alarms off as a side effect. */ irq_timepanel() { bool key; bool redraw_mode; volatile register Rtc *rtc = (Rtc *)RTC; char buf[16]; register int i; redraw_mode = time.second != last_second; key = io_getbit(2, 0); /* keys */ if (key && !last_key || mode >= NMODES) if (alarm_status == 1 || alarm_status == 2) alarm_status = 3; else if (!counter_alarm_off()) { last_second = 255; redraw_mode = TRUE; if (mode == 2 && alarm_suspend <= 30*60*100-1 && alarm_suspend > 29*60*100) alarm_suspend = 60*60*100-1; else { if (++mode >= NMODES) mode = 0; if (mode == 2) alarm_suspend = 30*60*100-1; } } last_key = key; if (mode == 2 && !alarm_suspend) { redraw_mode = TRUE; mode = 0; } if (mode != 1 && !(~time.second & 3)) timepanel_settemp(io_get_temp()); else timepanel_settime(&time, rtc->tenms < 50); if (redraw_mode) if (alarm_status && alarm_status < 3 && (time.second & 1)) /* alarm */ if (alarm_status == 1 && alarm_snooze > 0) timepanel_setstring(" SNOOZE"); else if (alarm_status == 2) timepanel_setstring("ALARM !!"); else timepanel_setstring(" ALARM"); else switch(mode) { case 0: /* date */ i = weekday(&time); sprintf(buf, "%c%c %d.%d.", "MDMDFSS"[i], "OIIORAO"[i], time.day, time.month); timepanel_setstring(buf); break; case 1: /* temp */ i = io_get_temp(); if (i <= 50) { sprintf(buf, "TEMP %d", io_get_temp()); timepanel_setstring(buf); } else timepanel_setstring("NO TEMP"); break; case 2: /* suspend */ sprintf(buf, "QUIET %2d", (alarm_suspend + 60*100-1) / 60*100); timepanel_setstring(buf); break; } } /* * print time on the numeric part of the time panel. This is called from * the RTC interrupt routine irq_clock because the RTC 100Hz register is * needed to flash the colon */ timepanel_settime( register Time *time, /* time to print */ register bool colon) /* turn colon on too */ { register int m, h; if (colon == last_colon && time->second == last_second) return; last_colon = colon; last_second = time->second; h = (time->hour / 10 << 4) + time->hour % 10; m = (time->minute / 10 << 4) + time->minute % 10; if (h < 10) h |= 0xf0; io_setbyte(2, 5, 0x0c + colon); io_setbyte(2, 2, h); io_setbyte(2, 3, m); } /* * print temp on the numeric part of the time panel. This is called from * the RTC interrupt routine irq_clock. */ timepanel_settemp( register int temp) /* temp to print */ { register int t; if (last_second == 99) return; last_colon = FALSE; last_second = 99; t = (temp / 10 << 4) + temp % 10; if (t < 10) t |= 0xf0; io_setbyte(2, 5, temp < 0 ? 0x06 : 0x04); io_setbyte(2, 2, t); io_setbyte(2, 3, 0xfa); } /* * print an 8-character ascii string on the alpha section of the time panel */ timepanel_setstring( char *string) /* string to print */ { register char *p; register int i; for (p=string, i=0; *p && i < 8; i++, p++) { io_setbyte(2, 4, 0x0f - i); io_setbyte(2, 1, *p); } while (i < 8) { io_setbyte(2, 4, 0x0f - i++); io_setbyte(2, 1, 0x20); } }