USB-LED-Fader/common/usbledfader.h
2006-09-26 18:18:27 +00:00

467 lines
19 KiB
C

#ifndef __usbledfader_h_included__
#define __usbledfader_h_included__
/**
* \file usbledfader.h
* \brief Global definitions and datatypes, used by the firmware and the commandline-client. Also contains the main doxygen-documentation.
* \author Ronald Schaten & Thomas Stegemann
* \version $Id: usbledfader.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $
*
* License: See documentation.
*/
/**
* \mainpage USB-LED-Fader
*
* \section sec_intro Introduction
*
* The USB-LED-Fader is a device to control a number of LEDs via USB. I built
* it to display the online-status of my internet-connection, the
* recording-status of my videorecorder, and warnings if the available
* disc-space is low. You can imagine an endless number of applications for
* this.
*
* The LEDs are controlled with pulse width modulation (PWM). That way, they
* are not only on or off, it is possible to control the brightness. Included
* in the device is a number of 'waveforms' that can be displayed on the LEDs.
* That way, one LED can display some kind of a sinus- or triangular wave
* without any interaction with the controlling host.
*
* Every LED can be controlled individually, each one can display it's own
* waveforms.
*
* You can assign three different waves to every LED: two 'eternal' waves (0 &
* 1). They are displayed alternating until anything different is required. The
* third wave (2) is only displayed once, afterwards the device will switch
* back to alternating between the first two waves.
*
* One wave is described by three parameters: the waveform, the duration for
* one repetition of the wave and the number of repetitions before switching to
* the next wave.
*
* This version supports four LEDs, it should be quite easy to change that
* number between one and eight. I have not tested any number greater than
* four, but I can imagine that the load on the controller can be too high to
* reliably communicate via USB.
*
* There are three parts included in the distribution: The firmware for an
* ATmega8 microcontroller, a commandline-client that can be run under Linux,
* and the circuits needed to build the device.
*
* This project is based on the PowerSwitch example application by Objective
* Development. Like that, it uses Objective Development's firmware-only USB
* driver for Atmel's AVR microcontrollers.
*
* Objective Development's USB driver is a firmware-only implementation of the
* USB 1.1 standard (low speed device) on cheap single chip microcomputers of
* Atmel's AVR series, such as the ATtiny2313 or even some of the small 8 pin
* devices. It implements the standard to the point where useful applications
* can be implemented. See the file "firmware/usbdrv/usbdrv.h" for features and
* limitations.
*
* \section sec_install Building and installing
*
* Both, the firmware and Unix command line tool are built with "make". You may
* need to customize both makefiles.
*
* \subsection sec_fw Firmware
*
* The firmware for this project requires avr-gcc and avr-libc (a C-library for
* the AVR controller). Please read the instructions at
* http://www.nongnu.org/avr-libc/user-manual/install_tools.html for how to
* install the GNU toolchain (avr-gcc, assembler, linker etc.) and avr-libc.
*
* Once you have the GNU toolchain for AVR microcontrollers installed, you can
* run "make" in the subdirectory "firmware". You may have to edit the Makefile
* to use your preferred downloader with "make program". The current version is
* built for avrdude with a parallel connection to an stk200-compatible
* programmer.
*
* If working with a brand-new controller, you may have to set the fuse-bits to
* use the external crystal:
*
* \code
* avrdude -p atmega8 -P /dev/parport0 -c sp12 -U hfuse:w:0xC9:m -U lfuse:w:0x9F:m
* \endcode
*
* Afterwards, you can compile and flash to the device:
*
* \code
* make program
* \endcode
*
* \subsection sec_client Commandline client
*
* The command line tool requires libusb. Please take the packages from your
* system's distribution or download libusb from http://libusb.sourceforge.net/
* and install it before you compile. Change to directory "commandline", check
* the Makefile and edit the settings if required and type
*
* \code
* make
* \endcode
*
* This will build the unix executable "usb-led-fader" which can be used to
* control the device.
*
* \section sec_usage Usage
*
* Connect the device to the USB-port. All LED should flash up to indicate that
* the device is initialized.
*
* Then use the commandline-client as follows:
*
* \code
* usb-led-fader status
* usb-led-fader set <ledId> <waveId> <waveformId> <periodDuration> <repetitionCount>
* usb-led-fader clear <ledId>
* usb-led-fader reset
* usb-led-fader show <waveformId>
* usb-led-fader test
* \endcode
*
* When using the set-function, it is possible to define several waves at once.
* You simply have to give the parameters for all waves. See examples below.
*
* \subsection sec_params Parameters
*
* - \e ledId: ID of the LED (0-n, depending on the number of LEDs in your
* circuit).
* - \e waveId: ID of the wave (0-1: constant waves, 2: override).
* - \e waveformId: ID of the waveform (0-31: brightness, 32-37: patterns). For
* a reference to the patterns, consult the function fade_calculateWaveform()
* in the file "firmware/main.c".
* - \e periodDuration: Time in sec/10 for one repetition of the waveform. A
* value of 0 can be used to reset the wave.
* - \e repetitionCount: Number of repetitions before switching to the next
* wave. A value of 0 can be used to repeat this forever.
*
* \subsection sec_examples Examples
*
* <b>Get the status of all LEDs:</b>
* \code
* usb-led-fader status
* \endcode
* This will result in an output similar to this:
* \code
* LED 0 curid curvalue curpos currep nextupd
* 0 2 26 0 23
* wave waveform length repeat duration updtime
* 0 38 32 1 20 45
* 1 0 1 1 0 1
* 2 0 1 1 0 1
* LED 1 curid curvalue curpos currep nextupd
* 0 14 19 0 19
* wave waveform length repeat duration updtime
* 0 38 32 1 20 45
* 1 0 1 1 0 1
* 2 0 1 1 0 1
* LED 2 curid curvalue curpos currep nextupd
* 0 31 16 0 43
* wave waveform length repeat duration updtime
* 0 38 32 1 20 45
* 1 0 1 1 0 1
* 2 0 1 1 0 1
* LED 3 curid curvalue curpos currep nextupd
* 0 6 9 0 39
* wave waveform length repeat duration updtime
* 0 38 32 1 20 45
* 1 0 1 1 0 1
* 2 0 1 1 0 1
* \endcode
* In this output, the values curvalue, curpos, nextupd and updtime are for
* debugging purposes only. They shouldn't be of interest to the common user.
* The meaning of the other values should be clear.
*
* <b>Set the first LED to keep a middle brightness:</b>
* \code
* usb-led-fader set 0 0 15 10 1
* \endcode
* So, on LED 0 the wave 0 is set to waveform 15. It will stay there for one
* second and will be repeated once before switching to the next wave. There is
* no next wave because we didn't define one, so this waveform will stay
* forever.
*
* <b>Now set a second wave on the first LED, a little brighter than the one
* before:</b>
* \code
* usb-led-fader set 0 1 25 10 1
* \endcode
* This is wave 1 on LED 0, waveform 25 indicates a constant level of
* brightness. After setting the second wave, it will alternate with the first
* one after every second, because both waves have the same duration and the
* same number of repetitions.
*
* <b>Set a third wave on the first LED:</b>
* \code
* usb-led-fader set 0 2 36 20 5
* \endcode
* This sets the third wave (wave 2) on the first LED. Waveform 36 is a nice
* sinus-like wave, so the LED starts to fade. One period of the fading takes 2
* seconds, it is repeated for 5 times. Since this is the third wave, after the
* repetitions the LED returns to alternating between wave 0 and wave 1, this
* wave is discarded.
*
* <b>Set multiple waves at once:</b>
* \code
* usb-led-fader set 0 0 15 10 1 0 1 25 10 1 0 2 36 20 5
* \endcode
* This will set all of the above waves at once. Thus, the first LED will first
* fade the sinus-wave five times, then start alternating between the two
* brightnesses in one-second-rhythm.
*
* <b>Clear the first LED:</b>
* \code
* usb-led-fader clear 0
* \endcode
* This will clear all three waves on the first LED.
*
* <b>Reset the device:</b>
* \code
* usb-led-fader reset
* \endcode
* All LEDs will flash once, to indicate that the device is reset and the LEDs
* are working.
*
* <b>Show a waveform on the screen:</b>
* \code
* usb-led-fader show 36
* \endcode
* This will lead to an output like the following:
* \code
* wave 36 - length 64
* 31: *****
* 30: *********
* 29: ***********
* 28: ***************
* 27: *****************
* 26: *******************
* 25: *******************
* 24: *********************
* 23: ***********************
* 22: *************************
* 21: *************************
* 20: ***************************
* 19: *****************************
* 18: *****************************
* 17: *******************************
* 16: *********************************
* 15: ***********************************
* 14: ***********************************
* 13: *************************************
* 12: ***************************************
* 11: ***************************************
* 10: *****************************************
* 9: *******************************************
* 8: *********************************************
* 7: *********************************************
* 6: ***********************************************
* 5: *************************************************
* 4: *****************************************************
* 3: *******************************************************
* 2: ***********************************************************
* 1: ****************************************************************
* ================================================================
* \endcode
* Keep in mind that the width of the displayed wave corresponds to the length
* of the waveform. If you display a very simple one like the constant
* brightness levels (0-31), the length is 1. Therefore only one column is
* displayed.
*
* <b>Test the device:</b>
* \code
* usb-led-fader test
* \endcode
* This function sends many random numbers to the device. The device returns
* the packages, and the client looks for differences in the sent and the
* received numbers.
*
* \section sec_drawbacks Drawbacks
*
* As mentioned above, controlling the PWM for several LEDs is a lot of work
* for one small microcontroller. Speaking the USB protocol is so, either. Both
* combined result in a lot of load on the device, so the communication with
* the device is not 100% reliable. More than 99% though, at least in our
* tests.
*
* <b>SO BE WARNED:</b> You should not use this device to control the state of
* your nuclear reactor. If you intend to use it in that way despite of this
* warning, please let me know... ;-)
*
*
* \section sec_files Files in the distribution
*
* - \e Readme.txt: The file you are currently reading.
* - \e firmware: Source code of the controller firmware.
* - \e firmware/usbdrv: USB driver -- See Readme.txt in this directory for
* info
* - \e commandline: Source code of the host software (needs libusb).
* - \e common: Files needed by the firmware and the commandline-client.
* - \e circuit: Circuit diagrams in PDF and EAGLE 4 format. A free version of
* EAGLE is available for Linux, Mac OS X and Windows from
* http://www.cadsoft.de/.
* - \e License.txt: Public license for all contents of this project, except
* for the USB driver. Look in firmware/usbdrv/License.txt for further info.
* - \e Changelog.txt: Logfile documenting changes in soft-, firm- and
* hardware.
*
* \section sec_thanks Thanks!
*
* I'd like to thank <b>Objective Development</b> for the possibility to use
* their driver for my project. In fact, this project wouldn't exist without
* the driver.
*
* And I'd like to give special credits to <b>Thomas Stegemann</b>. He wrote
* the PWM-stuff, and I guess it would have been nearly to impossible to me to
* write the rest of the project without his help since C isn't my natural
* language.
*
* \section sec_license About the license
*
* Our work - all contents except for the USB driver - are licensed under the
* GNU General Public License (GPL). A copy of the GPL is included in
* License.txt. The driver itself is licensed under a special license by
* Objective Development. See firmware/usbdrv/License.txt for further info.
*
* <b>(c) 2006 by Ronald Schaten - http://www.schatenseite.de</b>
*/
#include <stdint.h>
/* return codes for USB-communication */
#define msgOK 0 /**< Return code for OK. */
#define msgErr 1 /**< Return code for Error. */
/* These are the vendor specific SETUP commands implemented by our USB device */
#define CMD_ECHO 0 /**< Command to echo the sent data */
#define CMD_GET 1 /**< Command to fetch values */
#define CMD_SET 2 /**< Command to send values */
#define CMD_CLEAR 3 /**< Command to switch off a certain LED */
#define CMD_RESET 4 /**< Command to reset the whole device */
/** Description of one waveform. */
typedef struct S_fade_Waveform {
uint8_t waveformId; /**< ID of this waveform. */
uint8_t waveformLength; /**< Length of this waveform. */
uint8_t waveformRepetition; /**< How often is this waveform to be repeated? */
uint8_t waveformDuration; /**< Duration for one cycle of this waveform, stored for status-output. */
uint32_t waveformUpdateTime; /**< Time between two waveform-samples in calls of timerInterrupt(), calculated from waveformDuration. */
} fade_Waveform;
/** The state of one LED. */
typedef struct S_fade_LedState {
fade_Waveform wave[3]; /**< Three waveforms: base-function1, base-function2 and override-function. */
uint8_t waveCurrentId; /**< Which of the three waveforms is currently displayed? */
uint8_t waveCurrentValue; /**< The current brightness. */
uint8_t waveCurrentPosition; /**< Our position in the current waveform. */
uint8_t waveCurrentRepetition; /**< We are in the n-th repetition. */
int32_t waveNextUpdate; /**< Number of cycles till next update. */
} fade_LedState;
/** Contains the state of all four LEDs. */
typedef struct S_fade_GlobalData {
fade_LedState led[4]; /**< Data for four LEDs. */
} fade_GlobalData;
uint8_t fade_calculateWaveform(uint8_t waveformId, uint8_t waveformPosition);
/**
* Calculate a waveform. Returns either the length of a given waveform or the
* output-level at a certain position in the wave.
* \param waveformId ID of the waveform in question.
* \param waveformPosition 0 or position in the given waveform.
* \return If the waveformPosition is 0, the number of steps in this waveform is returned. Otherwise the resulting output-level, an integer between 0 and 31.
*/
uint8_t fade_calculateWaveform(uint8_t waveformId, uint8_t waveformPosition) {
/*
* values for sinus-wave, amplitude 31, 64 steps:
* awk 'BEGIN{ pi=3.1415927; for(i=1; i<=64; i++) { printf("%.0f, ", sin(i*pi/32)*31) } printf("\n"); }'
* 3, 6, 9, 12, 15, 17, 20, 22, 24, 26, 27, 29, 30, 30, 31, 31, 31, 30,
* 30, 29, 27, 26, 24, 22, 20, 17, 15, 12, 9, 6, 3, -0, -3, -6, -9,
* -12, -15, -17, -20, -22, -24, -26, -27, -29, -30, -30, -31, -31, -31,
* -30, -30, -29, -27, -26, -24, -22, -20, -17, -15, -12, -9, -6, -3, 0
*/
/* sinus-wave:
* awk 'BEGIN{ pi=3.1415927; for(i=1; i<=64; i++) { printf("%.0f, ", sin((i+48)*pi/32)*15+16) } printf("\n"); }'
*/
uint8_t sinus[] = { 1, 1, 2, 2, 3, 4, 4, 5, 6, 8, 9, 10, 12, 13, 15, 16,
17, 19, 20, 22, 23, 24, 26, 27, 28, 28, 29, 30, 30, 31, 31, 31, 31, 31,
30, 30, 29, 28, 28, 27, 26, 24, 23, 22, 20, 19, 17, 16, 15, 13, 12, 10,
9, 8, 6, 5, 4, 4, 3, 2, 2, 1, 1, 1 };
/*
* another nice wave, wider than the original sinus:
* awk 'BEGIN{ pi=3.1415927; for(i=1; i<=32; i++) { printf("%.0f, ", sqrt(sin(i*pi/32)*31+.00001)*sqrt(32)) } printf("\n"); }'
*/
uint8_t widecurve[] = { 10, 14, 17, 19, 22, 23, 25, 26, 28, 29, 30, 30, 31,
31, 31, 31, 31, 31, 31, 30, 30, 29, 28, 26, 25, 23, 22, 19, 17, 14, 10,
0 };
if (waveformId <= 31) {
/* No fading, just a constant level */
if (waveformPosition == 0) {
return 1;
} else {
return waveformId;
}
} else {
switch (waveformId) {
case 32: /* blink */
if (waveformPosition == 0) {
return 2;
} else {
if (waveformPosition == 1) {
return 31;
} else {
return 0;
}
}
case 33: /* triangular */
if (waveformPosition == 0) {
return 62;
} else {
if (waveformPosition <= 32) {
return waveformPosition - 1;
} else {
return 63 - waveformPosition;
}
}
case 34: /* sawtooth rising */
if (waveformPosition == 0) {
return 32;
} else {
return waveformPosition - 1;
}
case 35: /* sawtooth falling */
if (waveformPosition == 0) {
return 32;
} else {
return 31 - (waveformPosition - 1);
}
case 36: /* sinus */
if (waveformPosition == 0) {
return 64;
} else {
return sinus[waveformPosition - 1];
}
case 37: /* wide curve */
if (waveformPosition == 0) {
return 32;
} else {
return widecurve[waveformPosition - 1];
}
case 38: /* wide curve - inverted */
if (waveformPosition == 0) {
return 32;
} else {
return 31 - widecurve[(waveformPosition + 15) % 32];
}
}
}
return 0;
}
#endif