USB-LED-Fader/firmware/main.c
Ronald Schaten e4b8aa91e7 Changed the circuit: zener-diodes instead of the LM317, so the uC runs at 5V
instead of 3.6V. Now, even the blue LED lights at full brightness.
And changed the interface to the controller to use only two wires on port D, so
the firmware needed an update, too.
2006-09-29 21:51:07 +00:00

277 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.2 2006/09/29 21:51:07 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();
DDRD = ~0; /* output SE0 for USB reset */
PORTD = 0x00; /* no pullups on USB pins */
DDRC = 0xff; /* all outputs */
PORTC = 0x00;
j = 0;
while (--j) { /* USB Reset by device only required on Watchdog Reset */
i = 0;
while (--i); /* delay >10ms for USB reset */
}
DDRD = ~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;
}