/** * \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 #include #include #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; }