279 lines
11 KiB
C
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;
|
||
|
}
|