Source file: /~heha/hs/dlo/DLO_DeluxeUSB.zip/DeluxeUSB/firmware/DeluxeUSB.c

/* Firmware for DeluxeUSB hardware */
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>  /* for sei() */
#include <util/delay.h>     /* for _delay_ms() */

#include <avr/pgmspace.h>   /* required by usbdrv.h */
#include <string.h>
#include "usbdrv.h"
#include "usbdrv.c"

/* port pin definitions */
#define PCK 0	// (5) parallel clock, L->H edge for '595 port expanders
#define SCK 4	// (3) serial clock, L->H edge for '595 port expanders
#define SDO 3	// (2) serial data output for '595 port expanders

PROGMEM char usbHidReportDescriptor[24] = {    /* USB report descriptor */
 0x06, 0x00, 0xff,	// USAGE_PAGE (Generic Desktop)
 0x09, 0x01,		// USAGE (Vendor Usage 1)
 0xa1, 0x01,		// COLLECTION (Application)
 0x15, 0x00,		//   LOGICAL_MINIMUM (0) = LED off
 0x25, 0x01,		//   LOGICAL_MAXIMUM (1) = LED on
 0x75, 0x01,		//   REPORT_SIZE (1) = 1 bit (on/off)
 0x85, 0x02,		//   REPORT_ID (2) 
 0x96, 0x20, 0x00,	//   REPORT_COUNT (32) = 32 LEDs (packed in 4 bytes)
			//   prepared for more than 127 LEDs (16 bit count)
 0x09, 0x00,		//   USAGE (Undefined)
 0xb2, 0x02, 0x01,	//   FEATURE (Data,Var,Abs,Buf)
 0xc0};			// END_COLLECTION
// Report ID 1 is reserved for setting / retrieving the LED count,
// thus changing this descriptor (is this valid?)
// Report ID 3 will set a single LED, report ID 4 will reset a single LED.
// This is merely for the Pick-By-Light project, not for DiscoLitez.

/* First byte contains report ID (2), the next four the 32 LED states */
static uchar reportBuffer[5];
register uchar offset asm("r3");// where to write incoming data at usbFunctionWrite()

// A status variable that survives RESET
register uchar LedOn asm("r2");	// TRUE when LEDs are not switched OFF
register uchar LedOO asm("r4");	// temporary LED on/off register
register  char AutoT asm("r5");	// auto-timer for automatic animation

// shifts out a byte MSB first
static void OutByte(uchar b) {
 uchar t=0;		// Default value with SCK=low
 asm volatile(		// This routine needs 5 clocks per bit and no SPI
"	or	r4,%0	\n"	// set some LedOO bits
"	bst	%0,7	\n"
"	bld	%1,%4	\n"
"	out	%2,%1	\n"	// SCK = low, SDO = data bit (same time)
"	bst	%0,6	\n"
"	sbi	%2,%3	\n"	// SCK = high, clocking (3 clocks later)
"	bld	%1,%4	\n"
"	out	%2,%1	\n"	// SCK = low (2 clocks later)
"	bst	%0,5	\n"
"	sbi	%2,%3	\n"
"	bld	%1,%4	\n"
"	out	%2,%1	\n"
"	bst	%0,4	\n"
"	sbi	%2,%3	\n"
"	bld	%1,%4	\n"
"	out	%2,%1	\n"
"	bst	%0,3	\n"
"	sbi	%2,%3	\n"
"	bld	%1,%4	\n"
"	out	%2,%1	\n"
"	bst	%0,2	\n"
"	sbi	%2,%3	\n"
"	bld	%1,%4	\n"
"	out	%2,%1	\n"
"	bst	%0,1	\n"
"	sbi	%2,%3	\n"
"	bld	%1,%4	\n"
"	out	%2,%1	\n"
"	bst	%0,0	\n"
"	sbi	%2,%3	\n"
"	bld	%1,%4	\n"
"	out	%2,%1	\n"
"	nop		\n"
"	sbi	%2,%3	\n"
:
: "r" (b), "r" (t), "I" (_SFR_IO_ADDR(PORTB)), "I" (SCK), "I" (SDO)
: "cc");
}

static void PulsePck(void) {
 PORTB|=1<<PCK;			// strobe shifted data to outputs
 asm volatile("	nop\n"::);
 PORTB=0;
 LedOn=LedOO;			// Now the state of LEDs is safe
}

static void OutLedState() {
 LedOO = 0;
 OutByte(reportBuffer[4]);	// Start with farmost data
 OutByte(reportBuffer[3]);
 OutByte(reportBuffer[2]);
 OutByte(reportBuffer[1]);
 PulsePck();
}

static void OutLedOff() {	// needed for USB standby mode
 uchar i;			// Let compiler decide to unroll this loop!
 LedOO = 0;
 for (i=0; i<4; i++) OutByte(0);
 PulsePck();
}

/* ------------------------------------------------------------------------- */

usbMsgLen_t usbFunctionSetup(uchar data[8]) {
 usbRequest_t *rq = (void*)data;

 usbMsgPtr = reportBuffer;
 if (rq->bRequest == USBRQ_HID_GET_REPORT) {  // wValue: ReportType:ReportID
  if (AutoT<0) OutLedState();
  AutoT=12;
  return sizeof(reportBuffer);
 }else if (rq->bRequest == USBRQ_HID_SET_REPORT) {
  offset=0;
  return USB_NO_MSG;	// let usbFunctionWrite accept data
 }
 return 0;
}

// Data arrives in 8-byte chunks maximum (which is not a problem for Deluxe32)
uchar usbFunctionWrite(uchar *data, uchar len) {
 uchar space=sizeof(reportBuffer)-offset;
 if (len>space) len=space;	// ignore additional data
 memcpy(reportBuffer+offset,data,len);
 offset+=len;
 space-=len;
 if (!space) {
  OutLedState();
  AutoT=12;			// no auto-animation for ca. 3 seconds
  return 1;
 }
 return 0;		// expect more data
}

// Einfache Animation: Durchlaufender Leuchtpunkt
static void AutoPlay(void) {
 --AutoT;
 if (AutoT >= 0) return;
 if (AutoT == -40) {	// no LED lights up for 8 states
  PORTB|=1<<SDO;	// switch on _one_ LED
  LedOO|=AutoT;		// Ensure LedOO != 0
  AutoT=0;
 }
 PORTB|=1<<SCK;		// Shift only one bit
 PulsePck();		// update LEDs
}

// For performance of ISR, the oscillator tuning is done in main loop,
// not inside the ISR.
static void tuneOscillator(void) {
#define SOLL 14	// usbSofDiff should be 16500/8%256 = 14.5
#define MABW 7	// Deviation of less than ±6 is OK and silently ignored
 schar t = usbSofDiff-SOLL;	// center result around signed zero
 if (t<-MABW) OSCCAL++;		// make faster
 if (t> MABW) OSCCAL--;		// make slower
}
/* ------------------------------------------------------------------------- */

int main(void) __attribute__((__noreturn__));
int main(void) {
 uchar SofCmp = 0;
 uchar mcusr = MCUSR;
 MCUSR = 0;
 ACSR |= 0x80;		// disable analog comparator and save 70 µA
 DDRB=1<<PCK|1<<SCK|1<<SDO;	// Generate 3 SPI outputs
 WDTCR = 0x18;		// watchdog survives RESET: disable it
 WDTCR = 0;		// (not using macros to save some bytes)
 if (mcusr & 1<<WDRF && USBIN&USBMASK) {	// no SE0 (wakeup) state?
  if (LedOn) OutLedOff();
  GIMSK = 1<<INT0;
  MCUCR = 0xF4;		// SLEEP_MODE_PWR_DOWN
  MCUCR = 0xF0;		// switch off BOD too do save more power
  sei();		// must be enabled for wakeup
  sleep_cpu();		// stop all and let INT0 do the next wakeup
  cli();
 }
 MCUCR = 0x60;		// SLEEP_MODE_STANDBY (no need for pull-ups)
 PRR    = 0x0B;		// save power, all peripherals OFF except TIMER0
 TCCR0B = 0x02;		// TIMER0 runs with 1/8 CPU clock
 OutLedOff();
 AutoT = 0;		// animate immediately (visible after 10 seconds)
 WDTCR = 0x08;		// enable watchdog timer with shortest timeout (15 ms)
 usbInit();
 sei();

 for(;;){                /* main event loop */
  uchar t,d;
  if (USBIN&USBMASK) sleep_cpu();	// sleep, except at SE0, until SOF
  t=usbSofCount;	// atomic access to volatile variable
  d=t-SofCmp;		// time difference [ms], mostly 1
  if (d || !(USBIN&USBMASK)) wdt_reset();
  usbPoll();
  if (d) {		// After 1 ms (or maybe more)
   SofCmp=t;
   tuneOscillator();
   if (!t) AutoPlay();	// Animate LEDs each 256 ms
  }
 }
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded