467 lines
19 KiB
C
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
|
|
|