summaryrefslogtreecommitdiff
path: root/firmware/main.c
diff options
context:
space:
mode:
authorDaniel Friesel <derf@finalrewind.org>2013-05-14 09:17:59 +0200
committerDaniel Friesel <derf@finalrewind.org>2013-05-14 09:17:59 +0200
commitbead72f65b5f0fcdb5839fd6bfe24c60adb0f480 (patch)
tree991f31be81e4ca8082091cfff8ee58a8b5ade193 /firmware/main.c
initial commit
Diffstat (limited to 'firmware/main.c')
-rw-r--r--firmware/main.c198
1 files changed, 198 insertions, 0 deletions
diff --git a/firmware/main.c b/firmware/main.c
new file mode 100644
index 0000000..1a9da09
--- /dev/null
+++ b/firmware/main.c
@@ -0,0 +1,198 @@
+/* Name: main.c
+ * Project: PowerSwitch based on AVR USB driver
+ * Author: Christian Starkjohann
+ * Creation Date: 2005-01-16
+ * Tabsize: 4
+ * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+#include <avr/wdt.h>
+
+#include "usbdrv.h"
+#include "oddebug.h"
+#include <util/delay.h>
+
+/*
+This module implements an 8 bit parallel output controlled via USB. It is
+intended to switch the power supply to computers and/or other electronic
+devices.
+
+Application examples:
+- Rebooting computers located at the provider's site
+- Remotely switch on/off of rarely used computers
+- Rebooting other electronic equipment which is left unattended
+- Control room heating from remote
+*/
+
+#ifndef TEST_DRIVER_SIZE /* define this to check out size of pure driver */
+
+static uchar actionTimers[8];
+static uchar permstatus = 0;
+
+/* This is the AT90S2313 version of the routine. Change for others. */
+static void outputByte(uchar b)
+{
+ DDRB = ~b;
+ PORTB = b;
+}
+
+static uchar computeTemporaryChanges(void)
+{
+uchar i, status = 0, mask = 1;
+
+ for(i=0;i<8;i++){
+ if(actionTimers[i])
+ status |= mask;
+ mask <<= 1;
+ }
+ return status;
+}
+
+static void computeOutputStatus(void)
+{
+uchar status = permstatus ^ computeTemporaryChanges();
+
+ outputByte(status);
+}
+
+/* We poll for the timer interrupt instead of declaring an interrupt handler
+ * with global interrupts enabled. This saves recursion depth. Our timer does
+ * not need high precision and does not run at a high rate anyway.
+ */
+static void timerInterrupt(void)
+{
+static uchar prescaler;
+uchar i;
+
+ if(!prescaler--){
+ prescaler = 8; /* rate = 12M / 1024 * 256 * 9 */
+ for(i=0;i<8;i++){
+ if(actionTimers[i])
+ actionTimers[i]--;
+ }
+ computeOutputStatus();
+ }
+}
+
+USB_PUBLIC uchar usbFunctionSetup(uchar data[8])
+{
+usbRequest_t *rq = (void *)data;
+uchar status = permstatus;
+static uchar replyBuf[2];
+
+ usbMsgPtr = replyBuf;
+ if(rq->bRequest == 0){ /* ECHO */
+ replyBuf[0] = rq->wValue.bytes[0];
+ replyBuf[1] = rq->wValue.bytes[1];
+ return 2;
+ }
+ if(rq->bRequest == 1){ /* GET_STATUS -> result = 2 bytes */
+ replyBuf[0] = status;
+ replyBuf[1] = computeTemporaryChanges();
+ return 2;
+ }
+ if(rq->bRequest == 2 || rq->bRequest == 3){ /* SWITCH_ON or SWITCH_OFF, index = bit number */
+ uchar bit = rq->wIndex.bytes[0] & 7;
+ uchar mask = 1 << bit;
+ uchar needChange, isOn = status & mask;
+ if(rq->bRequest == 2){ /* SWITCH_ON */
+ status |= mask;
+ needChange = !isOn;
+ }else{ /* SWITCH_OFF */
+ status &= ~mask;
+ needChange = isOn;
+ }
+ if(rq->wValue.bytes[0] == 0){ /* duration == 0 -> permanent switch */
+ actionTimers[bit] = 0;
+ permstatus = status;
+ }else if(needChange){ /* temporary switch: value = duration in 200ms units */
+ actionTimers[bit] = rq->wValue.bytes[0];
+ }
+ }
+ computeOutputStatus();
+ return 0;
+}
+
+/* allow some inter-device compatibility */
+#if !defined TCCR0 && defined TCCR0B
+#define TCCR0 TCCR0B
+#endif
+#if !defined TIFR && defined TIFR0
+#define TIFR TIFR0
+#endif
+
+int main(void)
+{
+uchar i;
+
+ wdt_enable(WDTO_1S);
+ odDebugInit();
+ DDRB = 0xff;
+ DDRD = ~USBMASK; /* all outputs except PD2 = INT0 */
+ PORTD = 0;
+ PORTB = 0; /* no pullups on USB pins */
+/* We fake an USB disconnect by pulling D+ and D- to 0 during reset. This is
+ * necessary if we had a watchdog reset or brownout reset to notify the host
+ * that it should re-enumerate the device. Otherwise the host's and device's
+ * concept of the device-ID would be out of sync.
+ */
+ computeOutputStatus(); /* set output status before we do the delay */
+ usbDeviceDisconnect(); /* enforce re-enumeration, do this while interrupts are disabled! */
+ i = 0;
+ while(--i){ /* fake USB disconnect for > 500 ms */
+ wdt_reset();
+ _delay_ms(2);
+ }
+ usbDeviceConnect();
+ TCCR0 = 5; /* set prescaler to 1/1024 */
+ usbInit();
+ sei();
+ for(;;){ /* main event loop */
+ wdt_reset();
+ usbPoll();
+ if(TIFR & (1 << TOV0)){
+ TIFR |= 1 << TOV0; /* clear pending flag */
+ timerInterrupt();
+ }
+ }
+ return 0;
+}
+
+#else /* TEST_DRIVER_SIZE */
+
+/* This is the minimum do-nothing function to determine driver size. The
+ * resulting binary will consist of the C startup code, space for interrupt
+ * vectors and our minimal initialization. The C startup code and vectors
+ * (together ca. 70 bytes of flash) can not directly be considered a part
+ * of this driver. The driver is therefore ca. 70 bytes smaller than the
+ * resulting binary due to this overhead. The driver also contains strings
+ * of arbitrary length. You can save another ca. 50 bytes if you don't
+ * include a textual product and vendor description.
+ */
+uchar usbFunctionSetup(uchar data[8])
+{
+ return 0;
+}
+
+int main(void)
+{
+#ifdef PORTD
+ PORTD = 0;
+ DDRD = ~USBMASK; /* all outputs except PD2 = INT0 */
+#endif
+ PORTB = 0; /* no pullups on USB pins */
+ DDRB = 0xff; /* all outputs except USB data */
+ usbInit();
+ sei();
+ for(;;){ /* main event loop */
+ usbPoll();
+ }
+ return 0;
+}
+
+#endif /* TEST_DRIVER_SIZE */