USB-LED-Fader/firmware/main.c
2006-09-26 18:18:27 +00:00

279 lines
11 KiB
C

/**
* \file main.c
* \brief Firmware for the USB-LED-Fader.
* \author Ronald Schaten & Thomas Stegemann
* \version $Id: main.c,v 1.1 2006/09/26 18:18:27 rschaten Exp $
*
* License: See documentation.
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "usbdrv.h"
#include "oddebug.h"
#include "pwm_channels.h"
#include "usbledfader.h"
#include "channels.h"
/** Global variable, contains the state of all four LEDs. */
static fade_GlobalData fade_globalData;
/** Global variable, contains the rest-amount of data to send to the host. */
static uint8_t usbRead;
/**
* Handler for the timer-interrupt. Determines the state of the four LEDs and
* calls pwm_Channels_show() if something is to be changed. This function
* contains the logic by which the waveforms are assigned to the LEDs.
*/
static void timerInterrupt(void)
{
uint8_t i = 0, changed = 0;
for (i = 0; i < CHANNELS; i++) {
fade_LedState *pLed = &(fade_globalData.led[i]); /* fetch current LED */
pLed->waveNextUpdate--;
if (pLed->waveNextUpdate <= 0) { /* time to update */
fade_Waveform *pWave = &(pLed->wave[pLed->waveCurrentId]); /* fetch currently active wave */
pLed->waveCurrentPosition++; /* go to next position */
if (pLed->waveCurrentPosition > pWave->waveformLength) {
pLed->waveCurrentPosition = 1; /* restart wave */
if (pWave->waveformRepetition == 0) {
/* repeat this waveform forever */
} else {
/* next repetition */
pLed->waveCurrentRepetition++;
if (pLed->waveCurrentRepetition >= pWave->waveformRepetition) { /* enough of this wave */
pLed->waveCurrentRepetition = 0; /* reset repetition counter */
switch (pLed->waveCurrentId) { /* activate next wave */
case 0:
if (pLed->wave[1].waveformDuration > 0) { /* only activate if a wave is set */
pLed->waveCurrentId = 1;
}
break;
case 1:
if (pLed->wave[0].waveformDuration > 0) { /* only activate if a wave is set */
pLed->waveCurrentId = 0;
}
break;
case 2:
/* wave 2 is only to be repeated the given times,
* reset and continue with wave 0 */
pWave->waveformId = 0;
pWave->waveformLength = fade_calculateWaveform(pWave->waveformId, 0);
pWave->waveformRepetition = 1;
pWave->waveformDuration = 0;
pWave->waveformUpdateTime = 1;
pLed->waveCurrentId = 0;
break;
}
}
}
}
uint8_t newValue = fade_calculateWaveform(pLed->wave[pLed->waveCurrentId].waveformId, pLed->waveCurrentPosition); /* fetch new value */
if (newValue != pLed->waveCurrentValue) { /* only update if the value has changed */
pLed->waveCurrentValue = newValue;
changed = 1;
}
pLed->waveNextUpdate = pLed->wave[pLed->waveCurrentId].waveformUpdateTime; /* next update according to the wave's settings */
}
}
if (changed) { /* any value has changed, update all LEDs */
pwm_Channels channels;
for (i = 0; i < CHANNELS; i++) {
channels.channel[i] = fade_globalData.led[i].waveCurrentValue;
}
pwm_Channels_show(channels);
}
}
/**
* Start displaying a certain waveform on a single LED.
* \param ledId ID of the LED that is changed.
* \param waveId ID of the wave that to be set: 0 and 1 are the base waves, 2 is the override wave.
* \param waveformId ID of the Waveform that is to be assigned to the LED.
* \param periodDuration How long should this wave stay on display? Time in seconds/10.
* \param repetitionCount How many times should this wave be repeated while it is on display?
*/
void fade_startWaveform(uint8_t ledId, uint8_t waveId, uint8_t waveformId, uint8_t periodDuration, uint8_t repetitionCount) {
if ((ledId < CHANNELS) && (waveId < 3)) {
fade_LedState *pLed = &(fade_globalData.led[ledId]);
fade_Waveform *pWave = &(pLed->wave[waveId]);
pLed->waveCurrentId = waveId;
pLed->waveCurrentPosition = 0;
pLed->waveCurrentRepetition = 0;
pLed->waveNextUpdate = 0;
if (periodDuration > 0) {
pWave->waveformId = waveformId;
pWave->waveformLength = fade_calculateWaveform(waveformId, 0);
pWave->waveformRepetition = repetitionCount;
pWave->waveformDuration = periodDuration;
/* waveformUpdateTime in calls of timerInterrupt().
* periodDuration in seconds/10.
* 12000000 cycles per second
* 64 cycles per timer/counter (prescaler)
* 256 timer/counter per interrupt-call
* -> (12000000 / (256 * 64)) = 732 calls per second */
pWave->waveformUpdateTime = ((uint32_t)periodDuration * 12000000 / 256 / 64 / 10 / pWave->waveformLength );
} else {
/* periodDuration = 0, reset the wave */
pWave->waveformId = 0;
pWave->waveformLength = fade_calculateWaveform(pWave->waveformId, 0);
pWave->waveformRepetition = 1;
pWave->waveformDuration = 0;
pWave->waveformUpdateTime = 1;
}
}
}
/**
* Fills fade_globalData. The state of all LEDs is initialized to off. One
* signal is displayed on all LEDs to ensure they're working.
*/
void fade_globalData_init(void) {
int i = 0, j = 0;
for (i = 0; i < CHANNELS; i++) {
fade_globalData.led[i].waveCurrentId = 0;
fade_globalData.led[i].waveCurrentPosition = 0;
fade_globalData.led[i].waveCurrentRepetition = 0;
fade_globalData.led[i].waveNextUpdate = 0;
for (j = 0; j < 3; j++) {
fade_globalData.led[i].wave[j].waveformId = 0;
fade_globalData.led[i].wave[j].waveformLength =
fade_calculateWaveform(fade_globalData.led[i].wave[j].
waveformId, 0);
fade_globalData.led[i].wave[j].waveformRepetition = 1;
fade_globalData.led[i].wave[j].waveformDuration = 0;
fade_globalData.led[i].wave[j].waveformUpdateTime = 1;
}
}
/* show that we are ready */
for (i = 0; i < CHANNELS; i++) {
fade_startWaveform(i, 2, 36, 10, 1);
}
}
/**
* USB-Data-Handler (device -> host). Handles data that is to be sent to the
* host via USB-Interface. In our case the data contains the current settings
* for the LEDs. This function is called until the returned length is shorter
* than the buffer (typically 8 bytes).
* \param data Buffer for the data.
* \param len Length of the buffer.
* \return Length of the returned buffer.
*/
uchar usbFunctionRead(uchar *data, uchar len) {
uint8_t i = 0;
uint8_t *p_fade_globalData = (uint8_t*)&fade_globalData;
while ((i < len) && (usbRead < sizeof(fade_GlobalData))) {
data[i] = p_fade_globalData[usbRead];
usbRead++;
i++;
}
return i;
}
/**
* USB-Data-Handler (host -> device). Handles data that is received from the
* USB-Interface. In our case the data contains settings for the LEDs.
* \param data The received data, up to 8 bytes.
* \param len Length of the received data.
* \return 1 if we have received the entire payload successfully, 0 if we expect more data. We don't, so we always return 1.
*/
uchar usbFunctionWrite(uchar *data, uchar len) {
/* parameters:
* data[0]: command (0: echo, 1: read status, 2: set status, 3: clear)
* data[1]: ledId
* data[2]: waveId
* data[3]: waveformId
* data[4]: periodDuration
* data[5]: repetitionCount
*/
fade_startWaveform(data[1], data[2], data[3], data[4], data[5]);
return 1;
}
/**
* USB-Setup-Handler. Handles setup-calls that are received from the
* USB-Interface.
* \param data Eight bytes of data.
* \return The number of returned bytes (in replyBuffer[]).
*/
uchar usbFunctionSetup(uchar data[8]) {
int i;
static uchar replyBuffer[8];
uchar replyLength;
replyBuffer[0] = msgOK;
switch (data[1]) {
case CMD_ECHO: /* echo */
replyBuffer[0] = data[2];
replyBuffer[1] = data[3];
replyLength = 2;
break;
case CMD_GET: /* read status */
usbRead = 0;
replyLength = 0xff; /* special value, indicates that usbFunctionRead() has to be called */
break;
case CMD_SET: /* set status */
replyLength = 0xff; /* special value, indicates that usbFunctionWrite() has to be called */
break;
case CMD_CLEAR: /* clear one LED */
for (i = 0; i <= 2; i++) {
/* clear all three waves on this LED */
fade_startWaveform(data[2], i, 0, 0, 0);
}
replyLength = 1;
break;
case CMD_RESET: /* reset the device */
fade_globalData_init();
replyLength = 1;
break;
default: /* WTF? */
replyBuffer[0] = msgErr;
replyLength = 1;
break;
}
usbMsgPtr = replyBuffer;
return replyLength;
}
/**
* Main-function. Initializes the hardware and starts the main loop of the
* application.
* \return An integer. Whatever... :-)
*/
int main(void) {
uchar i, j;
odDebugInit();
DDRB = ~0; /* output SE0 for USB reset */
PORTB = 0x00; /* no pullups on USB pins */
DDRC = 0xff; /* all outputs */
PORTC = 0x00;
DDRD = 0x00; /* all inputs */
PORTD = 0x00;
j = 0;
while (--j) { /* USB Reset by device only required on Watchdog Reset */
i = 0;
while (--i); /* delay >10ms for USB reset */
}
DDRB = ~USBMASK; /* all outputs except USB data */
TCCR0 = 3; /* set prescaler to 1/64 */
usbInit();
sei();
pwm_Channels_init();
fade_globalData_init();
while (1) { /* main event loop */
usbPoll();
if (TIFR & (1 << TOV0)) {
TIFR |= 1 << TOV0; /* clear pending flag */
timerInterrupt();
}
}
return 0;
}