Erste CVS-Version
This commit is contained in:
		
							
								
								
									
										346
									
								
								License.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										346
									
								
								License.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,346 @@ | ||||
| The following license applies to all but the firmware/usbdrv directories. For | ||||
| that directory, please refer to the firmware/usbdrv/License.txt file for | ||||
| additional license restrictions. | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
|  | ||||
| 		    GNU GENERAL PUBLIC LICENSE | ||||
| 		       Version 2, June 1991 | ||||
|  | ||||
|  Copyright (C) 1989, 1991 Free Software Foundation, Inc. | ||||
|                        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  Everyone is permitted to copy and distribute verbatim copies | ||||
|  of this license document, but changing it is not allowed. | ||||
|  | ||||
| 			    Preamble | ||||
|  | ||||
|   The licenses for most software are designed to take away your | ||||
| freedom to share and change it.  By contrast, the GNU General Public | ||||
| License is intended to guarantee your freedom to share and change free | ||||
| software--to make sure the software is free for all its users.  This | ||||
| General Public License applies to most of the Free Software | ||||
| Foundation's software and to any other program whose authors commit to | ||||
| using it.  (Some other Free Software Foundation software is covered by | ||||
| the GNU Library General Public License instead.)  You can apply it to | ||||
| your programs, too. | ||||
|  | ||||
|   When we speak of free software, we are referring to freedom, not | ||||
| price.  Our General Public Licenses are designed to make sure that you | ||||
| have the freedom to distribute copies of free software (and charge for | ||||
| this service if you wish), that you receive source code or can get it | ||||
| if you want it, that you can change the software or use pieces of it | ||||
| in new free programs; and that you know you can do these things. | ||||
|  | ||||
|   To protect your rights, we need to make restrictions that forbid | ||||
| anyone to deny you these rights or to ask you to surrender the rights. | ||||
| These restrictions translate to certain responsibilities for you if you | ||||
| distribute copies of the software, or if you modify it. | ||||
|  | ||||
|   For example, if you distribute copies of such a program, whether | ||||
| gratis or for a fee, you must give the recipients all the rights that | ||||
| you have.  You must make sure that they, too, receive or can get the | ||||
| source code.  And you must show them these terms so they know their | ||||
| rights. | ||||
|  | ||||
|   We protect your rights with two steps: (1) copyright the software, and | ||||
| (2) offer you this license which gives you legal permission to copy, | ||||
| distribute and/or modify the software. | ||||
|  | ||||
|   Also, for each author's protection and ours, we want to make certain | ||||
| that everyone understands that there is no warranty for this free | ||||
| software.  If the software is modified by someone else and passed on, we | ||||
| want its recipients to know that what they have is not the original, so | ||||
| that any problems introduced by others will not reflect on the original | ||||
| authors' reputations. | ||||
|  | ||||
|   Finally, any free program is threatened constantly by software | ||||
| patents.  We wish to avoid the danger that redistributors of a free | ||||
| program will individually obtain patent licenses, in effect making the | ||||
| program proprietary.  To prevent this, we have made it clear that any | ||||
| patent must be licensed for everyone's free use or not licensed at all. | ||||
|  | ||||
|   The precise terms and conditions for copying, distribution and | ||||
| modification follow. | ||||
|  | ||||
| 		    GNU GENERAL PUBLIC LICENSE | ||||
|    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||
|  | ||||
|   0. This License applies to any program or other work which contains | ||||
| a notice placed by the copyright holder saying it may be distributed | ||||
| under the terms of this General Public License.  The "Program", below, | ||||
| refers to any such program or work, and a "work based on the Program" | ||||
| means either the Program or any derivative work under copyright law: | ||||
| that is to say, a work containing the Program or a portion of it, | ||||
| either verbatim or with modifications and/or translated into another | ||||
| language.  (Hereinafter, translation is included without limitation in | ||||
| the term "modification".)  Each licensee is addressed as "you". | ||||
|  | ||||
| Activities other than copying, distribution and modification are not | ||||
| covered by this License; they are outside its scope.  The act of | ||||
| running the Program is not restricted, and the output from the Program | ||||
| is covered only if its contents constitute a work based on the | ||||
| Program (independent of having been made by running the Program). | ||||
| Whether that is true depends on what the Program does. | ||||
|  | ||||
|   1. You may copy and distribute verbatim copies of the Program's | ||||
| source code as you receive it, in any medium, provided that you | ||||
| conspicuously and appropriately publish on each copy an appropriate | ||||
| copyright notice and disclaimer of warranty; keep intact all the | ||||
| notices that refer to this License and to the absence of any warranty; | ||||
| and give any other recipients of the Program a copy of this License | ||||
| along with the Program. | ||||
|  | ||||
| You may charge a fee for the physical act of transferring a copy, and | ||||
| you may at your option offer warranty protection in exchange for a fee. | ||||
|  | ||||
|   2. You may modify your copy or copies of the Program or any portion | ||||
| of it, thus forming a work based on the Program, and copy and | ||||
| distribute such modifications or work under the terms of Section 1 | ||||
| above, provided that you also meet all of these conditions: | ||||
|  | ||||
|     a) You must cause the modified files to carry prominent notices | ||||
|     stating that you changed the files and the date of any change. | ||||
|  | ||||
|     b) You must cause any work that you distribute or publish, that in | ||||
|     whole or in part contains or is derived from the Program or any | ||||
|     part thereof, to be licensed as a whole at no charge to all third | ||||
|     parties under the terms of this License. | ||||
|  | ||||
|     c) If the modified program normally reads commands interactively | ||||
|     when run, you must cause it, when started running for such | ||||
|     interactive use in the most ordinary way, to print or display an | ||||
|     announcement including an appropriate copyright notice and a | ||||
|     notice that there is no warranty (or else, saying that you provide | ||||
|     a warranty) and that users may redistribute the program under | ||||
|     these conditions, and telling the user how to view a copy of this | ||||
|     License.  (Exception: if the Program itself is interactive but | ||||
|     does not normally print such an announcement, your work based on | ||||
|     the Program is not required to print an announcement.) | ||||
|  | ||||
| These requirements apply to the modified work as a whole.  If | ||||
| identifiable sections of that work are not derived from the Program, | ||||
| and can be reasonably considered independent and separate works in | ||||
| themselves, then this License, and its terms, do not apply to those | ||||
| sections when you distribute them as separate works.  But when you | ||||
| distribute the same sections as part of a whole which is a work based | ||||
| on the Program, the distribution of the whole must be on the terms of | ||||
| this License, whose permissions for other licensees extend to the | ||||
| entire whole, and thus to each and every part regardless of who wrote it. | ||||
|  | ||||
| Thus, it is not the intent of this section to claim rights or contest | ||||
| your rights to work written entirely by you; rather, the intent is to | ||||
| exercise the right to control the distribution of derivative or | ||||
| collective works based on the Program. | ||||
|  | ||||
| In addition, mere aggregation of another work not based on the Program | ||||
| with the Program (or with a work based on the Program) on a volume of | ||||
| a storage or distribution medium does not bring the other work under | ||||
| the scope of this License. | ||||
|  | ||||
|   3. You may copy and distribute the Program (or a work based on it, | ||||
| under Section 2) in object code or executable form under the terms of | ||||
| Sections 1 and 2 above provided that you also do one of the following: | ||||
|  | ||||
|     a) Accompany it with the complete corresponding machine-readable | ||||
|     source code, which must be distributed under the terms of Sections | ||||
|     1 and 2 above on a medium customarily used for software interchange; or, | ||||
|  | ||||
|     b) Accompany it with a written offer, valid for at least three | ||||
|     years, to give any third party, for a charge no more than your | ||||
|     cost of physically performing source distribution, a complete | ||||
|     machine-readable copy of the corresponding source code, to be | ||||
|     distributed under the terms of Sections 1 and 2 above on a medium | ||||
|     customarily used for software interchange; or, | ||||
|  | ||||
|     c) Accompany it with the information you received as to the offer | ||||
|     to distribute corresponding source code.  (This alternative is | ||||
|     allowed only for noncommercial distribution and only if you | ||||
|     received the program in object code or executable form with such | ||||
|     an offer, in accord with Subsection b above.) | ||||
|  | ||||
| The source code for a work means the preferred form of the work for | ||||
| making modifications to it.  For an executable work, complete source | ||||
| code means all the source code for all modules it contains, plus any | ||||
| associated interface definition files, plus the scripts used to | ||||
| control compilation and installation of the executable.  However, as a | ||||
| special exception, the source code distributed need not include | ||||
| anything that is normally distributed (in either source or binary | ||||
| form) with the major components (compiler, kernel, and so on) of the | ||||
| operating system on which the executable runs, unless that component | ||||
| itself accompanies the executable. | ||||
|  | ||||
| If distribution of executable or object code is made by offering | ||||
| access to copy from a designated place, then offering equivalent | ||||
| access to copy the source code from the same place counts as | ||||
| distribution of the source code, even though third parties are not | ||||
| compelled to copy the source along with the object code. | ||||
|  | ||||
|   4. You may not copy, modify, sublicense, or distribute the Program | ||||
| except as expressly provided under this License.  Any attempt | ||||
| otherwise to copy, modify, sublicense or distribute the Program is | ||||
| void, and will automatically terminate your rights under this License. | ||||
| However, parties who have received copies, or rights, from you under | ||||
| this License will not have their licenses terminated so long as such | ||||
| parties remain in full compliance. | ||||
|  | ||||
|   5. You are not required to accept this License, since you have not | ||||
| signed it.  However, nothing else grants you permission to modify or | ||||
| distribute the Program or its derivative works.  These actions are | ||||
| prohibited by law if you do not accept this License.  Therefore, by | ||||
| modifying or distributing the Program (or any work based on the | ||||
| Program), you indicate your acceptance of this License to do so, and | ||||
| all its terms and conditions for copying, distributing or modifying | ||||
| the Program or works based on it. | ||||
|  | ||||
|   6. Each time you redistribute the Program (or any work based on the | ||||
| Program), the recipient automatically receives a license from the | ||||
| original licensor to copy, distribute or modify the Program subject to | ||||
| these terms and conditions.  You may not impose any further | ||||
| restrictions on the recipients' exercise of the rights granted herein. | ||||
| You are not responsible for enforcing compliance by third parties to | ||||
| this License. | ||||
|  | ||||
|   7. If, as a consequence of a court judgment or allegation of patent | ||||
| infringement or for any other reason (not limited to patent issues), | ||||
| conditions are imposed on you (whether by court order, agreement or | ||||
| otherwise) that contradict the conditions of this License, they do not | ||||
| excuse you from the conditions of this License.  If you cannot | ||||
| distribute so as to satisfy simultaneously your obligations under this | ||||
| License and any other pertinent obligations, then as a consequence you | ||||
| may not distribute the Program at all.  For example, if a patent | ||||
| license would not permit royalty-free redistribution of the Program by | ||||
| all those who receive copies directly or indirectly through you, then | ||||
| the only way you could satisfy both it and this License would be to | ||||
| refrain entirely from distribution of the Program. | ||||
|  | ||||
| If any portion of this section is held invalid or unenforceable under | ||||
| any particular circumstance, the balance of the section is intended to | ||||
| apply and the section as a whole is intended to apply in other | ||||
| circumstances. | ||||
|  | ||||
| It is not the purpose of this section to induce you to infringe any | ||||
| patents or other property right claims or to contest validity of any | ||||
| such claims; this section has the sole purpose of protecting the | ||||
| integrity of the free software distribution system, which is | ||||
| implemented by public license practices.  Many people have made | ||||
| generous contributions to the wide range of software distributed | ||||
| through that system in reliance on consistent application of that | ||||
| system; it is up to the author/donor to decide if he or she is willing | ||||
| to distribute software through any other system and a licensee cannot | ||||
| impose that choice. | ||||
|  | ||||
| This section is intended to make thoroughly clear what is believed to | ||||
| be a consequence of the rest of this License. | ||||
|  | ||||
|   8. If the distribution and/or use of the Program is restricted in | ||||
| certain countries either by patents or by copyrighted interfaces, the | ||||
| original copyright holder who places the Program under this License | ||||
| may add an explicit geographical distribution limitation excluding | ||||
| those countries, so that distribution is permitted only in or among | ||||
| countries not thus excluded.  In such case, this License incorporates | ||||
| the limitation as if written in the body of this License. | ||||
|  | ||||
|   9. The Free Software Foundation may publish revised and/or new versions | ||||
| of the General Public License from time to time.  Such new versions will | ||||
| be similar in spirit to the present version, but may differ in detail to | ||||
| address new problems or concerns. | ||||
|  | ||||
| Each version is given a distinguishing version number.  If the Program | ||||
| specifies a version number of this License which applies to it and "any | ||||
| later version", you have the option of following the terms and conditions | ||||
| either of that version or of any later version published by the Free | ||||
| Software Foundation.  If the Program does not specify a version number of | ||||
| this License, you may choose any version ever published by the Free Software | ||||
| Foundation. | ||||
|  | ||||
|   10. If you wish to incorporate parts of the Program into other free | ||||
| programs whose distribution conditions are different, write to the author | ||||
| to ask for permission.  For software which is copyrighted by the Free | ||||
| Software Foundation, write to the Free Software Foundation; we sometimes | ||||
| make exceptions for this.  Our decision will be guided by the two goals | ||||
| of preserving the free status of all derivatives of our free software and | ||||
| of promoting the sharing and reuse of software generally. | ||||
|  | ||||
| 			    NO WARRANTY | ||||
|  | ||||
|   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | ||||
| FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN | ||||
| OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | ||||
| PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | ||||
| OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||
| MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS | ||||
| TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE | ||||
| PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | ||||
| REPAIR OR CORRECTION. | ||||
|  | ||||
|   12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||||
| WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | ||||
| REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | ||||
| INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | ||||
| OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | ||||
| TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | ||||
| YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | ||||
| PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | ||||
| POSSIBILITY OF SUCH DAMAGES. | ||||
|  | ||||
| 		     END OF TERMS AND CONDITIONS | ||||
|  | ||||
| 	    How to Apply These Terms to Your New Programs | ||||
|  | ||||
|   If you develop a new program, and you want it to be of the greatest | ||||
| possible use to the public, the best way to achieve this is to make it | ||||
| free software which everyone can redistribute and change under these terms. | ||||
|  | ||||
|   To do so, attach the following notices to the program.  It is safest | ||||
| to attach them to the start of each source file to most effectively | ||||
| convey the exclusion of warranty; and each file should have at least | ||||
| the "copyright" line and a pointer to where the full notice is found. | ||||
|  | ||||
|     <one line to give the program's name and a brief idea of what it does.> | ||||
|     Copyright (C) <year>  <name of author> | ||||
|  | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
|  | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
|  | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program; if not, write to the Free Software | ||||
|     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
|  | ||||
| Also add information on how to contact you by electronic and paper mail. | ||||
|  | ||||
| If the program is interactive, make it output a short notice like this | ||||
| when it starts in an interactive mode: | ||||
|  | ||||
|     Gnomovision version 69, Copyright (C) year name of author | ||||
|     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | ||||
|     This is free software, and you are welcome to redistribute it | ||||
|     under certain conditions; type `show c' for details. | ||||
|  | ||||
| The hypothetical commands `show w' and `show c' should show the appropriate | ||||
| parts of the General Public License.  Of course, the commands you use may | ||||
| be called something other than `show w' and `show c'; they could even be | ||||
| mouse-clicks or menu items--whatever suits your program. | ||||
|  | ||||
| You should also get your employer (if you work as a programmer) or your | ||||
| school, if any, to sign a "copyright disclaimer" for the program, if | ||||
| necessary.  Here is a sample; alter the names: | ||||
|  | ||||
|   Yoyodyne, Inc., hereby disclaims all copyright interest in the program | ||||
|   `Gnomovision' (which makes passes at compilers) written by James Hacker. | ||||
|  | ||||
|   <signature of Ty Coon>, 1 April 1989 | ||||
|   Ty Coon, President of Vice | ||||
|  | ||||
| This General Public License does not permit incorporating your program into | ||||
| proprietary programs.  If your program is a subroutine library, you may | ||||
| consider it more useful to permit linking proprietary applications with the | ||||
| library.  If this is what you want to do, use the GNU Library General | ||||
| Public License instead of this License. | ||||
							
								
								
									
										297
									
								
								Readme.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								Readme.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,297 @@ | ||||
| $Id: Readme.txt,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  | ||||
| For full documentation and examples, take a look at htmldoc/index.html. | ||||
|  | ||||
|  | ||||
| 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. | ||||
|  | ||||
|  | ||||
| Building and installing | ||||
| ======================= | ||||
|  | ||||
| Both, the firmware and Unix command line tool are built with "make". You may | ||||
| need to customize both makefiles. | ||||
|  | ||||
|  | ||||
| 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: | ||||
|  | ||||
|  avrdude -p atmega8 -P /dev/parport0 -c sp12 -U hfuse:w:0xC9:m \ | ||||
|                                              -U lfuse:w:0x9F:m | ||||
|  | ||||
| Afterwards, you can compile and flash to the device: | ||||
|  | ||||
|  make program | ||||
|  | ||||
|  | ||||
| Commandline client | ||||
| ------------------ | ||||
|  | ||||
| The command line tool requires libusb. Please 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 | ||||
|  | ||||
|  make | ||||
|  | ||||
| This will build the unix executable "usb-led-fader" which can be used to | ||||
| control the device. | ||||
|  | ||||
|  | ||||
| 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: | ||||
|  | ||||
|  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 | ||||
|  | ||||
| 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. | ||||
|  | ||||
|  | ||||
| Parameters | ||||
| ---------- | ||||
|  | ||||
| - ledId: ID of the LED (0-n, depending on the number of LEDs in your circuit). | ||||
| - waveId: ID of the wave (0-1: constant waves, 2: override). | ||||
| - 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". | ||||
| - periodDuration: Time in sec/10 for one repetition of the waveform. A value of | ||||
|   0 can be used to reset the wave. | ||||
| - repetitionCount: Number of repetitions before switching to the next wave. A | ||||
|   value of 0 can be used to repeat this forever. | ||||
|  | ||||
|  | ||||
| Examples | ||||
| -------- | ||||
|  | ||||
| -> Get the status of all LEDs: | ||||
|  usb-led-fader status | ||||
| This will result in an output similar to this: | ||||
|  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 | ||||
| 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. | ||||
|  | ||||
| -> Set the first LED to keep a middle brightness: | ||||
|  usb-led-fader set 0 0 15 10 1 | ||||
| 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. | ||||
|  | ||||
| -> Now set a second wave on the first LED, a little brighter than the one | ||||
|    before: | ||||
|  usb-led-fader set 0 1 25 10 1 | ||||
| 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. | ||||
|  | ||||
| -> Set a third wave on the first LED: | ||||
|  usb-led-fader set 0 2 36 20 5 | ||||
| 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. | ||||
|  | ||||
| -> Set multiple waves at once: | ||||
|  usb-led-fader set 0 0 15 10 1 0 1 25 10 1 0 2 36 20 5 | ||||
| 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. | ||||
|  | ||||
| Clear the first LED: | ||||
|  usb-led-fader clear 0 | ||||
| This will clear all three waves on the first LED. | ||||
|  | ||||
| -> Reset the device: | ||||
|  usb-led-fader reset | ||||
| All LEDs will flash once, to indicate that the device is reset and the LEDs are | ||||
| working. | ||||
|  | ||||
| -> Show a waveform on the screen: | ||||
|  usb-led-fader show 36 | ||||
| This will lead to an output like the following: | ||||
|  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: **************************************************************** | ||||
|      ================================================================ | ||||
| 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. | ||||
|  | ||||
| -> Test the device: | ||||
|  usb-led-fader test | ||||
| 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. | ||||
|  | ||||
|  | ||||
| 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. | ||||
|  | ||||
| SO BE WARNED: 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... ;-) | ||||
|  | ||||
|  | ||||
| Files in the distribution | ||||
| ========================= | ||||
|  | ||||
| - Readme.txt: The file you are currently reading. | ||||
| - firmware: Source code of the controller firmware. | ||||
| - firmware/usbdrv: USB driver -- See Readme.txt in this directory for info | ||||
| - commandline: Source code of the host software (needs libusb). | ||||
| - common: Files needed by the firmware and the commandline-client. | ||||
| - 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/. | ||||
| - License.txt: Public license for all contents of this project, except for the | ||||
|   USB driver. Look in firmware/usbdrv/License.txt for further info. | ||||
| - Changelog.txt: Logfile documenting changes in soft-, firm- and hardware. | ||||
|  | ||||
|  | ||||
| Thanks! | ||||
| ======= | ||||
|  | ||||
| I'd like to thank Objective Development 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 Thomas Stegemann. 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. | ||||
|  | ||||
|  | ||||
| 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. | ||||
|  | ||||
|  | ||||
| (c) 2006 by Ronald Schaten - http://www.schatenseite.de | ||||
							
								
								
									
										
											BIN
										
									
								
								circuit/circuit.brd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								circuit/circuit.brd
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								circuit/circuit.sch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								circuit/circuit.sch
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										30
									
								
								circuit/partlist.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								circuit/partlist.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| Partlist | ||||
|  | ||||
| Exported from circuit.sch at  9/15/2006 14:21:25  | ||||
|  | ||||
| EAGLE Version 4.16 Copyright (c) 1988-2005 CadSoft | ||||
|  | ||||
| Part     Value          Device          Package      Library  Sheet | ||||
|  | ||||
| C1       4,7u           CPOL-EUE2.5-5   E2,5-5       rcl      1 | ||||
| C2       100n           C-EU025-024X044 C025-024X044 rcl      1 | ||||
| C3       22p            C-EU025-024X044 C025-024X044 rcl      1 | ||||
| C4       22p            C-EU025-024X044 C025-024X044 rcl      1 | ||||
| IC1      MEGA8-P        MEGA8-P         DIL28-3      avr      1 | ||||
| IC2      LM317LZ        LM317LZ         TO92         linear   1 | ||||
| JP1      ISP            JP5Q            JP5Q         jumper   1 | ||||
| LED1                    LED5MM          LED5MM       led      1 | ||||
| LED2                    LED5MM          LED5MM       led      1 | ||||
| LED3                    LED5MM          LED5MM       led      1 | ||||
| LED4                    LED5MM          LED5MM       led      1 | ||||
| Q1       12MHz          CRYTALHC18U-V   HC18U-V      crystal  1 | ||||
| R1       432            R-EU_0207/10    0207/10      rcl      1 | ||||
| R2       240            R-EU_0207/10    0207/10      rcl      1 | ||||
| R3       1k5            R-EU_0207/10    0207/10      rcl      1 | ||||
| R4       68             R-EU_0207/10    0207/10      rcl      1 | ||||
| R5       68             R-EU_0207/10    0207/10      rcl      1 | ||||
| R6       1k             R-EU_0207/10    0207/10      rcl      1 | ||||
| R7       1k             R-EU_0207/10    0207/10      rcl      1 | ||||
| R8       1k             R-EU_0207/10    0207/10      rcl      1 | ||||
| R9       1k             R-EU_0207/10    0207/10      rcl      1 | ||||
| X1                      PN61729         PN61729      con-berg 1 | ||||
							
								
								
									
										23
									
								
								commandline/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								commandline/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| # $Id: Makefile,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  | ||||
| CC              = gcc | ||||
| LIBUSB_CONFIG   = libusb-config | ||||
| # Make sure that libusb-config is in the search path or specify a full path. On | ||||
| # Windows, there is no libusb-config and you must configure the options below | ||||
| # manually. See examples. | ||||
|  | ||||
| CFLAGS          = `$(LIBUSB_CONFIG) --cflags` -O -Wall -I../common | ||||
|  | ||||
| LIBS            = `$(LIBUSB_CONFIG) --libs` | ||||
|  | ||||
| all: usb-led-fader | ||||
|  | ||||
| .c.o: | ||||
| 	$(CC) $(CFLAGS) -c $< | ||||
|  | ||||
| usb-led-fader: usb-led-fader.o | ||||
| 	$(CC) -o usb-led-fader usb-led-fader.o $(LIBS) | ||||
|  | ||||
| clean: | ||||
| 	rm -f *.o | ||||
| 	rm -f usb-led-fader | ||||
							
								
								
									
										426
									
								
								commandline/usb-led-fader.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										426
									
								
								commandline/usb-led-fader.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,426 @@ | ||||
| /** | ||||
|  * \file usb-led-fader.c | ||||
|  * \brief Commandline-tool for the USB-LED-Fader. | ||||
|  * \author Ronald Schaten | ||||
|  * \version $Id: usb-led-fader.c,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  */ | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <usb.h>                /* this is libusb, see http://libusb.sourceforge.net/ */ | ||||
|  | ||||
| #include "usbledfader.h" | ||||
| #include "channels.h" | ||||
|  | ||||
| #define USBDEV_SHARED_VENDOR    0x16C0  /**< VOTI */ | ||||
| #define USBDEV_SHARED_PRODUCT   0x05DC  /**< Obdev's free shared PID. Use obdev's generic shared VID/PID pair and follow the rules outlined in firmware/usbdrv/USBID-License.txt. */ | ||||
|  | ||||
| /* These are error codes for the communication via USB. */ | ||||
| #define USB_ERROR_NOTFOUND  1 /**< Error code if the device isn't found. */ | ||||
| #define USB_ERROR_ACCESS    2 /**< Error code if the device isn't accessible. */ | ||||
| #define USB_ERROR_IO        3 /**< Error code if errors in the communication with the device occur. */ | ||||
|  | ||||
| /** | ||||
|  * Displays usage-informations. This function is called if the parameters | ||||
|  * cannot be parsed. | ||||
|  * \param name The name of this application. | ||||
|  */ | ||||
| void usage(char *name) | ||||
| { | ||||
|     fprintf(stderr, "usage:\n"); | ||||
|     fprintf(stderr, "  %s status\n", name); | ||||
|     fprintf(stderr, "  %s set ledId waveId waveformId periodDuration repetitionCount\n", name); | ||||
|     fprintf(stderr, "  %s clear ledId\n", name); | ||||
|     fprintf(stderr, "  %s reset\n", name); | ||||
|     fprintf(stderr, "  %s show waveformId\n", name); | ||||
|     fprintf(stderr, "  %s test\n\n", name); | ||||
|     fprintf(stderr, "parameters:\n"); | ||||
|     fprintf(stderr, "  ledId: ID of the LED (0-%d).\n", CHANNELS - 1); | ||||
|     fprintf(stderr, "  waveId: ID of the wave (0-1: constant waves, 2: override).\n"); | ||||
|     fprintf(stderr, "  waveformId: ID of the waveform (0-31: brightness, 32-37: patterns).\n"); | ||||
|     fprintf(stderr, "  periodDuration: Time in sec/10 for one repetition of the waveform.\n"); | ||||
|     fprintf(stderr, "                  A value of 0 can be used to reset the wave.\n"); | ||||
|     fprintf(stderr, "  repetitionCount: Number of repetitions before switching to the next wave.\n"); | ||||
|     fprintf(stderr, "                   A value of 0 can be used to repeat this forever.\n"); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Reads and converts a string from USB. The conversion to ASCII is 'lossy' (unknown characters become '?'). | ||||
|  * \param dev Handle of the USB-Device. | ||||
|  * \param index Index of the required data. | ||||
|  * \param langid Index of the expected language. | ||||
|  * \param buf Buffer to contain the return-string. | ||||
|  * \param buflen Length of buf. | ||||
|  * \return Length of the string. | ||||
|  */ | ||||
| int usbGetStringAscii(usb_dev_handle * dev, int index, int langid, char *buf, int buflen) { | ||||
|     char buffer[256]; | ||||
|     int rval, i; | ||||
|  | ||||
|     if ((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, langid, buffer, sizeof(buffer), 1000)) < 0) { | ||||
|         return rval; | ||||
|     } | ||||
|     if (buffer[1] != USB_DT_STRING) { | ||||
|         return 0; | ||||
|     } | ||||
|     if ((unsigned char) buffer[0] < rval) { | ||||
|         rval = (unsigned char) buffer[0]; | ||||
|     } | ||||
|     rval /= 2; | ||||
|     /* lossy conversion to ISO Latin1 */ | ||||
|     for (i = 1; i < rval; i++) { | ||||
|         if (i > buflen) { | ||||
|             /* destination buffer overflow */ | ||||
|             break; | ||||
|         } | ||||
|         buf[i - 1] = buffer[2 * i]; | ||||
|         if (buffer[2 * i + 1] != 0) { | ||||
|             /* outside of ISO Latin1 range */ | ||||
|             buf[i - 1] = '?'; | ||||
|         } | ||||
|     } | ||||
|     buf[i - 1] = 0; | ||||
|     return i - 1; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Connect to the USB-device. Loops through all connected USB-Devices and | ||||
|  * searches our counterpart. | ||||
|  * \param device Handle to address the device. | ||||
|  * \param vendor USBDEV_SHARED_VENDOR as defined. | ||||
|  * \param vendorName In our case "www.schatenseite.de". | ||||
|  * \param product USBDEV_SHARED_PRODUCT as defined. | ||||
|  * \param productName In our case "USB-LED-Fader". | ||||
|  * \return Error code. | ||||
|  */ | ||||
| int usbOpenDevice(usb_dev_handle ** device, int vendor, char *vendorName, int product, char *productName) { | ||||
|     struct usb_bus *bus; | ||||
|     struct usb_device *dev; | ||||
|     usb_dev_handle *handle = NULL; | ||||
|     int errorCode = USB_ERROR_NOTFOUND; | ||||
|     static int didUsbInit = 0; | ||||
|  | ||||
|     if (!didUsbInit) { | ||||
|         didUsbInit = 1; | ||||
|         usb_init(); | ||||
|     } | ||||
|     usb_find_busses(); | ||||
|     usb_find_devices(); | ||||
|     for (bus = usb_get_busses(); bus; bus = bus->next) { | ||||
|         for (dev = bus->devices; dev; dev = dev->next) { | ||||
|             if (dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product) { | ||||
|                 char string[256]; | ||||
|                 int len; | ||||
|                 handle = usb_open(dev); /* we need to open the device in order to query strings */ | ||||
|                 if (!handle) { | ||||
|                     errorCode = USB_ERROR_ACCESS; | ||||
|                     fprintf(stderr, "Warning: cannot open USB device: %s\n", usb_strerror()); | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (vendorName == NULL && productName == NULL) {        /* name does not matter */ | ||||
|                     break; | ||||
|                 } | ||||
|                 /* now check whether the names match: */ | ||||
|                 len = usbGetStringAscii(handle, dev->descriptor.iManufacturer, 0x0409, string, sizeof(string)); | ||||
|                 if (len < 0) { | ||||
|                     errorCode = USB_ERROR_IO; | ||||
|                     fprintf(stderr, "Warning: cannot query manufacturer for device: %s\n", usb_strerror()); | ||||
|                 } else { | ||||
|                     errorCode = USB_ERROR_NOTFOUND; | ||||
|                     /* fprintf(stderr, "seen device from vendor ->%s<-\n", string); */ | ||||
|                     if (strcmp(string, vendorName) == 0) { | ||||
|                         len = usbGetStringAscii(handle, dev->descriptor.iProduct, 0x0409, string, sizeof(string)); | ||||
|                         if (len < 0) { | ||||
|                             errorCode = USB_ERROR_IO; | ||||
|                             fprintf(stderr, "Warning: cannot query product for device: %s\n", usb_strerror()); | ||||
|                         } else { | ||||
|                             errorCode = USB_ERROR_NOTFOUND; | ||||
|                             /* fprintf(stderr, "seen product ->%s<-\n", string); */ | ||||
|                             if (strcmp(string, productName) == 0) { | ||||
|                                 break; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 usb_close(handle); | ||||
|                 handle = NULL; | ||||
|             } | ||||
|         } | ||||
|         if (handle) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     if (handle != NULL) { | ||||
|         errorCode = 0; | ||||
|         *device = handle; | ||||
|     } | ||||
|     return errorCode; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Test connection to the device. The test consists of writing 1000 random | ||||
|  * numbers to the device and checking the echo. This should discover systematic | ||||
|  * bit errors (e.g. in bit stuffing). | ||||
|  * \param handle Handle to talk to the device. | ||||
|  * \param argc Number of arguments. | ||||
|  * \param argv Arguments. | ||||
|  */ | ||||
| void dev_test(usb_dev_handle *handle, int argc, char** argv) { | ||||
|     unsigned char buffer[8]; | ||||
|     int nBytes; | ||||
|     int i, v, r; | ||||
|     if (argc != 2) { | ||||
|         usage(argv[0]); | ||||
|         exit(1); | ||||
|     } | ||||
|     for (i = 0; i < 1000; i++) { | ||||
|         v = rand() & 0xffff; | ||||
|         nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_ECHO, v, 0, (char *) buffer, sizeof(buffer), 5000); | ||||
|         if (nBytes < 2) { | ||||
|             if (nBytes < 0) { | ||||
|                 fprintf(stderr, "USB error: %s\n", usb_strerror()); | ||||
|             } | ||||
|             fprintf(stderr, "only %d bytes received in iteration %d\n", nBytes, i); | ||||
|             exit(1); | ||||
|         } | ||||
|         r = buffer[0] | (buffer[1] << 8); | ||||
|         if (r != v) { | ||||
|             fprintf(stderr, "data error: received 0x%x instead of 0x%x in iteration %d\n", r, v, i); | ||||
|             exit(1); | ||||
|         } | ||||
|     } | ||||
|     printf("test succeeded\n"); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Set waves. It is possible to set any number of waves at once. | ||||
|  * \param handle Handle to talk to the device. | ||||
|  * \param argc Number of arguments. | ||||
|  * \param argv Arguments. | ||||
|  */ | ||||
| void dev_set(usb_dev_handle *handle, int argc, char** argv) { | ||||
|     unsigned char buffer[8]; | ||||
|     int nBytes; | ||||
|     int parameter; | ||||
|     if ((argc < 7) || ((argc - 2) % 5 != 0)) { | ||||
|         usage(argv[0]); | ||||
|         exit(1); | ||||
|     } | ||||
|     for (parameter = 2; (parameter + 4) < argc; parameter += 5) { | ||||
|         int ledId = atoi(argv[parameter + 0]); | ||||
|         if ((ledId < 0) || (ledId > (CHANNELS - 1))) { | ||||
|             fprintf(stderr, "invalid ledId: %d\n", ledId); | ||||
|             exit(1); | ||||
|         } | ||||
|         int waveId = atoi(argv[parameter + 1]); | ||||
|         if ((waveId < 0) || (waveId > 2)) { | ||||
|             fprintf(stderr, "invalid waveId: %d\n", waveId); | ||||
|             exit(1); | ||||
|         } | ||||
|         int waveformId = atoi(argv[parameter + 2]); | ||||
|         if ((waveformId < 0) || (waveformId > 38)) { | ||||
|             fprintf(stderr, "invalid waveformId: %d\n", waveformId); | ||||
|             exit(1); | ||||
|         } | ||||
|         int periodDuration = atoi(argv[parameter + 3]); | ||||
|         if ((periodDuration < 0) || (periodDuration > 255)) { | ||||
|             fprintf(stderr, "invalid periodDuration: %d\n", periodDuration); | ||||
|             exit(1); | ||||
|         } | ||||
|         int repetitionCount = atoi(argv[parameter + 4]); | ||||
|         if ((repetitionCount < 0) || (repetitionCount > 255)) { | ||||
|             fprintf(stderr, "invalid repetitionCount: %d\n", repetitionCount); | ||||
|             exit(1); | ||||
|         } | ||||
|  | ||||
|         buffer[0] = CMD_SET; | ||||
|         buffer[1] = ledId; | ||||
|         buffer[2] = waveId; | ||||
|         buffer[3] = waveformId; | ||||
|         buffer[4] = periodDuration; | ||||
|         buffer[5] = repetitionCount; | ||||
|  | ||||
|         nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, CMD_SET, ledId, 0, (char *) buffer, sizeof(buffer), 5000); | ||||
|  | ||||
|         if (nBytes < 0) { | ||||
|             fprintf(stderr, "USB error: %s\n", usb_strerror()); | ||||
|             exit(1); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Clear all waves on one LED. | ||||
|  * \param handle Handle to talk to the device. | ||||
|  * \param argc Number of arguments. | ||||
|  * \param argv Arguments. | ||||
|  */ | ||||
| void dev_clear(usb_dev_handle *handle, int argc, char** argv) { | ||||
|     unsigned char buffer[8]; | ||||
|     int nBytes; | ||||
|     if (argc != 3) { | ||||
|         usage(argv[0]); | ||||
|         exit(1); | ||||
|     } | ||||
|     int ledId = atoi(argv[2]); | ||||
|     if ((ledId < 0) || (ledId > (CHANNELS - 1))) { | ||||
|         fprintf(stderr, "invalid LED: %d\n", ledId); | ||||
|         exit(1); | ||||
|     } | ||||
|     nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, CMD_CLEAR, ledId, 0, (char *) buffer, sizeof(buffer), 5000); | ||||
|     if (nBytes < 0) { | ||||
|         fprintf(stderr, "USB error: %s\n", usb_strerror()); | ||||
|         exit(1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Get the status of the device. Status information is printed in detail. | ||||
|  * \param handle Handle to talk to the device. | ||||
|  * \param argc Number of arguments. | ||||
|  * \param argv Arguments. | ||||
|  */ | ||||
| void dev_status(usb_dev_handle *handle, int argc, char** argv) { | ||||
|     int nBytes; | ||||
|     int i, j; | ||||
|     static fade_GlobalData fade_globalData; /* contains the state of all four LEDs. */ | ||||
|     if (argc != 2) { | ||||
|         usage(argv[0]); | ||||
|         exit(1); | ||||
|     } | ||||
|     nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_GET, 0, 0, (char *) &fade_globalData, sizeof(fade_globalData), 5000); | ||||
|     if (nBytes < 0) { | ||||
|         fprintf(stderr, "USB error: %s\n", usb_strerror()); | ||||
|         exit(1); | ||||
|     } | ||||
|     if (nBytes != sizeof(fade_globalData)) { | ||||
|         fprintf(stderr, "USB oddity: %d bytes received, %d bytes expected.\n", nBytes, sizeof(fade_globalData)); | ||||
|         exit(1); | ||||
|     } | ||||
|     for (i = 0; i < CHANNELS; i++) { | ||||
|         printf("LED %d      %10s %10s %10s %10s %10s\n", i, "curid", "curvalue", "curpos", "currep", "nextupd"); | ||||
|         printf("           %10d %10d %10d %10d %10d\n", | ||||
|             fade_globalData.led[i].waveCurrentId, | ||||
|             fade_globalData.led[i].waveCurrentValue, | ||||
|             fade_globalData.led[i].waveCurrentPosition, | ||||
|             fade_globalData.led[i].waveCurrentRepetition, | ||||
|             fade_globalData.led[i].waveNextUpdate); | ||||
|         printf("%10s %10s %10s %10s %10s %10s\n", "wave", "waveform", "length", "repeat", "duration", "updtime"); | ||||
|         for (j = 0; j < 3; j++) { | ||||
|             printf("%10d %10d %10d %10d %10d %10d\n", | ||||
|                 j, | ||||
|                 fade_globalData.led[i].wave[j].waveformId, | ||||
|                 fade_globalData.led[i].wave[j].waveformLength, | ||||
|                 fade_globalData.led[i].wave[j].waveformRepetition, | ||||
|                 fade_globalData.led[i].wave[j].waveformDuration, | ||||
|                 fade_globalData.led[i].wave[j].waveformUpdateTime); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Reset the device. | ||||
|  * \param handle Handle to talk to the device. | ||||
|  * \param argc Number of arguments. | ||||
|  * \param argv Arguments. | ||||
|  */ | ||||
| void dev_reset(usb_dev_handle *handle, int argc, char** argv) { | ||||
|     unsigned char buffer[8]; | ||||
|     int nBytes; | ||||
|     if (argc != 2) { | ||||
|         usage(argv[0]); | ||||
|         exit(1); | ||||
|     } | ||||
|     nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, CMD_RESET, 0, 0, (char *) buffer, sizeof(buffer), 5000); | ||||
|     if (nBytes < 0) { | ||||
|         fprintf(stderr, "USB error: %s\n", usb_strerror()); | ||||
|         exit(1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Show a waveform. This will not send a command to the device, the waveform is | ||||
|  * only printed on the screen. | ||||
|  * \param handle Handle to talk to the device (not needed). | ||||
|  * \param argc Number of arguments. | ||||
|  * \param argv Arguments. | ||||
|  */ | ||||
| int dev_show(int argc, char **argv) { | ||||
|     if (argc != 3) { | ||||
|         usage(argv[0]); | ||||
|         exit(1); | ||||
|     } | ||||
|     int waveformId = atoi(argv[2]); | ||||
|     if ((waveformId < 0) || (waveformId > 38)) { | ||||
|         fprintf(stderr, "invalid waveformId: %d\n", waveformId); | ||||
|         exit(1); | ||||
|     } | ||||
|     int i, j; | ||||
|     int length = fade_calculateWaveform(waveformId, 0); | ||||
|     printf("wave %2d - length %2d\n", waveformId, length); | ||||
|     for (i = 31; i > 0; i--) { | ||||
|         printf("%2d: ", i); | ||||
|         for (j = 1; j <= length; j++) { | ||||
|             if (fade_calculateWaveform(waveformId, j) >= i) { | ||||
|                 printf("*"); | ||||
|             } else { | ||||
|                 printf(" "); | ||||
|             } | ||||
|         } | ||||
|         printf("\n"); | ||||
|     } | ||||
|     printf("    "); | ||||
|     for (j = 1; j <= length; j++) { | ||||
|         printf("="); | ||||
|     } | ||||
|     printf("\n"); | ||||
|     exit(0); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Main function. Initializes the USB-device, parses commandline-parameters and | ||||
|  * calls the functions that communicate with the device. | ||||
|  * \param argc Number of arguments. | ||||
|  * \param argv Arguments. | ||||
|  * \return Error code. | ||||
|  */ | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
|     usb_dev_handle *handle = NULL; | ||||
|  | ||||
|     if (argc < 2) { | ||||
|         usage(argv[0]); | ||||
|         exit(1); | ||||
|     } | ||||
|     usb_init(); | ||||
|     if (usbOpenDevice (&handle, USBDEV_SHARED_VENDOR, "www.schatenseite.de", USBDEV_SHARED_PRODUCT, "USB-LED-Fader") != 0) { | ||||
|         fprintf(stderr, "Could not find USB device \"USB-LED-Fader\" with vid=0x%x pid=0x%x\n", USBDEV_SHARED_VENDOR, USBDEV_SHARED_PRODUCT); | ||||
|         exit(1); | ||||
|     } | ||||
|     /* We have searched all devices on all busses for our USB device above. Now | ||||
|      * try to open it and perform the vendor specific control operations for the | ||||
|      * function requested by the user. | ||||
|      */ | ||||
|     if (strcmp(argv[1], "test") == 0) { | ||||
|         dev_test(handle, argc, argv); | ||||
|     } else if (strcmp(argv[1], "set") == 0) { | ||||
|         dev_set(handle, argc, argv); | ||||
|     } else if (strcmp(argv[1], "clear") == 0) { | ||||
|         dev_clear(handle, argc, argv); | ||||
|     } else if (strcmp(argv[1], "status") == 0) { | ||||
|         dev_status(handle, argc, argv); | ||||
|     } else if (strcmp(argv[1], "reset") == 0) { | ||||
|         dev_reset(handle, argc, argv); | ||||
|     } else if (strcmp(argv[1], "show") == 0) { | ||||
|         dev_reset(handle, argc, argv); | ||||
|     } else { | ||||
|         usage(argv[0]); | ||||
|         exit(1); | ||||
|     } | ||||
|     usb_close(handle); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
							
								
								
									
										16
									
								
								common/channels.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								common/channels.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| #ifndef __channels_h_included__ | ||||
| #define __channels_h_included__ | ||||
|  | ||||
| /** | ||||
|  * \file channels.h | ||||
|  * \brief Global definitions, used by the firmware and the commandline-client. | ||||
|  * \author Thomas Stegemann | ||||
|  * \version $Id: channels.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  */ | ||||
|  | ||||
| #define CHANNELS 4 /**< number of output channels */ | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										466
									
								
								common/usbledfader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										466
									
								
								common/usbledfader.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,466 @@ | ||||
| #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 | ||||
|  | ||||
							
								
								
									
										48
									
								
								firmware/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								firmware/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| # $Id: Makefile,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  | ||||
| AVRDUDE = avrdude -p atmega8 -P /dev/parport0 -c stk200 | ||||
|  | ||||
| COMPILE = avr-gcc -Wall -Os -Iusbdrv -I../common -I. -mmcu=atmega8 #-DDEBUG_LEVEL=2 | ||||
| # NEVER compile the final product with debugging! Any debug output will | ||||
| # distort timing so that the specs can't be met. | ||||
|  | ||||
| OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o pwm_timer.o pwm_channels.o message_queue.o | ||||
| # Note that we link usbdrv.o first! This is required for correct alignment of | ||||
| # driver-internal global variables! | ||||
|  | ||||
|  | ||||
| # symbolic targets: | ||||
| all:	main.hex | ||||
|  | ||||
| .c.o: | ||||
| 	$(COMPILE) -c $< -o $@ | ||||
|  | ||||
| .S.o: | ||||
| 	$(COMPILE) -x assembler-with-cpp -c $< -o $@ | ||||
| # "-x assembler-with-cpp" should not be necessary since this is the default | ||||
| # file type for the .S (with capital S) extension. However, upper case | ||||
| # characters are not always preserved on Windows. To ensure WinAVR | ||||
| # compatibility define the file type manually. | ||||
|  | ||||
| .c.s: | ||||
| 	$(COMPILE) -S $< -o $@ | ||||
|  | ||||
| program:	all | ||||
| 	$(AVRDUDE) -E noreset,vcc -U flash:w:main.hex | ||||
|  | ||||
| clean: | ||||
| 	rm -f main.hex main.lst main.obj main.cof main.list main.map main.eep.hex main.bin *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s | ||||
|  | ||||
| # file targets: | ||||
| main.bin:	$(OBJECTS) | ||||
| 	$(COMPILE) -o main.bin $(OBJECTS) | ||||
|  | ||||
| main.hex:	main.bin | ||||
| 	rm -f main.hex main.eep.hex | ||||
| 	avr-objcopy -j .text -j .data -O ihex main.bin main.hex | ||||
|  | ||||
| disasm:	main.bin | ||||
| 	avr-objdump -d main.bin | ||||
|  | ||||
| cpp: | ||||
| 	$(COMPILE) -E main.c | ||||
							
								
								
									
										34
									
								
								firmware/boolean.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								firmware/boolean.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| #ifndef boolean_h | ||||
| #define boolean_h | ||||
|  | ||||
| /** | ||||
|  * \file boolean.h | ||||
|  * \brief Provides boolean variables in C. | ||||
|  * \author Thomas Stegemann | ||||
|  * \version $Id: boolean.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  */ | ||||
|  | ||||
| /** Possible boolean values */ | ||||
| typedef enum E_Boolean { | ||||
|     False = 0, /**< logical false */ | ||||
|     True = 1 /**< logical true */ | ||||
| } Boolean; | ||||
|  | ||||
| /** | ||||
|  * Boolean function. Returns true or false, depending on the given condition. | ||||
|  * \param condition The condition to evaluate, must be integer. | ||||
|  * \return True or false. | ||||
|  */ | ||||
| static inline Boolean | ||||
| boolean (int condition) | ||||
| { | ||||
|     if (condition) { | ||||
|         return True; | ||||
|     } else { | ||||
|         return False; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										26
									
								
								firmware/config_message_queue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								firmware/config_message_queue.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| #ifndef config_message_queue_h | ||||
| #define config_message_queue_h | ||||
|  | ||||
| /** | ||||
|  * \file config_message_queue.h | ||||
|  * \brief Configures the message-queue. | ||||
|  * \author Thomas Stegemann | ||||
|  * \version $Id: config_message_queue.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  * | ||||
|  * - define the size of the messageQueue(messageQueue_Size) and the type of the | ||||
|  *   messageQueue_QueuedMessage | ||||
|  * - check that messageQueue_SizeType can hold 0..messageQueue_Size+1 | ||||
|  * - the messageQueue buffers up to messageQueue_Size messages of the type | ||||
|  *   messageQueue_QueuedMessage | ||||
|  * - currently the messageQueue is used by pwm_Channels and pwm_Timer with the | ||||
|  *   pwm_Channels_Message | ||||
|  */ | ||||
|  | ||||
| #include "pwm_timer.h" | ||||
|  | ||||
| typedef pwm_Channels_Message messageQueue_QueuedMessage; | ||||
| enum { messageQueue_Size = 3 }; | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										23
									
								
								firmware/config_message_queue_impl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								firmware/config_message_queue_impl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| #ifndef config_message_queue_impl_h | ||||
| #define config_message_queue_impl_h | ||||
|  | ||||
| /** | ||||
|  * \file config_message_queue_impl.h | ||||
|  * \brief Configures the implementation of the message-queue. | ||||
|  * \author Thomas Stegemann | ||||
|  * \version $Id: config_message_queue_impl.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  * | ||||
|  * - define the SizeType for the messageQueue | ||||
|  * - the messageQueue_SizeType must hold 0..messageQueue_Size + 1, see | ||||
|  *   config_message_queue.h | ||||
|  * - the messageQueue_SizeType must be read/written by the processor in an | ||||
|  *   atomic instruction | ||||
|  */ | ||||
|  | ||||
| #include <stdint.h> | ||||
|  | ||||
| typedef uint8_t messageQueue_SizeType; | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										27
									
								
								firmware/config_pwm_timer_impl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								firmware/config_pwm_timer_impl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #ifndef config_pwm_timer_impl_h | ||||
| #define config_pwm_timer_impl_h | ||||
|  | ||||
| /** | ||||
|  * \file config_pwm_timer_impl.h | ||||
|  * \brief Configures the implementation of the PWM-timer. | ||||
|  * \author Thomas Stegemann | ||||
|  * \version $Id: config_pwm_timer_impl.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  * | ||||
|  * - pwm_Timer_Cycles_Max defines the number of (prescaled) processor cycles | ||||
|  *   for a full pwm_TimerCycle | ||||
|  * - pwm_Timer_Cycles_ReadMin defines the number of (prescaled) processor | ||||
|  *   cycles the reading from the message queue may last | ||||
|  * - pwm_Timer_Cycles_SleepMax defines the minimum number of (prescaled) | ||||
|  *   processor cycles for which the timer is used. for less cycles the | ||||
|  *   pwm_Timer waits active | ||||
|  */ | ||||
|  | ||||
| #include "pwm_channels.h" | ||||
|  | ||||
| enum { pwm_Timer_Cycles_Max = pwm_Channels_Brightness_Max * pwm_Channels_Brightness_Max }; | ||||
| enum { pwm_Timer_Cycles_ReadMin = 2 }; | ||||
| enum { pwm_Timer_Cycles_SleepMax = 2 }; | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										278
									
								
								firmware/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								firmware/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,278 @@ | ||||
| /** | ||||
|  * \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; | ||||
| } | ||||
							
								
								
									
										95
									
								
								firmware/message_queue.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								firmware/message_queue.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| /** | ||||
|  * \file message_queue.c | ||||
|  * \brief A message queue used to exchange messages between two concurrent threads. | ||||
|  * \author Thomas Stegemann | ||||
|  * \version $Id: message_queue.c,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  */ | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include "message_queue.h" | ||||
| #include "config_message_queue_impl.h" | ||||
|  | ||||
| /** Structure of the global data of the queue */ | ||||
| typedef struct S_messageQueue_GlobalData { | ||||
|     messageQueue_QueuedMessage queue[messageQueue_Size]; /**< the queue itself */ | ||||
|     messageQueue_SizeType begin; /**< the current start of the queue */ | ||||
|     messageQueue_SizeType end; /**< the current end of the queue */ | ||||
| } messageQueue_GlobalData; | ||||
|  | ||||
| /** Global data of the queue */ | ||||
| static volatile messageQueue_GlobalData m_data; | ||||
|  | ||||
| /** | ||||
|  * Get the next entry fron the queue. | ||||
|  * \param value Number of the current entry. | ||||
|  * \return 0 if the value is larger than the queue, otherwise the next entry. | ||||
|  */ | ||||
| static inline messageQueue_SizeType messageQueue_next(messageQueue_SizeType value) { | ||||
|     value++; | ||||
|     if(value >= messageQueue_Size) { | ||||
|         value= 0; | ||||
|     } | ||||
|     return value; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Initialize the queue. | ||||
|  */ | ||||
| void messageQueue_init(void) { | ||||
|     m_data.begin= 0; | ||||
|     m_data.end= 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Clean up the queue. Currently this does nothing. | ||||
|  */ | ||||
| void messageQueue_cleanup(void) | ||||
| {} | ||||
|  | ||||
| /** | ||||
|  * Test if the queue is empty. | ||||
|  * \return True if it is empty, otherwise false. | ||||
|  */ | ||||
| Boolean messageQueue_isEmpty(void) { | ||||
|     return boolean(m_data.begin == m_data.end); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Test if the queue is full. If it is full, new entries will overwrite the | ||||
|  * first entries. | ||||
|  * \return True if it is full, otherwise false. | ||||
|  */ | ||||
| Boolean messageQueue_isFull(void) { | ||||
|     return boolean(messageQueue_next(m_data.end) == m_data.begin); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Read a message from the queue. | ||||
|  * \param pMessage Pointer to a message variable that should be set to the | ||||
|  * message. | ||||
|  * \return True if an entry could be read, otherwise false. | ||||
|  */ | ||||
| Boolean messageQueue_read(messageQueue_QueuedMessage* pMessage) { | ||||
|     Boolean success= !messageQueue_isEmpty(); | ||||
|     if(success) { | ||||
|         *pMessage= m_data.queue[m_data.begin]; | ||||
|         m_data.begin= messageQueue_next(m_data.begin); | ||||
|     } | ||||
|     return success; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Write a message to the queue. | ||||
|  * \param message The message to append. | ||||
|  * \return True if the message could be appended, otherwise false. | ||||
|  */ | ||||
| Boolean messageQueue_write(messageQueue_QueuedMessage message) { | ||||
|     Boolean success= !messageQueue_isFull(); | ||||
|     if(success) { | ||||
|         m_data.queue[m_data.end]= message; | ||||
|         m_data.end= messageQueue_next(m_data.end); | ||||
|     } | ||||
|     return success; | ||||
| } | ||||
							
								
								
									
										37
									
								
								firmware/message_queue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								firmware/message_queue.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| #ifndef message_queue_h | ||||
| #define message_queue_h | ||||
|  | ||||
| /** | ||||
|  * \file message_queue.h | ||||
|  * \brief A message queue used to exchange messages between two concurrent | ||||
|  * threads. | ||||
|  * \author Thomas Stegemann | ||||
|  * \version $Id: message_queue.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  * | ||||
|  * - exchange messages between two concurrent threads (e.g.: main thread and | ||||
|  *   interrupt calls) | ||||
|  * - before using any other function of the messageQueue, init must be called | ||||
|  * - one thread must be data source (use isFull and write) | ||||
|  * - the other thread must be the data sink (use isEmpty and read) | ||||
|  * - two concurrent threads must not use both the write functions and two | ||||
|  *   concurrent threads must not use both the read functions | ||||
|  * - read/write return True on success and False if the message could not be | ||||
|  *   read/written because the queue is empty/full | ||||
|  * - the size of the messageQueue and the type of the | ||||
|  *   messageQueue_QueuedMessage are defined in config_message_queue.h | ||||
|  * - only one messageQueue can be used in a project | ||||
|  */ | ||||
|  | ||||
| #include "boolean.h" | ||||
| #include "config_message_queue.h" | ||||
|  | ||||
| void    messageQueue_init   (void); | ||||
| void    messageQueue_cleanup(void); | ||||
| Boolean messageQueue_isEmpty(void); | ||||
| Boolean messageQueue_isFull (void); | ||||
| Boolean messageQueue_read   (messageQueue_QueuedMessage* pMessage); | ||||
| Boolean messageQueue_write  (messageQueue_QueuedMessage message); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										108
									
								
								firmware/pwm_channels.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								firmware/pwm_channels.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| /** | ||||
|  * \file pwm_channels.c | ||||
|  * \brief Manages the values of the displayed channels. | ||||
|  * \author Thomas Stegemann | ||||
|  * \version $Id: pwm_channels.c,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "pwm_channels.h" | ||||
| #include "pwm_timer.h" | ||||
| #include "config_pwm_timer_impl.h" | ||||
| #include "message_queue.h" | ||||
|  | ||||
| /** Structure to contain the state of one channel */ | ||||
| typedef struct S_pwm_Channels_ChannelBrightness { | ||||
|     pwm_Channels_Bitfield field; /**< Bitfield resembling one channel */ | ||||
|     pwm_Timer_Cycles      cycle; /**< Number of on-cycles */ | ||||
| } pwm_Channels_ChannelBrightness; | ||||
|  | ||||
| /** | ||||
|  * Initialize channels. Basically, only the PWM-timer is started. | ||||
|  */ | ||||
| void pwm_Channels_init(void) { | ||||
|     pwm_Timer_init(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Clean up channels. Basically, the PWM-timer gets cleaned. | ||||
|  */ | ||||
| void pwm_Channels_cleanup(void) { | ||||
|     pwm_Timer_cleanup(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Calculate the Channels_Message. Requires the channel-list to be sorted by | ||||
|  * cycles. | ||||
|  * \param channels Array of the channels. | ||||
|  * \return Current message. | ||||
|  */ | ||||
| static pwm_Channels_Message pwm_Channels_Message_get(pwm_Channels_ChannelBrightness channels[CHANNELS]) { | ||||
|     int j; | ||||
|     pwm_Channels_StepCounter i= 0; | ||||
|     pwm_Channels_Message message; | ||||
|     message.step[i].field = 0; | ||||
|     for (j = 0; j < CHANNELS; j++) { | ||||
|         message.step[i].field |= channels[j].field; | ||||
|     } | ||||
|     message.step[i].cycle= 0; | ||||
|  | ||||
|     for (j = 0; j < CHANNELS; j++) { | ||||
|         if(channels[j].cycle == message.step[i].cycle) { | ||||
|             message.step[i].field&= ~channels[j].field; | ||||
|         } else { | ||||
|             message.step[i].cycle= channels[j].cycle; | ||||
|             i++; | ||||
|             message.step[i]= message.step[i-1]; | ||||
|             message.step[i].field&= ~channels[j].field; | ||||
|         } | ||||
|     } | ||||
|     message.step[i].cycle= pwm_Timer_Cycles_Max; | ||||
|     return message; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Calculate number of cycles from a brightness. | ||||
|  * \param brightness The brightness. | ||||
|  * \return The number of cycles. | ||||
|  */ | ||||
| pwm_Timer_Cycles pwm_Channels_BrightnessToCycles(pwm_Channels_Brightness brightness) { | ||||
|     return brightness * brightness; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Compare the number of cycles in two channels. This is needed for the | ||||
|  * qsort-call in pwm_Channels_show(). | ||||
|  * \param cmp1 First channel. | ||||
|  * \param cmp2 Second channel. | ||||
|  * \return A value <0 if cmp1 is smaller than cmp2, 0 if they are of the same | ||||
|  * length and a value >0 if cmp1 is larger than cmp2. | ||||
|  */ | ||||
| int pwm_Channels_CompareChannels(const void * cmp1, const void * cmp2) { | ||||
|     return ((const pwm_Channels_ChannelBrightness*)cmp1)->cycle - ((const pwm_Channels_ChannelBrightness*)cmp2)->cycle; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Writes the current pattern to the message-queue. The pattern is built from | ||||
|  * the state of all channels. | ||||
|  * \param channels Array with the channel-states. | ||||
|  */ | ||||
| void pwm_Channels_show(pwm_Channels channels) { | ||||
|     int i; | ||||
|     pwm_Channels_Message message; | ||||
|     pwm_Channels_ChannelBrightness channel_brightness[CHANNELS]; | ||||
|     for (i = 0; i < CHANNELS; i++) { | ||||
|         channel_brightness[i].field = 1 << i; // 1 << i equals 2^i | ||||
|         channel_brightness[i].cycle = pwm_Channels_BrightnessToCycles(channels.channel[i]); | ||||
|     } | ||||
|  | ||||
|     qsort(channel_brightness, CHANNELS, sizeof(pwm_Channels_ChannelBrightness), pwm_Channels_CompareChannels); | ||||
|     message= pwm_Channels_Message_get(channel_brightness); | ||||
|     while(!messageQueue_write(message)) { | ||||
|         pwm_Timer_idle(); | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										39
									
								
								firmware/pwm_channels.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								firmware/pwm_channels.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| #ifndef pwm_Channels_h | ||||
| #define pwm_Channels_h | ||||
|  | ||||
| /** | ||||
|  * \file pwm_channels.h | ||||
|  * \brief Manages the values of the displayed channels. | ||||
|  * \author Thomas Stegemann | ||||
|  * \version $Id: pwm_channels.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  * | ||||
|  * - display the specified channels for a cycle of pwm_timer | ||||
|  * - before using the function show, init must be called | ||||
|  * - for every cycle of pwm_timer, show must be called | ||||
|  * - show buffers the selected channels, so it returns immediatly, as long as | ||||
|  *   the internal buffer is not full | ||||
|  * - when the buffer is full the function blocks until another pwm_timer cycle | ||||
|  *   has processed the current channels | ||||
|  */ | ||||
| #include <stdint.h> | ||||
| #include "channels.h" | ||||
|  | ||||
| /** Type to contain the brightness of one channel. */ | ||||
| typedef uint8_t pwm_Channels_Brightness; | ||||
|  | ||||
| /** Definition of the maximum brightness. */ | ||||
| enum { pwm_Channels_Brightness_Max = 31 }; | ||||
|  | ||||
| /** Structure to contain the state of several channels. */ | ||||
| typedef struct S_pwm_Channels { | ||||
|   pwm_Channels_Brightness channel[CHANNELS]; /**< Array of channels. */ | ||||
| } pwm_Channels; | ||||
|  | ||||
| void pwm_Channels_init(void); | ||||
| void pwm_Channels_cleanup(void); | ||||
| void pwm_Channels_show(pwm_Channels channels); | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										139
									
								
								firmware/pwm_timer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								firmware/pwm_timer.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | ||||
| /** | ||||
|  * \file pwm_timer.c | ||||
|  * \brief Controls the actual PWM-output. | ||||
|  * \author Thomas Stegemann | ||||
|  * \version $Id: pwm_timer.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 "boolean.h" | ||||
| #include "message_queue.h" | ||||
| #include "pwm_timer.h" | ||||
| #include "config_pwm_timer_impl.h" | ||||
|  | ||||
| /** Structure to contain the global data for the timer. */ | ||||
| typedef struct S_pwm_Timer_GlobalData { | ||||
|     pwm_Channels_Message     message[2]; /**< Array of two messages */ | ||||
|     pwm_Channels_Message*    pActive; /**< Pointer to the active message */ | ||||
|     pwm_Channels_Message*    pRead; /**< Pointer to the message to read */ | ||||
|     pwm_Channels_StepCounter step; /**< Current step in the cycle */ | ||||
|     pwm_Timer_Cycles         currentCycle; /**< Current cycle */ | ||||
|     Boolean                  readDone; /**< Indicates if something is read from the queue */ | ||||
| } pwm_Timer_GlobalData; | ||||
|  | ||||
| static pwm_Timer_GlobalData m_data; /**< Global data for the timer. */ | ||||
|  | ||||
| /** | ||||
|  * Initialize the PWM-Timer. Sets basic values, starts the timer and | ||||
|  * initializes output-pins. | ||||
|  */ | ||||
| void pwm_Timer_init(void) { | ||||
|     messageQueue_init(); | ||||
|     m_data.step=           0; | ||||
|     m_data.currentCycle=   0; | ||||
|     m_data.pActive=        &m_data.message[0]; | ||||
|     m_data.pRead=          &m_data.message[1]; | ||||
|     m_data.readDone=       False; | ||||
|     m_data.pActive->step[0].cycle= pwm_Channels_Brightness_Max; | ||||
|     m_data.pActive->step[0].field=      0; | ||||
|     /* clk/64 prescaling, CTC mode */ | ||||
|     /* enable timer1 overflow (=output compare 1a) */ | ||||
|     TCCR1B= _BV(CS11) | _BV(CS10) | _BV(WGM12); | ||||
|     TCCR1A= 0; | ||||
|     TIMSK|= _BV(OCIE1A); | ||||
|     /* load initial delay */ | ||||
|     OCR1A=  pwm_Timer_Cycles_Max; | ||||
|     /* initialize output pin */ | ||||
|     DDRC = (1 << CHANNELS) - 1; // set all used channel-pins to output | ||||
|     PORTC = 0; | ||||
|     sei(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Clean up the timer. Basically, the message-queue is cleaned. | ||||
|  */ | ||||
| void pwm_Timer_cleanup(void) { | ||||
|     messageQueue_cleanup(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Do nothing. | ||||
|  */ | ||||
| void pwm_Timer_idle(void) | ||||
| {} | ||||
|  | ||||
| /** | ||||
|  * Sleeps the required number of cycles. There are two possible ways of | ||||
|  * sleeping: 'active' and 'passive'. If we are required to sleep less than the | ||||
|  * number of cycles defined in pwm_Timer_Cycles_SleepMax, we execute an empty | ||||
|  * loop until we are ready (active sleeping). Otherwise, we set the timer to | ||||
|  * wake us after the given number of cycles (passive sleeping). | ||||
|  * \param sleep Number of cycles. | ||||
|  * \return True if we slept 'actively' (doing the while-loop), otherwise false. | ||||
|  */ | ||||
| static Boolean pwm_Timer_sleep(pwm_Timer_Cycles sleep) { | ||||
|     Boolean sleepDone= False; | ||||
|     if((sleep < pwm_Timer_Cycles_SleepMax)) { | ||||
|         while (TCNT1 < sleep) | ||||
|         {} | ||||
|         sleepDone= True; | ||||
|     } else { | ||||
|         OCR1A= sleep; | ||||
|     } | ||||
|     return sleepDone; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Switch the output-pins to the given pattern. | ||||
|  * \param field 8-bit output-pattern. | ||||
|  */ | ||||
| static void pwm_Timer_switchLed(pwm_Channels_Bitfield field) { | ||||
|     PORTC= field; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Timer interrupt routine. Determines the pattern to set and handles the times | ||||
|  * to do PWM. | ||||
|  */ | ||||
| SIGNAL(SIG_OUTPUT_COMPARE1A) { | ||||
|     pwm_Timer_Cycles sleep= pwm_Timer_Cycles_Max; | ||||
|     OCR1A= pwm_Timer_Cycles_Max; | ||||
|     sei(); | ||||
|     do { | ||||
|         if((m_data.step == pwm_Channels_StepCounter_Max) || (m_data.currentCycle == pwm_Timer_Cycles_Max)) { | ||||
|             if(m_data.readDone) { | ||||
|                 pwm_Channels_Message* pSwap= m_data.pActive; | ||||
|                 m_data.pActive= m_data.pRead; | ||||
|                 m_data.pRead=   pSwap; | ||||
|                 m_data.readDone= False; | ||||
|                 m_data.currentCycle= 0; | ||||
|                 m_data.step= 0; | ||||
|                 sleep= 0; | ||||
|             } else { | ||||
|                 /* error could not read a new channels message in a whole cycle */ | ||||
|                 /* wait a complete cycle for the next message */ | ||||
|                 //sleep= pwm_Timer_Cycles_Max; | ||||
|                 m_data.currentCycle= 0; | ||||
|                 m_data.step= 0; | ||||
|                 sleep= 0; | ||||
|             } | ||||
|         } else { | ||||
|             pwm_Timer_switchLed(m_data.pActive->step[m_data.step].field); | ||||
|             sleep= m_data.pActive->step[m_data.step].cycle - m_data.currentCycle; | ||||
|             m_data.currentCycle= m_data.pActive->step[m_data.step].cycle; | ||||
|             m_data.step++; | ||||
|         } | ||||
|     } while(pwm_Timer_sleep(sleep)); | ||||
|  | ||||
|     if(!m_data.readDone && (sleep > pwm_Timer_Cycles_ReadMin)) { | ||||
|         if(messageQueue_read(m_data.pRead)) { | ||||
|             m_data.readDone= True; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										70
									
								
								firmware/pwm_timer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								firmware/pwm_timer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| #ifndef pwm_timer_h | ||||
| #define pwm_timer_h | ||||
|  | ||||
| /** | ||||
|  * \file pwm_timer.h | ||||
|  * \brief Controls the actual PWM-output. | ||||
|  * \author Thomas Stegemann | ||||
|  * \version $Id: pwm_timer.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  * | ||||
|  * - read and process the pwm_Channels_Message from the messageQueue (written | ||||
|  *   by pwm_Channels) | ||||
|  * - use a timed interrupt to switch the led at a specified processor cycle | ||||
|  * - init starts the processing and the timer | ||||
|  * - idle is called by the pwm_Channels when the internal buffer is full | ||||
|  * - at every pwm_timer cycle the leds can be switched in up to four steps | ||||
|  *   every step defines which leds are switched on/off and up to which | ||||
|  *   processor cycle the status is hold so the brightness for the three leds | ||||
|  *   can be switched independently | ||||
|  * - example: | ||||
|  *   - start with all leds for 10 cycles: | ||||
|  *     \code | ||||
|  *     step[0]= {10, 1|2|4}; | ||||
|  *     \endcode | ||||
|  *   - switch off the red led for further 10 cycles | ||||
|  *     \code | ||||
|  *     step[1]= {20,   2|4}; | ||||
|  *     \endcode | ||||
|  *   - switch off the green led for further 10 cycles | ||||
|  *     \code | ||||
|  *     step[2]= {30,     4}; | ||||
|  *     \endcode | ||||
|  *   - switch off all leds for the remaining time | ||||
|  *     \code | ||||
|  *     step[3]= {pwm_Timer_Cycles_Max, 0}; | ||||
|  *     \endcode | ||||
|  */ | ||||
|  | ||||
| #include "pwm_channels.h" | ||||
|  | ||||
| /** 8-bit-field to contain the state of the channels. */ | ||||
| typedef uint8_t pwm_Channels_Bitfield; | ||||
|  | ||||
| /** Value to count the steps in one channel. */ | ||||
| typedef uint8_t pwm_Channels_StepCounter; | ||||
|  | ||||
| /** Contains a number of controller-cycles. */ | ||||
| typedef uint16_t pwm_Timer_Cycles; | ||||
|  | ||||
| /** Definition of the maximum number of steps. */ | ||||
| enum{pwm_Channels_StepCounter_Max= CHANNELS + 1}; | ||||
|  | ||||
| /** Structure to contain one step. */ | ||||
| typedef struct S_pwm_Channels_Step { | ||||
|     pwm_Timer_Cycles cycle; /**< Number of cycles to complete this step. */ | ||||
|     pwm_Channels_Bitfield field; /**< The state of all channels. */ | ||||
| } pwm_Channels_Step; | ||||
|  | ||||
| /** Structure to contain an array of steps. */ | ||||
| typedef struct S_pwm_Channels_Message { | ||||
|     pwm_Channels_Step step[pwm_Channels_StepCounter_Max]; /**< Array of steps. */ | ||||
| } pwm_Channels_Message; | ||||
|  | ||||
| void pwm_Timer_init(void); | ||||
| void pwm_Timer_cleanup(void); | ||||
| void pwm_Timer_idle(void); | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										170
									
								
								firmware/usbconfig.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								firmware/usbconfig.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,170 @@ | ||||
| /* Name: usbconfig.h | ||||
|  * Project: AVR USB driver | ||||
|  * Author: Christian Starkjohann | ||||
|  * Creation Date: 2005-04-01 | ||||
|  * Tabsize: 4 | ||||
|  * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | ||||
|  * License: Proprietary, free under certain conditions. See Documentation. | ||||
|  * This Revision: $Id: usbconfig.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  */ | ||||
|  | ||||
| #ifndef __usbconfig_h_included__ | ||||
| #define __usbconfig_h_included__ | ||||
|  | ||||
| /** | ||||
|  * \file usbconfig.h | ||||
|  * \brief Configuration of the USB-driver. | ||||
|  * \version $Id: usbconfig.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  */ | ||||
|  | ||||
|  | ||||
| /* | ||||
| General Description: | ||||
| This file contains parts of the USB driver which can be configured and can or | ||||
| must be adapted to your hardware. | ||||
|  | ||||
| Please note that the usbdrv contains a usbconfig-prototype.h file now. We | ||||
| recommend that you use that file as a template because it will always list | ||||
| the newest features and options. | ||||
| */ | ||||
|  | ||||
| /* ---------------------------- Hardware Config ---------------------------- */ | ||||
|  | ||||
| #define USB_CFG_IOPORTNAME      B | ||||
| /* This is the port where the USB bus is connected. When you configure it to | ||||
|  * "PORTB", the registers PORTB, PINB (=PORTB-2) and DDRB (=PORTB-1) will be | ||||
|  * used. | ||||
|  */ | ||||
| #define USB_CFG_DMINUS_BIT      0 | ||||
| /* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. | ||||
|  * This MUST be bit 0 or 7. All other values will result in a compile error! | ||||
|  */ | ||||
| #define USB_CFG_DPLUS_BIT       1 | ||||
| /* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. | ||||
|  * This may be any bit in the port. Please note that D+ must also be connected | ||||
|  * to interrupt pin INT0! | ||||
|  */ | ||||
|  | ||||
| /* #define USB_CFG_PULLUP_IOPORTNAME   B */ | ||||
| /* This is the port where the USB D- pullup resistor is connected. When you | ||||
|  * configure it to "PORTB", the registers PORTB and DDRB (=PORTB-1) will be | ||||
|  * used. If this constant is defined, the macros usbDeviceConnect() and | ||||
|  * usbDeviceDisconnect will be available. | ||||
|  */ | ||||
| /* #define USB_CFG_PULLUP_BIT          2 */ | ||||
| /* This is the bit number in USB_CFG_PULLUP_IOPORT where the USB D- 1.5 kOhm | ||||
|  * pullup resistor is connected instead of VBUS. This may be any bit in | ||||
|  * the port. | ||||
|  */ | ||||
|  | ||||
| /* --------------------------- Functional Range ---------------------------- */ | ||||
|  | ||||
| #define USB_CFG_HAVE_INTRIN_ENDPOINT    0 | ||||
| /* Define this to 1 if you want to compile a version with two endpoints: The | ||||
|  * default control endpoint 0 and an interrupt-in endpoint 1. | ||||
|  */ | ||||
| #define USB_CFG_IMPLEMENT_HALT          0 | ||||
| /* Define this to 1 if you also want to implement the ENDPOINT_HALT feature | ||||
|  * for endpoint 1 (interrupt endpoint). Although you may not need this feature, | ||||
|  * it is required by the standard. We have made it a config option because it | ||||
|  * bloats the code considerably. | ||||
|  */ | ||||
| #define USB_CFG_INTR_POLL_INTERVAL      10 | ||||
| /* If you compile a version with endpoint 1 (interrupt-in), this is the poll | ||||
|  * interval. The value is in milliseconds and must not be less than 10 ms for | ||||
|  * low speed devices. | ||||
|  */ | ||||
| #define USB_CFG_IS_SELF_POWERED         1 | ||||
| /* Define this to 1 if the device has its own power supply. Set it to 0 if the | ||||
|  * device is powered from the USB bus. | ||||
|  */ | ||||
| #define USB_CFG_MAX_BUS_POWER           20 | ||||
| /* Set this variable to the maximum USB bus power consumption of your device. | ||||
|  * The value is in milliamperes. [It will be divided by two since USB | ||||
|  * communicates power requirements in units of 2 mA.] | ||||
|  */ | ||||
| #define USB_CFG_SAMPLE_EXACT            0 | ||||
| /* This variable affects Sampling Jitter for USB receiving. When it is 0, the | ||||
|  * driver guarantees a sampling window of 1/2 bit. The USB spec requires | ||||
|  * that the receiver has at most 1/4 bit sampling window. The 1/2 bit window | ||||
|  * should still work reliably enough because we work at low speed. If you want | ||||
|  * to meet the spec, set this value to 1. This will unroll a loop which | ||||
|  * results in bigger code size. | ||||
|  * If you have problems with long cables, try setting this value to 1. | ||||
|  */ | ||||
| #define USB_CFG_IMPLEMENT_FN_WRITE      1 | ||||
| /* Set this to 1 if you want usbFunctionWrite() to be called for control-out | ||||
|  * transfers. Set it to 0 if you don't need it and want to save a couple of | ||||
|  * bytes. | ||||
|  */ | ||||
| #define USB_CFG_IMPLEMENT_FN_READ       1 | ||||
| /* Set this to 1 if you need to send control replies which are generated | ||||
|  * "on the fly" when usbFunctionRead() is called. If you only want to send | ||||
|  * data from a static buffer, set it to 0 and return the data from | ||||
|  * usbFunctionSetup(). This saves a couple of bytes. | ||||
|  */ | ||||
|  | ||||
| /* -------------------------- Device Description --------------------------- */ | ||||
|  | ||||
| #define  USB_CFG_VENDOR_ID  0xc0, 0x16  /* 5824 in dec, stands for VOTI */ | ||||
| /* USB vendor ID for the device, low byte first. If you have registered your | ||||
|  * own Vendor ID, define it here. Otherwise you use obdev's free shared | ||||
|  * VID/PID pair. Be sure to read USBID-License.txt for rules! | ||||
|  */ | ||||
| #define USB_CFG_DEVICE_ID   0xdc, 0x05  /* 1500 in dec, obdev's free PID */ | ||||
| /* This is the ID of the product, low byte first. It is interpreted in the | ||||
|  * scope of the vendor ID. If you have registered your own VID with usb.org | ||||
|  * or if you have licensed a PID from somebody else, define it here. Otherwise | ||||
|  * you use obdev's free shared VID/PID pair. Be sure to read the rules in | ||||
|  * USBID-License.txt! | ||||
|  */ | ||||
| #define USB_CFG_DEVICE_VERSION  0x00, 0x01 | ||||
| /* Version number of the device: Minor number first, then major number. | ||||
|  */ | ||||
| #define USB_CFG_VENDOR_NAME     'w', 'w', 'w', '.', 's', 'c', 'h', 'a', 't', 'e', 'n', 's', 'e', 'i', 't', 'e', '.', 'd', 'e' | ||||
| #define USB_CFG_VENDOR_NAME_LEN 19 | ||||
| /* These two values define the vendor name returned by the USB device. The name | ||||
|  * must be given as a list of characters under single quotes. The characters | ||||
|  * are interpreted as Unicode (UTF-16) entities. | ||||
|  * If you don't want a vendor name string, undefine these macros. | ||||
|  * ALWAYS define a vendor name containing your Internet domain name if you use | ||||
|  * obdev's free shared VID/PID pair. See the file USBID-License.txt for | ||||
|  * details. | ||||
|  */ | ||||
| #define USB_CFG_DEVICE_NAME     'U', 'S', 'B', '-', 'L', 'E', 'D', '-', 'F', 'a', 'd', 'e', 'r' | ||||
| #define USB_CFG_DEVICE_NAME_LEN 13 | ||||
| /* Same as above for the device name. If you don't want a device name, undefine | ||||
|  * the macros. See the file USBID-License.txt before you assign a name. | ||||
|  */ | ||||
| #define USB_CFG_SERIAL_NUMBER_LENGTH  0 | ||||
| /* Set this define to the number of charcters in the serial number if your | ||||
|  * device should have a serial number to uniquely identify each hardware | ||||
|  * instance. You must supply the serial number in a string descriptor with the | ||||
|  * name "usbCfgSerialNumberStringDescriptor", e.g.: | ||||
|  * #define USB_CFG_SERIAL_NUMBER_LENGTH  5 | ||||
|  * int usbCfgSerialNumberStringDescriptor[] PROGMEM = { | ||||
|  *     USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LENGTH), | ||||
|  *     '1', '2', '3', '4', '5' | ||||
|  * }; | ||||
|  * See usbdrv.h for more information about the USB_STRING_DESCRIPTOR_HEADER() | ||||
|  * macro or usbdrv.c for example string descriptors. | ||||
|  * You may want to put "usbCfgSerialNumberStringDescriptor" at a constant | ||||
|  * flash memory address (with magic linker commands) so that you don't need | ||||
|  * to recompile if you change it. | ||||
|  */ | ||||
| #define USB_CFG_DEVICE_CLASS    0xff | ||||
| #define USB_CFG_DEVICE_SUBCLASS 0 | ||||
| /* See USB specification if you want to conform to an existing device class. | ||||
|  */ | ||||
| #define USB_CFG_INTERFACE_CLASS     0 | ||||
| #define USB_CFG_INTERFACE_SUBCLASS  0 | ||||
| #define USB_CFG_INTERFACE_PROTOCOL  0 | ||||
| /* See USB specification if you want to conform to an existing device class or | ||||
|  * protocol. | ||||
|  */ | ||||
| #define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH    0   /* total length of report descriptor */ | ||||
| /* Define this to the length of the HID report descriptor, if you implement | ||||
|  * an HID device. Otherwise don't define it or define it to 0. | ||||
|  */ | ||||
|  | ||||
| #endif /* __usbconfig_h_included__ */ | ||||
							
								
								
									
										115
									
								
								firmware/usbdrv/Changelog.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								firmware/usbdrv/Changelog.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| This file documents changes in the firmware-only USB driver for atmel's AVR | ||||
| microcontrollers. New entries are always appended to the end of the file. | ||||
| Scroll down to the bottom to see the most recent changes. | ||||
|  | ||||
| 2005-04-01: | ||||
|   - Implemented endpoint 1 as interrupt-in endpoint. | ||||
|   - Moved all configuration options to usbconfig.h which is not part of the | ||||
|     driver. | ||||
|   - Changed interface for usbVendorSetup(). | ||||
|   - Fixed compatibility with ATMega8 device. | ||||
|   - Various minor optimizations. | ||||
|  | ||||
| 2005-04-11: | ||||
|   - Changed interface to application: Use usbFunctionSetup(), usbFunctionRead() | ||||
|     and usbFunctionWrite() now. Added configuration options to choose which | ||||
|     of these functions to compile in. | ||||
|   - Assembler module delivers receive data non-inverted now. | ||||
|   - Made register and bit names compatible with more AVR devices. | ||||
|  | ||||
| 2005-05-03: | ||||
|   - Allow address of usbRxBuf on any memory page as long as the buffer does | ||||
|     not cross 256 byte page boundaries. | ||||
|   - Better device compatibility: works with Mega88 now. | ||||
|   - Code optimization in debugging module. | ||||
|   - Documentation updates. | ||||
|  | ||||
| 2006-01-02: | ||||
|   - Added (free) default Vendor- and Product-IDs bought from voti.nl. | ||||
|   - Added USBID-License.txt file which defines the rules for using the free | ||||
|     shared VID/PID pair. | ||||
|   - Added Readme.txt to the usbdrv directory which clarifies administrative | ||||
|     issues. | ||||
|  | ||||
| 2006-01-25: | ||||
|   - Added "configured state" to become more standards compliant. | ||||
|   - Added "HALT" state for interrupt endpoint. | ||||
|   - Driver passes the "USB Command Verifier" test from usb.org now. | ||||
|   - Made "serial number" a configuration option. | ||||
|   - Minor optimizations, we now recommend compiler option "-Os" for best | ||||
|     results. | ||||
|   - Added a version number to usbdrv.h | ||||
|  | ||||
| 2006-02-03: | ||||
|   - New configuration variable USB_BUFFER_SECTION for the memory section where | ||||
|     the USB rx buffer will go. This defaults to ".bss" if not defined. Since | ||||
|     this buffer MUST NOT cross 256 byte pages (not even touch a page at the | ||||
|     end), the user may want to pass a linker option similar to | ||||
|     "-Wl,--section-start=.mybuffer=0x800060". | ||||
|   - Provide structure for usbRequest_t. | ||||
|   - New defines for USB constants. | ||||
|   - Prepared for HID implementations. | ||||
|   - Increased data size limit for interrupt transfers to 8 bytes. | ||||
|   - New macro usbInterruptIsReady() to query interrupt buffer state. | ||||
|  | ||||
| 2006-02-18: | ||||
|   - Ensure that the data token which is sent as an ack to an OUT transfer is | ||||
|     always zero sized. This fixes a bug where the host reports an error after | ||||
|     sending an out transfer to the device, although all data arrived at the | ||||
|     device. | ||||
|   - Updated docs in usbdrv.h to reflect changed API in usbFunctionWrite(). | ||||
|  | ||||
| * Release 2006-02-20 | ||||
|  | ||||
|   - Give a compiler warning when compiling with debugging turned on. | ||||
|   - Added Oleg Semyonov's changes for IAR-cc compatibility. | ||||
|   - Added new (optional) functions usbDeviceConnect() and usbDeviceDisconnect() | ||||
|     (also thanks to Oleg!). | ||||
|   - Rearranged tests in usbPoll() to save a couple of instructions in the most | ||||
|     likely case that no actions are pending. | ||||
|   - We need a delay between the SET ADDRESS request until the new address | ||||
|     becomes active. This delay was handled in usbPoll() until now. Since the | ||||
|     spec says that the delay must not exceed 2ms, previous versions required | ||||
|     aggressive polling during the enumeration phase. We have now moved the | ||||
|     handling of the delay into the interrupt routine. | ||||
|   - We must not reply with NAK to a SETUP transaction. We can only achieve this | ||||
|     by making sure that the rx buffer is empty when SETUP tokens are expected. | ||||
|     We therefore don't pass zero sized data packets from the status phase of | ||||
|     a transfer to usbPoll(). This change MAY cause troubles if you rely on | ||||
|     receiving a less than 8 bytes long packet in usbFunctionWrite() to | ||||
|     identify the end of a transfer. usbFunctionWrite() will NEVER be called | ||||
|     with a zero length. | ||||
|  | ||||
| * Release 2006-03-14 | ||||
|  | ||||
|   - Improved IAR C support: tiny memory model, more devices | ||||
|   - Added template usbconfig.h file under the name usbconfig-prototype.h | ||||
|  | ||||
| * Release 2006-03-26 | ||||
|  | ||||
|   - Added provision for one more interrupt-in endpoint (endpoint 3). | ||||
|   - Added provision for one interrupt-out endpoint (endpoint 1). | ||||
|   - Added flowcontrol macros for USB. | ||||
|   - Added provision for custom configuration descriptor. | ||||
|   - Allow ANY two port bits for D+ and D-. | ||||
|   - Merged (optional) receive endpoint number into global usbRxToken variable. | ||||
|   - Use USB_CFG_IOPORTNAME instead of USB_CFG_IOPORT. We now construct the | ||||
|     variable name from the single port letter instead of computing the address | ||||
|     of related ports from the output-port address. | ||||
|  | ||||
| * Release 2006-06-26 | ||||
|  | ||||
|   - Updated documentation in usbdrv.h and usbconfig-prototype.h to reflect the | ||||
|     new features. | ||||
|   - Removed "#warning" directives because IAR does not understand them. Use | ||||
|     unused static variables instead to generate a warning. | ||||
|   - Do not include <avr/io.h> when compiling with IAR. | ||||
|   - Introduced USB_CFG_DESCR_PROPS_* in usbconfig.h to configure how each | ||||
|     USB descriptor should be handled. It is now possible to provide descriptor | ||||
|     data in Flash, RAM or dynamically at runtime. | ||||
|   - STALL is now a status in usbTxLen* instead of a message. We can now conform | ||||
|     to the spec and leave the stall status pending until it is cleared. | ||||
|   - Made usbTxPacketCnt1 and usbTxPacketCnt3 public. This allows the | ||||
|     application code to reset data toggling on interrupt pipes. | ||||
|  | ||||
| * Release 2006-07-18 | ||||
							
								
								
									
										458
									
								
								firmware/usbdrv/License.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										458
									
								
								firmware/usbdrv/License.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,458 @@ | ||||
| PREFACE | ||||
|  | ||||
| Conceiving and understanding a new license is not an easy task. To make things | ||||
| easier for both, the author and the licensee, we have decided to base our | ||||
| license for the USB driver on an existing license with well-understood | ||||
| properties. | ||||
|  | ||||
| Our favorite choice for the base license was the GNU General Public License | ||||
| (GPL). However, we cannot use the GNU GPL directly for the following reasons: | ||||
|  | ||||
| (1) It was not intended for projects involving hardware -- we must extend the | ||||
|     term "source code" to at least the circuit diagram. | ||||
| (2) The GNU GPL does not require publication. Only if a binary is published, | ||||
|     it requires that the source is published as well. This is reasonable for | ||||
|     software because unpublished software is of little relevance. For projects | ||||
|     involving hardware, we want to REQUIRE publication. More than that, we | ||||
|     even want to define HOW the publication must be done (files contained, | ||||
|     file formats etc). | ||||
| (3) As the author of the software, we can distribute it under more than one | ||||
|     license. For people who don't want to meet the obligations of the GNU GPL, | ||||
|     we want to offer commercial licenses. To avoid a split in revisions of | ||||
|     the driver, we need special privileges to distribute contributed | ||||
|     modifications under proprietary licenses. | ||||
|  | ||||
| We can not simply modify the GNU GPL and incorporate our changes because the | ||||
| Free Software Foundation (FSF) who holds the copyright for the text of the | ||||
| GNU GPL does not allow modifications. We therefore set up our own small | ||||
| license which incorporates the GNU GPL by reference: | ||||
|  | ||||
|  | ||||
|  | ||||
| LICENSE FOR PROJECTS BUILT WITH "OBJECTIVE DEVELOPMENT'S | ||||
| FIRMWARE-ONLY USB-DRIVER FOR ATMEL'S AVR MICROCONTROLLERS" | ||||
| Version 2006-01 | ||||
|  | ||||
|  | ||||
| I. Definitions | ||||
|  | ||||
| "OBDEV" shall mean OBJECTIVE DEVELOPMENT Software GmbH or any legal successor | ||||
| thereof. | ||||
|  | ||||
| "Software Source Code" shall mean the preferred form of the software for | ||||
| making modifications to it. | ||||
|  | ||||
| "USB Driver" shall mean the Software Source Code for OBDEV's firmware-only | ||||
| USB-driver for Atmel's AVR microcontrollers. | ||||
|  | ||||
| "Function" shall mean the Software Source Code for all software executed on | ||||
| the microcontroller except the USB Driver. | ||||
|  | ||||
| "Host Software" shall mean the Software Source Code for all software required | ||||
| to control the USB device from the USB host running any operating system. | ||||
|  | ||||
| "Project" shall mean the USB Driver, the Function, the Host Software, circuit | ||||
| diagrams of the controller based hardware and accompanying documentation. | ||||
|  | ||||
| "source code" shall have the same meaning as the term "Project" above. | ||||
|  | ||||
| "Web Site" shall mean a collection of text and multimedia documents accessible | ||||
| worldwide over internet through the HyperText Transfer Protocol (HTTP) on | ||||
| TCP port 80 (standard HTTP port). | ||||
|  | ||||
|  | ||||
| II. General License Terms | ||||
| The general terms of this license consist of the GNU General Public License | ||||
| Version 2 (GNU GPL2) which is hereby incorporated into this section as though | ||||
| it were fully set forth here. A copy of the GNU GPL2 is included for your | ||||
| convenience in appendix A of this license. | ||||
|  | ||||
| The term "source code" in the GNU GPL2 is to be understood as defined in | ||||
| section I above. If any term or definition in section I, III, IV or V | ||||
| conflicts with the GNU GPL2, the term or definition in section I, III, IV or | ||||
| V has precedence of the GNU GPL2. | ||||
|  | ||||
|  | ||||
| III. Distribution of the Project | ||||
| The distributed form of a Project must contain at least the following files: | ||||
| (a) Software Source Code files for the USB Driver, the Function and the Host | ||||
|     Software. | ||||
| (b) Circuit diagrams for the hardware in PDF, PNG or GIF image file format. | ||||
| (c) A file with name "Readme.txt" in ASCII format with at least the following | ||||
|     content (in English language): | ||||
|     - An explanation what the Project does. | ||||
|     - What to do with the distributed files (installation procedure etc.). | ||||
|     - A reference to Objective Development's USB driver. | ||||
|     - Your (author's) name and contact information. E-mail and/or URL is | ||||
|       sufficient. | ||||
| (d) Optionally a text file with a description of the circuit diagram, an | ||||
|     explanation of special (software) techniques used etc.  | ||||
| (e) A copy of this license in a file with the name "License.txt". This copy | ||||
|     can be in the "usbdrv" subdirectory which contains the driver. | ||||
|  | ||||
|  | ||||
| IV. Requirement for Publication | ||||
| All modifications and derived work (Projects using the USB Driver) MUST be | ||||
| distributed (published) as described in section III above on a Web Site. The | ||||
| main page must reproduce at least a description of the Project (e.g. as | ||||
| contained in the "Readme.txt" file distributed) and a download link for the | ||||
| entire Project. The URL of the main page must be submitted to OBDEV. OBDEV | ||||
| will provide a mechanism for submitting Project URLs and for publishing | ||||
| Projects on their Web Site. The Project must remain available for at least | ||||
| twelve (12) months after the initial publication or at least six (6) months | ||||
| after a subsequent version of that particular Project has been published. | ||||
|  | ||||
|  | ||||
| V. Author Privileges | ||||
| OBDEV reserves the right to distribute the USB Driver and all modified | ||||
| versions under other (proprietary) licenses. If you modify the USB Driver | ||||
| under the grants of this license, you therefore grant OBDEV (in addition to | ||||
| the grants of the GNU GPL2) a worldwide, perpetual, irrevocable royalty free | ||||
| license for your modifications. OBDEV shall not automatically gain rights | ||||
| other than those of the GNU GPL2 in the other parts of the Project. This | ||||
| section V overrides possibly contradicting terms in the GNU GPL2 referenced | ||||
| in section II. | ||||
|  | ||||
|  | ||||
| APPENDIX A | ||||
|  | ||||
|                     GNU GENERAL PUBLIC LICENSE | ||||
|                        Version 2, June 1991 | ||||
|  | ||||
|  Copyright (C) 1989, 1991 Free Software Foundation, Inc. | ||||
|                        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  Everyone is permitted to copy and distribute verbatim copies | ||||
|  of this license document, but changing it is not allowed. | ||||
|  | ||||
|                             Preamble | ||||
|  | ||||
|   The licenses for most software are designed to take away your | ||||
| freedom to share and change it.  By contrast, the GNU General Public | ||||
| License is intended to guarantee your freedom to share and change free | ||||
| software--to make sure the software is free for all its users.  This | ||||
| General Public License applies to most of the Free Software | ||||
| Foundation's software and to any other program whose authors commit to | ||||
| using it.  (Some other Free Software Foundation software is covered by | ||||
| the GNU Library General Public License instead.)  You can apply it to | ||||
| your programs, too. | ||||
|  | ||||
|   When we speak of free software, we are referring to freedom, not | ||||
| price.  Our General Public Licenses are designed to make sure that you | ||||
| have the freedom to distribute copies of free software (and charge for | ||||
| this service if you wish), that you receive source code or can get it | ||||
| if you want it, that you can change the software or use pieces of it | ||||
| in new free programs; and that you know you can do these things. | ||||
|  | ||||
|   To protect your rights, we need to make restrictions that forbid | ||||
| anyone to deny you these rights or to ask you to surrender the rights. | ||||
| These restrictions translate to certain responsibilities for you if you | ||||
| distribute copies of the software, or if you modify it. | ||||
|  | ||||
|   For example, if you distribute copies of such a program, whether | ||||
| gratis or for a fee, you must give the recipients all the rights that | ||||
| you have.  You must make sure that they, too, receive or can get the | ||||
| source code.  And you must show them these terms so they know their | ||||
| rights. | ||||
|  | ||||
|   We protect your rights with two steps: (1) copyright the software, and | ||||
| (2) offer you this license which gives you legal permission to copy, | ||||
| distribute and/or modify the software. | ||||
|  | ||||
|   Also, for each author's protection and ours, we want to make certain | ||||
| that everyone understands that there is no warranty for this free | ||||
| software.  If the software is modified by someone else and passed on, we | ||||
| want its recipients to know that what they have is not the original, so | ||||
| that any problems introduced by others will not reflect on the original | ||||
| authors' reputations. | ||||
|  | ||||
|   Finally, any free program is threatened constantly by software | ||||
| patents.  We wish to avoid the danger that redistributors of a free | ||||
| program will individually obtain patent licenses, in effect making the | ||||
| program proprietary.  To prevent this, we have made it clear that any | ||||
| patent must be licensed for everyone's free use or not licensed at all. | ||||
|  | ||||
|   The precise terms and conditions for copying, distribution and | ||||
| modification follow. | ||||
|  | ||||
|                     GNU GENERAL PUBLIC LICENSE | ||||
|    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||
|  | ||||
|   0. This License applies to any program or other work which contains | ||||
| a notice placed by the copyright holder saying it may be distributed | ||||
| under the terms of this General Public License.  The "Program", below, | ||||
| refers to any such program or work, and a "work based on the Program" | ||||
| means either the Program or any derivative work under copyright law: | ||||
| that is to say, a work containing the Program or a portion of it, | ||||
| either verbatim or with modifications and/or translated into another | ||||
| language.  (Hereinafter, translation is included without limitation in | ||||
| the term "modification".)  Each licensee is addressed as "you". | ||||
|  | ||||
| Activities other than copying, distribution and modification are not | ||||
| covered by this License; they are outside its scope.  The act of | ||||
| running the Program is not restricted, and the output from the Program | ||||
| is covered only if its contents constitute a work based on the | ||||
| Program (independent of having been made by running the Program). | ||||
| Whether that is true depends on what the Program does. | ||||
|  | ||||
|   1. You may copy and distribute verbatim copies of the Program's | ||||
| source code as you receive it, in any medium, provided that you | ||||
| conspicuously and appropriately publish on each copy an appropriate | ||||
| copyright notice and disclaimer of warranty; keep intact all the | ||||
| notices that refer to this License and to the absence of any warranty; | ||||
| and give any other recipients of the Program a copy of this License | ||||
| along with the Program. | ||||
|  | ||||
| You may charge a fee for the physical act of transferring a copy, and | ||||
| you may at your option offer warranty protection in exchange for a fee. | ||||
|  | ||||
|   2. You may modify your copy or copies of the Program or any portion | ||||
| of it, thus forming a work based on the Program, and copy and | ||||
| distribute such modifications or work under the terms of Section 1 | ||||
| above, provided that you also meet all of these conditions: | ||||
|  | ||||
|     a) You must cause the modified files to carry prominent notices | ||||
|     stating that you changed the files and the date of any change. | ||||
|  | ||||
|     b) You must cause any work that you distribute or publish, that in | ||||
|     whole or in part contains or is derived from the Program or any | ||||
|     part thereof, to be licensed as a whole at no charge to all third | ||||
|     parties under the terms of this License. | ||||
|  | ||||
|     c) If the modified program normally reads commands interactively | ||||
|     when run, you must cause it, when started running for such | ||||
|     interactive use in the most ordinary way, to print or display an | ||||
|     announcement including an appropriate copyright notice and a | ||||
|     notice that there is no warranty (or else, saying that you provide | ||||
|     a warranty) and that users may redistribute the program under | ||||
|     these conditions, and telling the user how to view a copy of this | ||||
|     License.  (Exception: if the Program itself is interactive but | ||||
|     does not normally print such an announcement, your work based on | ||||
|     the Program is not required to print an announcement.) | ||||
|  | ||||
| These requirements apply to the modified work as a whole.  If | ||||
| identifiable sections of that work are not derived from the Program, | ||||
| and can be reasonably considered independent and separate works in | ||||
| themselves, then this License, and its terms, do not apply to those | ||||
| sections when you distribute them as separate works.  But when you | ||||
| distribute the same sections as part of a whole which is a work based | ||||
| on the Program, the distribution of the whole must be on the terms of | ||||
| this License, whose permissions for other licensees extend to the | ||||
| entire whole, and thus to each and every part regardless of who wrote it. | ||||
|  | ||||
| Thus, it is not the intent of this section to claim rights or contest | ||||
| your rights to work written entirely by you; rather, the intent is to | ||||
| exercise the right to control the distribution of derivative or | ||||
| collective works based on the Program. | ||||
|  | ||||
| In addition, mere aggregation of another work not based on the Program | ||||
| with the Program (or with a work based on the Program) on a volume of | ||||
| a storage or distribution medium does not bring the other work under | ||||
| the scope of this License. | ||||
|  | ||||
|   3. You may copy and distribute the Program (or a work based on it, | ||||
| under Section 2) in object code or executable form under the terms of | ||||
| Sections 1 and 2 above provided that you also do one of the following: | ||||
|  | ||||
|     a) Accompany it with the complete corresponding machine-readable | ||||
|     source code, which must be distributed under the terms of Sections | ||||
|     1 and 2 above on a medium customarily used for software interchange; or, | ||||
|  | ||||
|     b) Accompany it with a written offer, valid for at least three | ||||
|     years, to give any third party, for a charge no more than your | ||||
|     cost of physically performing source distribution, a complete | ||||
|     machine-readable copy of the corresponding source code, to be | ||||
|     distributed under the terms of Sections 1 and 2 above on a medium | ||||
|     customarily used for software interchange; or, | ||||
|  | ||||
|     c) Accompany it with the information you received as to the offer | ||||
|     to distribute corresponding source code.  (This alternative is | ||||
|     allowed only for noncommercial distribution and only if you | ||||
|     received the program in object code or executable form with such | ||||
|     an offer, in accord with Subsection b above.) | ||||
|  | ||||
| The source code for a work means the preferred form of the work for | ||||
| making modifications to it.  For an executable work, complete source | ||||
| code means all the source code for all modules it contains, plus any | ||||
| associated interface definition files, plus the scripts used to | ||||
| control compilation and installation of the executable.  However, as a | ||||
| special exception, the source code distributed need not include | ||||
| anything that is normally distributed (in either source or binary | ||||
| form) with the major components (compiler, kernel, and so on) of the | ||||
| operating system on which the executable runs, unless that component | ||||
| itself accompanies the executable. | ||||
|  | ||||
| If distribution of executable or object code is made by offering | ||||
| access to copy from a designated place, then offering equivalent | ||||
| access to copy the source code from the same place counts as | ||||
| distribution of the source code, even though third parties are not | ||||
| compelled to copy the source along with the object code. | ||||
|  | ||||
|   4. You may not copy, modify, sublicense, or distribute the Program | ||||
| except as expressly provided under this License.  Any attempt | ||||
| otherwise to copy, modify, sublicense or distribute the Program is | ||||
| void, and will automatically terminate your rights under this License. | ||||
| However, parties who have received copies, or rights, from you under | ||||
| this License will not have their licenses terminated so long as such | ||||
| parties remain in full compliance. | ||||
|  | ||||
|   5. You are not required to accept this License, since you have not | ||||
| signed it.  However, nothing else grants you permission to modify or | ||||
| distribute the Program or its derivative works.  These actions are | ||||
| prohibited by law if you do not accept this License.  Therefore, by | ||||
| modifying or distributing the Program (or any work based on the | ||||
| Program), you indicate your acceptance of this License to do so, and | ||||
| all its terms and conditions for copying, distributing or modifying | ||||
| the Program or works based on it. | ||||
|  | ||||
|   6. Each time you redistribute the Program (or any work based on the | ||||
| Program), the recipient automatically receives a license from the | ||||
| original licensor to copy, distribute or modify the Program subject to | ||||
| these terms and conditions.  You may not impose any further | ||||
| restrictions on the recipients' exercise of the rights granted herein. | ||||
| You are not responsible for enforcing compliance by third parties to | ||||
| this License. | ||||
|  | ||||
|   7. If, as a consequence of a court judgment or allegation of patent | ||||
| infringement or for any other reason (not limited to patent issues), | ||||
| conditions are imposed on you (whether by court order, agreement or | ||||
| otherwise) that contradict the conditions of this License, they do not | ||||
| excuse you from the conditions of this License.  If you cannot | ||||
| distribute so as to satisfy simultaneously your obligations under this | ||||
| License and any other pertinent obligations, then as a consequence you | ||||
| may not distribute the Program at all.  For example, if a patent | ||||
| license would not permit royalty-free redistribution of the Program by | ||||
| all those who receive copies directly or indirectly through you, then | ||||
| the only way you could satisfy both it and this License would be to | ||||
| refrain entirely from distribution of the Program. | ||||
|  | ||||
| If any portion of this section is held invalid or unenforceable under | ||||
| any particular circumstance, the balance of the section is intended to | ||||
| apply and the section as a whole is intended to apply in other | ||||
| circumstances. | ||||
|  | ||||
| It is not the purpose of this section to induce you to infringe any | ||||
| patents or other property right claims or to contest validity of any | ||||
| such claims; this section has the sole purpose of protecting the | ||||
| integrity of the free software distribution system, which is | ||||
| implemented by public license practices.  Many people have made | ||||
| generous contributions to the wide range of software distributed | ||||
| through that system in reliance on consistent application of that | ||||
| system; it is up to the author/donor to decide if he or she is willing | ||||
| to distribute software through any other system and a licensee cannot | ||||
| impose that choice. | ||||
|  | ||||
| This section is intended to make thoroughly clear what is believed to | ||||
| be a consequence of the rest of this License. | ||||
|  | ||||
|   8. If the distribution and/or use of the Program is restricted in | ||||
| certain countries either by patents or by copyrighted interfaces, the | ||||
| original copyright holder who places the Program under this License | ||||
| may add an explicit geographical distribution limitation excluding | ||||
| those countries, so that distribution is permitted only in or among | ||||
| countries not thus excluded.  In such case, this License incorporates | ||||
| the limitation as if written in the body of this License. | ||||
|  | ||||
|   9. The Free Software Foundation may publish revised and/or new versions | ||||
| of the General Public License from time to time.  Such new versions will | ||||
| be similar in spirit to the present version, but may differ in detail to | ||||
| address new problems or concerns. | ||||
|  | ||||
| Each version is given a distinguishing version number.  If the Program | ||||
| specifies a version number of this License which applies to it and "any | ||||
| later version", you have the option of following the terms and conditions | ||||
| either of that version or of any later version published by the Free | ||||
| Software Foundation.  If the Program does not specify a version number of | ||||
| this License, you may choose any version ever published by the Free Software | ||||
| Foundation. | ||||
|  | ||||
|   10. If you wish to incorporate parts of the Program into other free | ||||
| programs whose distribution conditions are different, write to the author | ||||
| to ask for permission.  For software which is copyrighted by the Free | ||||
| Software Foundation, write to the Free Software Foundation; we sometimes | ||||
| make exceptions for this.  Our decision will be guided by the two goals | ||||
| of preserving the free status of all derivatives of our free software and | ||||
| of promoting the sharing and reuse of software generally. | ||||
|  | ||||
|                             NO WARRANTY | ||||
|  | ||||
|   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | ||||
| FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN | ||||
| OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | ||||
| PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | ||||
| OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||
| MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS | ||||
| TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE | ||||
| PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | ||||
| REPAIR OR CORRECTION. | ||||
|  | ||||
|   12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||||
| WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | ||||
| REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | ||||
| INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | ||||
| OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | ||||
| TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | ||||
| YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | ||||
| PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | ||||
| POSSIBILITY OF SUCH DAMAGES. | ||||
|  | ||||
|                      END OF TERMS AND CONDITIONS | ||||
|  | ||||
|             How to Apply These Terms to Your New Programs | ||||
|  | ||||
|   If you develop a new program, and you want it to be of the greatest | ||||
| possible use to the public, the best way to achieve this is to make it | ||||
| free software which everyone can redistribute and change under these terms. | ||||
|  | ||||
|   To do so, attach the following notices to the program.  It is safest | ||||
| to attach them to the start of each source file to most effectively | ||||
| convey the exclusion of warranty; and each file should have at least | ||||
| the "copyright" line and a pointer to where the full notice is found. | ||||
|  | ||||
|     <one line to give the program's name and a brief idea of what it does.> | ||||
|     Copyright (C) <year>  <name of author> | ||||
|  | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
|  | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
|  | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program; if not, write to the Free Software | ||||
|     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
|  | ||||
| Also add information on how to contact you by electronic and paper mail. | ||||
|  | ||||
| If the program is interactive, make it output a short notice like this | ||||
| when it starts in an interactive mode: | ||||
|  | ||||
|     Gnomovision version 69, Copyright (C) year name of author | ||||
|     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | ||||
|     This is free software, and you are welcome to redistribute it | ||||
|     under certain conditions; type `show c' for details. | ||||
|  | ||||
| The hypothetical commands `show w' and `show c' should show the appropriate | ||||
| parts of the General Public License.  Of course, the commands you use may | ||||
| be called something other than `show w' and `show c'; they could even be | ||||
| mouse-clicks or menu items--whatever suits your program. | ||||
|  | ||||
| You should also get your employer (if you work as a programmer) or your | ||||
| school, if any, to sign a "copyright disclaimer" for the program, if | ||||
| necessary.  Here is a sample; alter the names: | ||||
|  | ||||
|   Yoyodyne, Inc., hereby disclaims all copyright interest in the program | ||||
|   `Gnomovision' (which makes passes at compilers) written by James Hacker. | ||||
|  | ||||
|   <signature of Ty Coon>, 1 April 1989 | ||||
|   Ty Coon, President of Vice | ||||
|  | ||||
| This General Public License does not permit incorporating your program into | ||||
| proprietary programs.  If your program is a subroutine library, you may | ||||
| consider it more useful to permit linking proprietary applications with the | ||||
| library.  If this is what you want to do, use the GNU Library General | ||||
| Public License instead of this License. | ||||
							
								
								
									
										88
									
								
								firmware/usbdrv/Readme.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								firmware/usbdrv/Readme.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| This is the Readme file to Objective Development's firmware-only USB driver | ||||
| for Atmel AVR microcontrollers. For more information please visit | ||||
| http://www.obdev.at/avrusb/ | ||||
|  | ||||
| This directory contains the USB firmware only. Copy it as-is to your own | ||||
| project and add your own version of "usbconfig.h". A template for your own | ||||
| "usbconfig.h" can be found in "usbconfig-prototype.h" in this directory. | ||||
|  | ||||
|  | ||||
| TECHNICAL DOCUMENTATION | ||||
| ======================= | ||||
| The technical documentation for the firmware driver is contained in the file | ||||
| "usbdrv.h". Please read all of it carefully! | ||||
|  | ||||
|  | ||||
| USB IDENTIFIERS | ||||
| =============== | ||||
| Every USB device needs a vendor- and a product-identifier (VID and PID). VIDs | ||||
| are obtained from usb.org for a price of 1,500 USD. Once you have a VID, you | ||||
| can assign PIDs at will. | ||||
|  | ||||
| Since an entry level cost of 1,500 USD is too high for most small companies | ||||
| and hobbyists, we provide a single VID/PID pair for free. If you want to use | ||||
| your own VID and PID instead of our's, define the macros "USB_CFG_VENDOR_ID" | ||||
| and "USB_CFG_DEVICE_ID" accordingly in "usbconfig.h". | ||||
|  | ||||
| To use our predefined VID/PID pair, you MUST conform to a couple of | ||||
| requirements. See the file "USBID-License.txt" for details. | ||||
|  | ||||
|  | ||||
| HOST DRIVER | ||||
| =========== | ||||
| You have received this driver together with an example device implementation | ||||
| and an example host driver. The host driver is based on libusb and compiles | ||||
| on various Unix flavors (Linux, BSD, Mac OS X). It also compiles natively on | ||||
| Windows using MinGW (see www.mingw.org) and libusb-win32 (see | ||||
| libusb-win32.sourceforge.net). The "Automator" project contains a native | ||||
| Windows host driver (not based on libusb) for Human Interface Devices. | ||||
|  | ||||
|  | ||||
| DEVELOPMENT SYSTEM | ||||
| ================== | ||||
| This driver has been developed and optimized for the GNU compiler version 3 | ||||
| (gcc 3). It does work well with gcc 4 and future versions will probably be | ||||
| optimized for gcc 4. We recommend that you use the GNU compiler suite because | ||||
| it is freely available. AVR-USB has also been ported to the IAR compiler and | ||||
| assembler. It has been tested with IAR 4.10B/W32 and 4.12A/W32 on an ATmega8 | ||||
| with the "small" and "tiny" memory model. Please note that gcc is more | ||||
| efficient for usbdrv.c because this module has been deliberately optimized | ||||
| for gcc. | ||||
|  | ||||
|  | ||||
| USING AVR-USB FOR FREE | ||||
| ====================== | ||||
| The AVR firmware driver is published under an Open Source compliant license. | ||||
| See the file "License.txt" for details. Since it is not obvious for many | ||||
| people how this license applies to their own projects, here's a short guide: | ||||
|  | ||||
| (1) The USB driver and all your modifications to the driver itself are owned | ||||
| by Objective Development. You must give us a worldwide, perpetual, | ||||
| irrevocable royalty free license for your modifications. | ||||
|  | ||||
| (2) Since you own the code you have written (except where you modify our | ||||
| driver), you can (at least in principle) determine the license for it freely. | ||||
| However, to "pay" for the USB driver code you link against, we demand that | ||||
| you choose an Open Source compliant license (compatible with our license) for | ||||
| your source code and the hardware circuit diagrams. Simply attach your | ||||
| license of choice to your parts of the project and leave our "License.txt" in | ||||
| the "usbdrv" subdirectory. | ||||
|  | ||||
| (3) We also demand that you publish your work on the Internet and drop us a | ||||
| note with the URL. The publication must meet certain formal criteria (files | ||||
| distributed, file formats etc.). See the file "License.txt" for details. | ||||
|  | ||||
| Other than that, you are allowed to manufacture any number of units and sell | ||||
| them for any price. If you like our driver, we also encourage you to make a | ||||
| donation on our web site. | ||||
|  | ||||
|  | ||||
| COMMERCIAL LICENSES FOR AVR-USB | ||||
| =============================== | ||||
| If you don't want to publish your source code and the circuit diagrams under | ||||
| an Open Source license, you can simply pay money for AVR-USB. As an | ||||
| additional benefit you get USB PIDs for free, licensed exclusively to you. | ||||
| See http://www.obdev.at/products/avrusb/license.html for details. | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										143
									
								
								firmware/usbdrv/USBID-License.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								firmware/usbdrv/USBID-License.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| Royalty-Free Non-Exclusive License USB Product-ID | ||||
| ================================================= | ||||
|  | ||||
| Version 2006-06-19 | ||||
|  | ||||
| OBJECTIVE DEVELOPMENT Software GmbH hereby grants you the non-exclusive | ||||
| right to use three USB.org vendor-ID (VID) / product-ID (PID) pairs with | ||||
| products based on Objective Development's firmware-only USB driver for | ||||
| Atmel AVR microcontrollers: | ||||
|  | ||||
|  * VID = 5824 (=0x16c0) / PID = 1500 (=0x5dc) for devices implementing no | ||||
|    USB device class (vendor-class devices with USB class = 0xff). Devices | ||||
|    using this pair will be referred to as "VENDOR CLASS" devices. | ||||
|  | ||||
|  * VID = 5824 (=0x16c0) / PID = 1503 (=0x5df) for HID class devices | ||||
|    (excluding mice and keyboards). Devices using this pair will be referred | ||||
|    to as "HID CLASS" devices. | ||||
|  | ||||
|  * VID = 5824 (=0x16c0) / PID = 1505 (=0x5e1) for CDC class modem devices | ||||
|    Devices using this pair will be referred to as "CDC-ACM CLASS" devices. | ||||
|  | ||||
| Since the granted right is non-exclusive, the same VID/PID pairs may be | ||||
| used by many companies and individuals for different products. To avoid | ||||
| conflicts, your device and host driver software MUST adhere to the rules | ||||
| outlined below. | ||||
|  | ||||
| OBJECTIVE DEVELOPMENT Software GmbH has licensed these VID/PID pairs from | ||||
| Wouter van Ooijen (see www.voti.nl), who has licensed the VID from the USB | ||||
| Implementers Forum, Inc. (see www.usb.org). The VID is registered for the | ||||
| company name "Van Ooijen Technische Informatica". | ||||
|  | ||||
|  | ||||
| RULES AND RESTRICTIONS | ||||
| ====================== | ||||
|  | ||||
| (1) The USB device MUST provide a textual representation of the | ||||
| manufacturer and product identification. The manufacturer identification | ||||
| MUST be available at least in USB language 0x0409 (English/US). | ||||
|  | ||||
| (2) The textual manufacturer identification MUST contain either an Internet | ||||
| domain name (e.g. "mycompany.com") registered and owned by you, or an | ||||
| e-mail address under your control (e.g. "myname@gmx.net"). You can embed | ||||
| the domain name or e-mail address in any string you like, e.g.  "Objective | ||||
| Development http://www.obdev.at/avrusb/". | ||||
|  | ||||
| (3) You are responsible for retaining ownership of the domain or e-mail | ||||
| address for as long as any of your products are in use. | ||||
|  | ||||
| (4) You may choose any string for the textual product identification, as | ||||
| long as this string is unique within the scope of your textual manufacturer | ||||
| identification. | ||||
|  | ||||
| (5) Matching of device-specific drivers MUST be based on the textual | ||||
| manufacturer and product identification in addition to the usual VID/PID | ||||
| matching. This means that operating system features which are based on | ||||
| VID/PID matching only (e.g. Windows kernel level drivers, automatic actions | ||||
| when the device is plugged in etc) MUST NOT be used. The driver matching | ||||
| MUST be a comparison of the entire strings, NOT a sub-string match. For | ||||
| CDC-ACM CLASS devices, a generic class driver should be used and the | ||||
| matching is based on the USB device class. | ||||
|  | ||||
| (6) The extent to which VID/PID matching is allowed for non device-specific | ||||
| drivers or features depends on the operating system and particular VID/PID | ||||
| pair used: | ||||
|  | ||||
|  * Mac OS X, Linux, FreeBSD and other Unixes: No VID/PID matching is | ||||
|    required and hence no VID/PID-only matching is allowed at all. | ||||
|  | ||||
|  * Windows: The operating system performs VID/PID matching for the kernel | ||||
|    level driver. You are REQUIRED to use libusb-win32 (see | ||||
|    http://libusb-win32.sourceforge.net/) as the kernel level driver for | ||||
|    VENDOR CLASS devices. HID CLASS devices all use the generic HID class | ||||
|    driver shipped with Windows, except mice and keyboards. You therefore | ||||
|    MUST NOT use any of the shared VID/PID pairs for mice or keyboards. | ||||
|    CDC-ACM CLASS devices require a ".inf" file which matches on the VID/PID | ||||
|    pair. This ".inf" file MUST load the "usbser" driver to configure the | ||||
|    device as modem (COM-port). | ||||
|  | ||||
| (7) OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any | ||||
| problems which are caused by the shared use of these VID/PID pairs. You | ||||
| have been warned that the sharing of VID/PID pairs may cause problems. If | ||||
| you want to avoid them, get your own VID/PID pair for exclusive use. | ||||
|  | ||||
|  | ||||
| HOW TO IMPLEMENT THESE RULES | ||||
| ============================ | ||||
|  | ||||
| The following rules are for VENDOR CLASS and HID CLASS devices. CDC-ACM | ||||
| CLASS devices use the operating system's class driver and don't need a | ||||
| custom driver. | ||||
|  | ||||
| The host driver MUST iterate over all devices with the given VID/PID | ||||
| numbers in their device descriptors and query the string representation for | ||||
| the manufacturer name in USB language 0x0409 (English/US). It MUST compare | ||||
| the ENTIRE string with your textual manufacturer identification chosen in | ||||
| (2) above. A substring search for your domain or e-mail address is NOT | ||||
| acceptable. The driver MUST NOT touch the device (other than querying the | ||||
| descriptors) unless the strings match. | ||||
|  | ||||
| For all USB devices with matching VID/PID and textual manufacturer | ||||
| identification, the host driver must query the textual product | ||||
| identification and string-compare it with the name of the product it can | ||||
| control. It may only initialize the device if the product matches exactly. | ||||
|  | ||||
| Objective Development provides examples for these matching rules with the | ||||
| "PowerSwitch" project (using libusb) and with the "Automator" project | ||||
| (using Windows calls on Windows and libusb on Unix). | ||||
|  | ||||
|  | ||||
| Technical Notes: | ||||
| ================ | ||||
|  | ||||
| Sharing the same VID/PID pair among devices is possible as long as ALL | ||||
| drivers which match the VID/PID also perform matching on the textual | ||||
| identification strings. This is easy on all operating systems except | ||||
| Windows, since Windows establishes a static connection between the VID/PID | ||||
| pair and a kernel level driver. All devices with the same VID/PID pair must | ||||
| therefore use THE SAME kernel level driver. | ||||
|  | ||||
| We therefore demand that you use libusb-win32 for VENDOR CLASS devices. | ||||
| This is a generic kernel level driver which allows all types of USB access | ||||
| for user space applications. This is only a partial solution of the | ||||
| problem, though, because different device drivers may come with different | ||||
| versions of libusb-win32 and they may not work with the libusb version of | ||||
| the respective other driver. You are therefore encouraged to test your | ||||
| driver against a broad range of libusb-win32 versions. Do not use new | ||||
| features in new versions, or check for their existence before you use them. | ||||
| When a new libusb-win32 becomes available, make sure that your driver is | ||||
| compatible with it. | ||||
|  | ||||
| For HID CLASS devices it is necessary that all those devices bind to the | ||||
| same kernel driver: Microsoft's generic USB HID driver. This is true for | ||||
| all HID devices except those with a specialized driver. Currently, the only | ||||
| HIDs with specialized drivers are mice and keyboards. You therefore MUST | ||||
| NOT use a shared VID/PID with mouse and keyboard devices. | ||||
|  | ||||
| Sharing the same VID/PID among different products is unusual and probably | ||||
| violates the USB specification. If you do it, you do it at your own risk. | ||||
|  | ||||
| To avoid possible incompatibilities, we highly recommend that you get your | ||||
| own VID/PID pair if you intend to sell your product. Objective | ||||
| Development's commercial licenses for AVR-USB include a PID for | ||||
| unrestricted exclusive use. | ||||
							
								
								
									
										70
									
								
								firmware/usbdrv/iarcompat.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								firmware/usbdrv/iarcompat.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| /* Name: iarcompat.h | ||||
|  * Project: AVR USB driver | ||||
|  * Author: Christian Starkjohann | ||||
|  * Creation Date: 2006-03-01 | ||||
|  * Tabsize: 4 | ||||
|  * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH | ||||
|  * License: Proprietary, free under certain conditions. See Documentation. | ||||
|  * This Revision: $Id: iarcompat.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| General Description: | ||||
| This header is included when we compile with the IAR C-compiler and assembler. | ||||
| It defines macros for cross compatibility between gcc and IAR-cc. | ||||
|  | ||||
| Thanks to Oleg Semyonov for his help with the IAR tools port! | ||||
| */ | ||||
|  | ||||
| #ifndef __iarcompat_h_INCLUDED__ | ||||
| #define __iarcompat_h_INCLUDED__ | ||||
|  | ||||
| #if defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ | ||||
|  | ||||
| /* Enable bit definitions */ | ||||
| #ifndef ENABLE_BIT_DEFINITIONS | ||||
| #   define ENABLE_BIT_DEFINITIONS	1 | ||||
| #endif | ||||
|  | ||||
| /* Include IAR headers */ | ||||
| #include <ioavr.h> | ||||
| #ifndef __IAR_SYSTEMS_ASM__ | ||||
| #   include <inavr.h> | ||||
| #endif | ||||
|  | ||||
| #define __attribute__(arg) | ||||
| #define IAR_SECTION(section)    @ section | ||||
|  | ||||
| #ifndef USB_BUFFER_SECTION | ||||
| #   define  USB_BUFFER_SECTION  "TINY_Z"    /* if user has not selected a named section */ | ||||
| #endif | ||||
|  | ||||
| #ifdef __IAR_SYSTEMS_ASM__ | ||||
| #   define __ASSEMBLER__ | ||||
| #endif | ||||
|  | ||||
| #ifdef __HAS_ELPM__ | ||||
| #   define PROGMEM __farflash | ||||
| #else | ||||
| #   define PROGMEM __flash | ||||
| #endif | ||||
|  | ||||
| #define PRG_RDB(addr)   (*(PROGMEM char *)(addr)) | ||||
|  | ||||
| /* The following definitions are not needed by the driver, but may be of some | ||||
|  * help if you port a gcc based project to IAR. | ||||
|  */ | ||||
| #define cli()       __disable_interrupt() | ||||
| #define sei()       __enable_interrupt() | ||||
| #define wdt_reset() __watchdog_reset() | ||||
|  | ||||
| /* Depending on the device you use, you may get problems with the way usbdrv.h | ||||
|  * handles the differences between devices. Since IAR does not use #defines | ||||
|  * for MCU registers, we can't check for the existence of a particular | ||||
|  * register with an #ifdef. If the autodetection mechanism fails, include | ||||
|  * definitions for the required USB_INTR_* macros in your usbconfig.h. See | ||||
|  * usbconfig-prototype.h and usbdrv.h for details. | ||||
|  */ | ||||
|  | ||||
| #endif  /* defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ */ | ||||
| #endif  /* __iarcompat_h_INCLUDED__ */ | ||||
							
								
								
									
										53
									
								
								firmware/usbdrv/oddebug.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								firmware/usbdrv/oddebug.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| /* Name: oddebug.c | ||||
|  * Project: AVR library | ||||
|  * Author: Christian Starkjohann | ||||
|  * Creation Date: 2005-01-16 | ||||
|  * Tabsize: 4 | ||||
|  * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | ||||
|  * License: Proprietary, free under certain conditions. See Documentation. | ||||
|  * This Revision: $Id: oddebug.c,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  */ | ||||
|  | ||||
| #include "oddebug.h" | ||||
|  | ||||
| #if DEBUG_LEVEL > 0 | ||||
|  | ||||
| static uchar Warning__Never_compile_production_devices_with_debugging; | ||||
| /* The "#warning" preprocessor directive is non-standard. The unused static | ||||
|  * variable above should give a compiler warning on all compilers. | ||||
|  */ | ||||
|  | ||||
| static void uartPutc(char c) | ||||
| { | ||||
|     while(!(ODDBG_USR & (1 << ODDBG_UDRE)));    /* wait for data register empty */ | ||||
|     ODDBG_UDR = c; | ||||
| } | ||||
|  | ||||
| static uchar    hexAscii(uchar h) | ||||
| { | ||||
|     h &= 0xf; | ||||
|     if(h >= 10) | ||||
|         h += 'a' - (uchar)10 - '0'; | ||||
|     h += '0'; | ||||
|     return h; | ||||
| } | ||||
|  | ||||
| static void printHex(uchar c) | ||||
| { | ||||
|     uartPutc(hexAscii(c >> 4)); | ||||
|     uartPutc(hexAscii(c)); | ||||
| } | ||||
|  | ||||
| void    odDebug(uchar prefix, uchar *data, uchar len) | ||||
| { | ||||
|     printHex(prefix); | ||||
|     uartPutc(':'); | ||||
|     while(len--){ | ||||
|         uartPutc(' '); | ||||
|         printHex(*data++); | ||||
|     } | ||||
|     uartPutc('\r'); | ||||
|     uartPutc('\n'); | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										126
									
								
								firmware/usbdrv/oddebug.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								firmware/usbdrv/oddebug.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| /* Name: oddebug.h | ||||
|  * Project: AVR library | ||||
|  * Author: Christian Starkjohann | ||||
|  * Creation Date: 2005-01-16 | ||||
|  * Tabsize: 4 | ||||
|  * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | ||||
|  * License: Proprietary, free under certain conditions. See Documentation. | ||||
|  * This Revision: $Id: oddebug.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  */ | ||||
|  | ||||
| #ifndef __oddebug_h_included__ | ||||
| #define __oddebug_h_included__ | ||||
|  | ||||
| /* | ||||
| General Description: | ||||
| This module implements a function for debug logs on the serial line of the | ||||
| AVR microcontroller. Debugging can be configured with the define | ||||
| 'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging | ||||
| calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is | ||||
| 2, DBG1 and DBG2 logs will be printed. | ||||
|  | ||||
| A debug log consists of a label ('prefix') to indicate which debug log created | ||||
| the output and a memory block to dump in hex ('data' and 'len'). | ||||
| */ | ||||
|  | ||||
|  | ||||
| #ifndef F_CPU | ||||
| #   define  F_CPU   12000000    /* 12 MHz */ | ||||
| #endif | ||||
|  | ||||
| /* make sure we have the UART defines: */ | ||||
| #include "iarcompat.h" | ||||
| #ifndef __IAR_SYSTEMS_ICC__ | ||||
| #   include <avr/io.h> | ||||
| #endif | ||||
|  | ||||
| #ifndef uchar | ||||
| #   define  uchar   unsigned char | ||||
| #endif | ||||
|  | ||||
| #if DEBUG_LEVEL > 0 && !(defined TXEN || defined TXEN0) /* no UART in device */ | ||||
| #   warning "Debugging disabled because device has no UART" | ||||
| #   undef   DEBUG_LEVEL | ||||
| #endif | ||||
|  | ||||
| #ifndef DEBUG_LEVEL | ||||
| #   define  DEBUG_LEVEL 0 | ||||
| #endif | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| #if DEBUG_LEVEL > 0 | ||||
| #   define  DBG1(prefix, data, len) odDebug(prefix, data, len) | ||||
| #else | ||||
| #   define  DBG1(prefix, data, len) | ||||
| #endif | ||||
|  | ||||
| #if DEBUG_LEVEL > 1 | ||||
| #   define  DBG2(prefix, data, len) odDebug(prefix, data, len) | ||||
| #else | ||||
| #   define  DBG2(prefix, data, len) | ||||
| #endif | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| #if DEBUG_LEVEL > 0 | ||||
| extern void odDebug(uchar prefix, uchar *data, uchar len); | ||||
|  | ||||
| /* Try to find our control registers; ATMEL likes to rename these */ | ||||
|  | ||||
| #if defined UBRR | ||||
| #   define  ODDBG_UBRR  UBRR | ||||
| #elif defined UBRRL | ||||
| #   define  ODDBG_UBRR  UBRRL | ||||
| #elif defined UBRR0 | ||||
| #   define  ODDBG_UBRR  UBRR0 | ||||
| #elif defined UBRR0L | ||||
| #   define  ODDBG_UBRR  UBRR0L | ||||
| #endif | ||||
|  | ||||
| #if defined UCR | ||||
| #   define  ODDBG_UCR   UCR | ||||
| #elif defined UCSRB | ||||
| #   define  ODDBG_UCR   UCSRB | ||||
| #elif defined UCSR0B | ||||
| #   define  ODDBG_UCR   UCSR0B | ||||
| #endif | ||||
|  | ||||
| #if defined TXEN | ||||
| #   define  ODDBG_TXEN  TXEN | ||||
| #else | ||||
| #   define  ODDBG_TXEN  TXEN0 | ||||
| #endif | ||||
|  | ||||
| #if defined USR | ||||
| #   define  ODDBG_USR   USR | ||||
| #elif defined UCSRA | ||||
| #   define  ODDBG_USR   UCSRA | ||||
| #elif defined UCSR0A | ||||
| #   define  ODDBG_USR   UCSR0A | ||||
| #endif | ||||
|  | ||||
| #if defined UDRE | ||||
| #   define  ODDBG_UDRE  UDRE | ||||
| #else | ||||
| #   define  ODDBG_UDRE  UDRE0 | ||||
| #endif | ||||
|  | ||||
| #if defined UDR | ||||
| #   define  ODDBG_UDR   UDR | ||||
| #elif defined UDR0 | ||||
| #   define  ODDBG_UDR   UDR0 | ||||
| #endif | ||||
|  | ||||
| static inline void  odDebugInit(void) | ||||
| { | ||||
|     ODDBG_UCR |= (1<<ODDBG_TXEN); | ||||
|     ODDBG_UBRR = F_CPU / (19200 * 16L) - 1; | ||||
| } | ||||
| #else | ||||
| #   define odDebugInit() | ||||
| #endif | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| #endif /* __oddebug_h_included__ */ | ||||
							
								
								
									
										265
									
								
								firmware/usbdrv/usbconfig-prototype.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								firmware/usbdrv/usbconfig-prototype.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,265 @@ | ||||
| /* Name: usbconfig.h | ||||
|  * Project: AVR USB driver | ||||
|  * Author: Christian Starkjohann | ||||
|  * Creation Date: 2005-04-01 | ||||
|  * Tabsize: 4 | ||||
|  * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | ||||
|  * License: Proprietary, free under certain conditions. See Documentation. | ||||
|  * This Revision: $Id: usbconfig-prototype.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  */ | ||||
|  | ||||
| #ifndef __usbconfig_h_included__ | ||||
| #define __usbconfig_h_included__ | ||||
|  | ||||
| /* | ||||
| General Description: | ||||
| This file is an example configuration (with inline documentation) for the USB | ||||
| driver. It configures AVR-USB for an ATMega8 with USB D+ connected to Port D | ||||
| bit 2 (which is also hardware interrupt 0) and USB D- to Port D bit 0. You may | ||||
| wire the lines to any other port, as long as D- is on bit 0 and D+ is also | ||||
| wired to INT0. | ||||
| To create your own usbconfig.h file, copy this file to the directory | ||||
| containing "usbdrv" (that is your project firmware source directory) and | ||||
| rename it to "usbconfig.h". Then edit it accordingly. | ||||
| */ | ||||
|  | ||||
| /* ---------------------------- Hardware Config ---------------------------- */ | ||||
|  | ||||
| #define USB_CFG_IOPORTNAME      D | ||||
| /* This is the port where the USB bus is connected. When you configure it to | ||||
|  * "B", the registers PORTB, PINB and DDRB will be used. | ||||
|  */ | ||||
| #define USB_CFG_DMINUS_BIT      0 | ||||
| /* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. | ||||
|  * This may be any bit in the port. | ||||
|  */ | ||||
| #define USB_CFG_DPLUS_BIT       2 | ||||
| /* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. | ||||
|  * This may be any bit in the port. Please note that D+ must also be connected | ||||
|  * to interrupt pin INT0! | ||||
|  */ | ||||
|  | ||||
| /* ----------------------- Optional Hardware Config ------------------------ */ | ||||
|  | ||||
| /* #define USB_CFG_PULLUP_IOPORTNAME   D */ | ||||
| /* If you connect the 1.5k pullup resistor from D- to a port pin instead of | ||||
|  * V+, you can connect and disconnect the device from firmware by calling | ||||
|  * the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h). | ||||
|  * This constant defines the port on which the pullup resistor is connected. | ||||
|  */ | ||||
| /* #define USB_CFG_PULLUP_BIT          4 */ | ||||
| /* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined | ||||
|  * above) where the 1.5k pullup resistor is connected. See description | ||||
|  * above for details. | ||||
|  */ | ||||
| /* #define  USB_BUFFER_SECTION         ".bss" */ | ||||
| /* The USB receive buffer (variable "usbRxBuf") with a length of 22 bytes | ||||
|  * MUST NOT cross a 256 byte boundary. We have introduced this configuration | ||||
|  * option to allow you to change the data segment where this buffer is | ||||
|  * allocated. If you have problems with the default segment (start of .bss), | ||||
|  * you may change this setting. See the comment in usbdrv.h for details. | ||||
|  * On IAR C, the default is the TINY_Z segment (first 256 bytes). You must | ||||
|  * change this default for devices which don't have RAM below 0x100. | ||||
|  */ | ||||
|  | ||||
| /* --------------------------- Functional Range ---------------------------- */ | ||||
|  | ||||
| #define USB_CFG_HAVE_INTRIN_ENDPOINT    1 | ||||
| /* Define this to 1 if you want to compile a version with two endpoints: The | ||||
|  * default control endpoint 0 and an interrupt-in endpoint 1. | ||||
|  */ | ||||
| #define USB_CFG_HAVE_INTRIN_ENDPOINT3   0 | ||||
| /* Define this to 1 if you want to compile a version with three endpoints: The | ||||
|  * default control endpoint 0, an interrupt-in endpoint 1 and an interrupt-in | ||||
|  * endpoint 3. You must also enable endpoint 1 above. | ||||
|  */ | ||||
| #define USB_CFG_IMPLEMENT_HALT          0 | ||||
| /* Define this to 1 if you also want to implement the ENDPOINT_HALT feature | ||||
|  * for endpoint 1 (interrupt endpoint). Although you may not need this feature, | ||||
|  * it is required by the standard. We have made it a config option because it | ||||
|  * bloats the code considerably. | ||||
|  */ | ||||
| #define USB_CFG_INTR_POLL_INTERVAL      20 | ||||
| /* If you compile a version with endpoint 1 (interrupt-in), this is the poll | ||||
|  * interval. The value is in milliseconds and must not be less than 10 ms for | ||||
|  * low speed devices. | ||||
|  */ | ||||
| #define USB_CFG_IS_SELF_POWERED         0 | ||||
| /* Define this to 1 if the device has its own power supply. Set it to 0 if the | ||||
|  * device is powered from the USB bus. | ||||
|  */ | ||||
| #define USB_CFG_MAX_BUS_POWER           100 | ||||
| /* Set this variable to the maximum USB bus power consumption of your device. | ||||
|  * The value is in milliamperes. [It will be divided by two since USB | ||||
|  * communicates power requirements in units of 2 mA.] | ||||
|  */ | ||||
| #define USB_CFG_SAMPLE_EXACT            1 | ||||
| /* This variable affects Sampling Jitter for USB receiving. When it is 0, the | ||||
|  * driver guarantees a sampling window of 1/2 bit. The USB spec requires | ||||
|  * that the receiver has at most 1/4 bit sampling window. The 1/2 bit window | ||||
|  * should still work reliably enough because we work at low speed. If you want | ||||
|  * to meet the spec, set this value to 1. This will unroll a loop which | ||||
|  * results in bigger code size. | ||||
|  * If you have problems with long cables, try setting this value to 1. | ||||
|  */ | ||||
| #define USB_CFG_IMPLEMENT_FN_WRITE      0 | ||||
| /* Set this to 1 if you want usbFunctionWrite() to be called for control-out | ||||
|  * transfers. Set it to 0 if you don't need it and want to save a couple of | ||||
|  * bytes. | ||||
|  */ | ||||
| #define USB_CFG_IMPLEMENT_FN_READ       0 | ||||
| /* Set this to 1 if you need to send control replies which are generated | ||||
|  * "on the fly" when usbFunctionRead() is called. If you only want to send | ||||
|  * data from a static buffer, set it to 0 and return the data from | ||||
|  * usbFunctionSetup(). This saves a couple of bytes. | ||||
|  */ | ||||
| #define USB_CFG_IMPLEMENT_FN_WRITEOUT   0 | ||||
| /* Define this to 1 if you want to use interrupt-out (or bulk out) endpoint 1. | ||||
|  * You must implement the function usbFunctionWriteOut() which receives all | ||||
|  * interrupt/bulk data sent to endpoint 1. | ||||
|  */ | ||||
| #define USB_CFG_HAVE_FLOWCONTROL        0 | ||||
| /* Define this to 1 if you want flowcontrol over USB data. See the definition | ||||
|  * of the macros usbDisableAllRequests() and usbEnableAllRequests() in | ||||
|  * usbdrv.h. | ||||
|  */ | ||||
|  | ||||
| /* -------------------------- Device Description --------------------------- */ | ||||
|  | ||||
| #define  USB_CFG_VENDOR_ID       0xc0, 0x16 | ||||
| /* USB vendor ID for the device, low byte first. If you have registered your | ||||
|  * own Vendor ID, define it here. Otherwise you use obdev's free shared | ||||
|  * VID/PID pair. Be sure to read USBID-License.txt for rules! | ||||
|  * This template uses obdev's shared VID/PID pair for HIDs: 0x16c0/0x5df. | ||||
|  * Use this VID/PID pair ONLY if you understand the implications! | ||||
|  */ | ||||
| #define  USB_CFG_DEVICE_ID       0xdf, 0x05 | ||||
| /* This is the ID of the product, low byte first. It is interpreted in the | ||||
|  * scope of the vendor ID. If you have registered your own VID with usb.org | ||||
|  * or if you have licensed a PID from somebody else, define it here. Otherwise | ||||
|  * you use obdev's free shared VID/PID pair. Be sure to read the rules in | ||||
|  * USBID-License.txt! | ||||
|  * This template uses obdev's shared VID/PID pair for HIDs: 0x16c0/0x5df. | ||||
|  * Use this VID/PID pair ONLY if you understand the implications! | ||||
|  */ | ||||
| #define USB_CFG_DEVICE_VERSION  0x00, 0x01 | ||||
| /* Version number of the device: Minor number first, then major number. | ||||
|  */ | ||||
| #define USB_CFG_VENDOR_NAME     'w', 'w', 'w', '.', 'o', 'b', 'd', 'e', 'v', '.', 'a', 't' | ||||
| #define USB_CFG_VENDOR_NAME_LEN 12 | ||||
| /* These two values define the vendor name returned by the USB device. The name | ||||
|  * must be given as a list of characters under single quotes. The characters | ||||
|  * are interpreted as Unicode (UTF-16) entities. | ||||
|  * If you don't want a vendor name string, undefine these macros. | ||||
|  * ALWAYS define a vendor name containing your Internet domain name if you use | ||||
|  * obdev's free shared VID/PID pair. See the file USBID-License.txt for | ||||
|  * details. | ||||
|  */ | ||||
| #define USB_CFG_DEVICE_NAME     'T', 'e', 'm', 'p', 'l', 'a', 't', 'e' | ||||
| #define USB_CFG_DEVICE_NAME_LEN 8 | ||||
| /* Same as above for the device name. If you don't want a device name, undefine | ||||
|  * the macros. See the file USBID-License.txt before you assign a name if you | ||||
|  * use a shared VID/PID. | ||||
|  */ | ||||
| /*#define USB_CFG_SERIAL_NUMBER   'N', 'o', 'n', 'e' */ | ||||
| /*#define USB_CFG_SERIAL_NUMBER_LEN   0 */ | ||||
| /* Same as above for the serial number. If you don't want a serial number, | ||||
|  * undefine the macros. | ||||
|  * It may be useful to provide the serial number through other means than at | ||||
|  * compile time. See the section about descriptor properties below for how | ||||
|  * to fine tune control over USB descriptors such as the string descriptor | ||||
|  * for the serial number. | ||||
|  */ | ||||
| #define USB_CFG_DEVICE_CLASS        0 | ||||
| #define USB_CFG_DEVICE_SUBCLASS     0 | ||||
| /* See USB specification if you want to conform to an existing device class. | ||||
|  */ | ||||
| #define USB_CFG_INTERFACE_CLASS     3   /* HID */ | ||||
| #define USB_CFG_INTERFACE_SUBCLASS  0 | ||||
| #define USB_CFG_INTERFACE_PROTOCOL  0 | ||||
| /* See USB specification if you want to conform to an existing device class or | ||||
|  * protocol. | ||||
|  * This template defines a HID class device. If you implement a vendor class | ||||
|  * device, set USB_CFG_INTERFACE_CLASS to 0 and USB_CFG_DEVICE_CLASS to 0xff. | ||||
|  */ | ||||
| #define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH    42  /* total length of report descriptor */ | ||||
| /* Define this to the length of the HID report descriptor, if you implement | ||||
|  * an HID device. Otherwise don't define it or define it to 0. | ||||
|  * Since this template defines a HID device, it must also specify a HID | ||||
|  * report descriptor length. You must add a PROGMEM character array named | ||||
|  * "usbHidReportDescriptor" to your code which contains the report descriptor. | ||||
|  * Don't forget to keep the array and this define in sync! | ||||
|  */ | ||||
|  | ||||
| /* ------------------- Fine Control over USB Descriptors ------------------- */ | ||||
| /* If you don't want to use the driver's default USB descriptors, you can | ||||
|  * provide our own. These can be provided as (1) fixed length static data in | ||||
|  * flash memory, (2) fixed length static data in RAM or (3) dynamically at | ||||
|  * runtime in the function usbFunctionDescriptor(). See usbdrv.h for more | ||||
|  * information about this function. | ||||
|  * Descriptor handling is configured through the descriptor's properties. If | ||||
|  * no properties are defined or if they are 0, the default descriptor is used. | ||||
|  * Possible properties are: | ||||
|  *   + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched | ||||
|  *     at runtime via usbFunctionDescriptor(). | ||||
|  *   + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found | ||||
|  *     in static memory is in RAM, not in flash memory. | ||||
|  *   + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash), | ||||
|  *     the driver must know the descriptor's length. The descriptor itself is | ||||
|  *     found at the address of a well known identifier (see below). | ||||
|  * List of static descriptor names (must be declared PROGMEM if in flash): | ||||
|  *   char usbDescriptorDevice[]; | ||||
|  *   char usbDescriptorConfiguration[]; | ||||
|  *   char usbDescriptorHidReport[]; | ||||
|  *   char usbDescriptorString0[]; | ||||
|  *   int usbDescriptorStringVendor[]; | ||||
|  *   int usbDescriptorStringDevice[]; | ||||
|  *   int usbDescriptorStringSerialNumber[]; | ||||
|  * Other descriptors can't be provided statically, they must be provided | ||||
|  * dynamically at runtime. | ||||
|  * | ||||
|  * Descriptor properties are or-ed or added together, e.g.: | ||||
|  * #define USB_CFG_DESCR_PROPS_DEVICE   (USB_PROP_IS_RAM | USB_PROP_LENGTH(18)) | ||||
|  * | ||||
|  * The following descriptors are defined: | ||||
|  *   USB_CFG_DESCR_PROPS_DEVICE | ||||
|  *   USB_CFG_DESCR_PROPS_CONFIGURATION | ||||
|  *   USB_CFG_DESCR_PROPS_STRINGS | ||||
|  *   USB_CFG_DESCR_PROPS_STRING_0 | ||||
|  *   USB_CFG_DESCR_PROPS_STRING_VENDOR | ||||
|  *   USB_CFG_DESCR_PROPS_STRING_PRODUCT | ||||
|  *   USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER | ||||
|  *   USB_CFG_DESCR_PROPS_HID | ||||
|  *   USB_CFG_DESCR_PROPS_HID_REPORT | ||||
|  *   USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver) | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #define USB_CFG_DESCR_PROPS_DEVICE                  0 | ||||
| #define USB_CFG_DESCR_PROPS_CONFIGURATION           0 | ||||
| #define USB_CFG_DESCR_PROPS_STRINGS                 0 | ||||
| #define USB_CFG_DESCR_PROPS_STRING_0                0 | ||||
| #define USB_CFG_DESCR_PROPS_STRING_VENDOR           0 | ||||
| #define USB_CFG_DESCR_PROPS_STRING_PRODUCT          0 | ||||
| #define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER    0 | ||||
| #define USB_CFG_DESCR_PROPS_HID                     0 | ||||
| #define USB_CFG_DESCR_PROPS_HID_REPORT              0 | ||||
| #define USB_CFG_DESCR_PROPS_UNKNOWN                 0 | ||||
|  | ||||
| /* ----------------------- Optional MCU Description ------------------------ */ | ||||
|  | ||||
| /* The following configurations have working defaults in usbdrv.h. You | ||||
|  * usually don't need to set them explicitly. Only if you want to run | ||||
|  * the driver on a device which is not yet supported or with a compiler | ||||
|  * which is not fully supported (such as IAR C) or if you use a differnt | ||||
|  * interrupt than INT0, you may have to define some of these. | ||||
|  */ | ||||
| /* #define USB_INTR_CFG            MCUCR */ | ||||
| /* #define USB_INTR_CFG_SET        ((1 << ISC00) | (1 << ISC01)) */ | ||||
| /* #define USB_INTR_CFG_CLR        0 */ | ||||
| /* #define USB_INTR_ENABLE         GIMSK */ | ||||
| /* #define USB_INTR_ENABLE_BIT     INT0 */ | ||||
| /* #define USB_INTR_PENDING        GIFR */ | ||||
| /* #define USB_INTR_PENDING_BIT    INTF0 */ | ||||
|  | ||||
| #endif /* __usbconfig_h_included__ */ | ||||
							
								
								
									
										572
									
								
								firmware/usbdrv/usbdrv.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										572
									
								
								firmware/usbdrv/usbdrv.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,572 @@ | ||||
| /* Name: usbdrv.c | ||||
|  * Project: AVR USB driver | ||||
|  * Author: Christian Starkjohann | ||||
|  * Creation Date: 2004-12-29 | ||||
|  * Tabsize: 4 | ||||
|  * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | ||||
|  * License: Proprietary, free under certain conditions. See Documentation. | ||||
|  * This Revision: $Id: usbdrv.c,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  */ | ||||
|  | ||||
| #include "iarcompat.h" | ||||
| #ifndef __IAR_SYSTEMS_ICC__ | ||||
| #   include <avr/io.h> | ||||
| #   include <avr/pgmspace.h> | ||||
| #endif | ||||
| #include "usbdrv.h" | ||||
| #include "oddebug.h" | ||||
|  | ||||
| /* | ||||
| General Description: | ||||
| This module implements the C-part of the USB driver. See usbdrv.h for a | ||||
| documentation of the entire driver. | ||||
| */ | ||||
|  | ||||
| #ifndef IAR_SECTION | ||||
| #define IAR_SECTION(arg) | ||||
| #define __no_init | ||||
| #endif | ||||
| /* The macro IAR_SECTION is a hack to allow IAR-cc compatibility. On gcc, it | ||||
|  * is defined to nothing. __no_init is required on IAR. | ||||
|  */ | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| /* raw USB registers / interface to assembler code: */ | ||||
| /* usbRxBuf MUST be in 1 byte addressable range (because usbInputBuf is only 1 byte) */ | ||||
| __no_init uchar usbRxBuf[2][USB_BUFSIZE] __attribute__ ((section (USB_BUFFER_SECTION))) IAR_SECTION(USB_BUFFER_SECTION);/* raw RX buffer: PID, 8 bytes data, 2 bytes CRC */ | ||||
| uchar       usbDeviceAddr;      /* assigned during enumeration, defaults to 0 */ | ||||
| uchar       usbNewDeviceAddr;   /* device ID which should be set after status phase */ | ||||
| uchar       usbConfiguration;   /* currently selected configuration. Administered by driver, but not used */ | ||||
| uchar       usbInputBuf;        /* ptr to raw buffer used for receiving */ | ||||
| uchar       usbAppBuf;          /* ptr to raw buffer passed to app for processing */ | ||||
| volatile schar usbRxLen;        /* = 0; number of bytes in usbAppBuf; 0 means free */ | ||||
| uchar       usbCurrentTok;      /* last token received, if more than 1 rx endpoint: MSb=endpoint */ | ||||
| uchar       usbRxToken;         /* token for data we received; if more than 1 rx endpoint: MSb=endpoint */ | ||||
| uchar       usbMsgLen = 0xff;   /* remaining number of bytes, no msg to send if -1 (see usbMsgPtr) */ | ||||
| volatile uchar usbTxLen = USBPID_NAK;   /* number of bytes to transmit with next IN token or handshake token */ | ||||
| uchar       usbTxBuf[USB_BUFSIZE];/* data to transmit with next IN, free if usbTxLen contains handshake token */ | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT | ||||
| volatile uchar usbTxLen1 = USBPID_NAK;  /* TX count for endpoint 1 */ | ||||
| uchar       usbTxBuf1[USB_BUFSIZE];     /* TX data for endpoint 1 */ | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT3 | ||||
| volatile uchar usbTxLen3 = USBPID_NAK;  /* TX count for endpoint 1 */ | ||||
| uchar       usbTxBuf3[USB_BUFSIZE];     /* TX data for endpoint 1 */ | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| /* USB status registers / not shared with asm code */ | ||||
| uchar           *usbMsgPtr;     /* data to transmit next -- ROM or RAM address */ | ||||
| static uchar    usbMsgFlags;    /* flag values see below */ | ||||
| static uchar    usbIsReset;     /* = 0; USB bus is in reset phase */ | ||||
|  | ||||
| #define USB_FLG_TX_PACKET       (1<<0) | ||||
| /* Leave free 6 bits after TX_PACKET. This way we can increment usbMsgFlags to toggle TX_PACKET */ | ||||
| #define USB_FLG_MSGPTR_IS_ROM   (1<<6) | ||||
| #define USB_FLG_USE_DEFAULT_RW  (1<<7) | ||||
|  | ||||
| /* | ||||
| optimizing hints: | ||||
| - do not post/pre inc/dec integer values in operations | ||||
| - assign value of PRG_RDB() to register variables and don't use side effects in arg | ||||
| - use narrow scope for variables which should be in X/Y/Z register | ||||
| - assign char sized expressions to variables to force 8 bit arithmetics | ||||
| */ | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| #if USB_CFG_DESCR_PROPS_STRINGS == 0 | ||||
|  | ||||
| #if USB_CFG_DESCR_PROPS_STRING_0 == 0 | ||||
| #undef USB_CFG_DESCR_PROPS_STRING_0 | ||||
| #define USB_CFG_DESCR_PROPS_STRING_0    sizeof(usbDescriptorString0) | ||||
| PROGMEM char usbDescriptorString0[] = { /* language descriptor */ | ||||
|     4,          /* sizeof(usbDescriptorString0): length of descriptor in bytes */ | ||||
|     3,          /* descriptor type */ | ||||
|     0x09, 0x04, /* language index (0x0409 = US-English) */ | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| #if USB_CFG_DESCR_PROPS_STRING_VENDOR == 0 && USB_CFG_VENDOR_NAME_LEN | ||||
| #undef USB_CFG_DESCR_PROPS_STRING_VENDOR | ||||
| #define USB_CFG_DESCR_PROPS_STRING_VENDOR   sizeof(usbDescriptorStringVendor) | ||||
| PROGMEM int  usbDescriptorStringVendor[] = { | ||||
|     USB_STRING_DESCRIPTOR_HEADER(USB_CFG_VENDOR_NAME_LEN), | ||||
|     USB_CFG_VENDOR_NAME | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| #if USB_CFG_DESCR_PROPS_STRING_DEVICE == 0 && USB_CFG_DEVICE_NAME_LEN | ||||
| #undef USB_CFG_DESCR_PROPS_STRING_DEVICE | ||||
| #define USB_CFG_DESCR_PROPS_STRING_DEVICE   sizeof(usbDescriptorStringDevice) | ||||
| PROGMEM int  usbDescriptorStringDevice[] = { | ||||
|     USB_STRING_DESCRIPTOR_HEADER(USB_CFG_DEVICE_NAME_LEN), | ||||
|     USB_CFG_DEVICE_NAME | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| #if USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER == 0 && USB_CFG_SERIAL_NUMBER_LEN | ||||
| #undef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER | ||||
| #define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER    sizeof(usbDescriptorStringSerialNumber) | ||||
| PROGMEM int usbDescriptorStringSerialNumber[] = { | ||||
|     USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LEN), | ||||
|     USB_CFG_SERIAL_NUMBER | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| #endif  /* USB_CFG_DESCR_PROPS_STRINGS == 0 */ | ||||
|  | ||||
| #if USB_CFG_DESCR_PROPS_DEVICE == 0 | ||||
| #undef USB_CFG_DESCR_PROPS_DEVICE | ||||
| #define USB_CFG_DESCR_PROPS_DEVICE  sizeof(usbDescriptorDevice) | ||||
| PROGMEM char usbDescriptorDevice[] = {    /* USB device descriptor */ | ||||
|     18,         /* sizeof(usbDescriptorDevice): length of descriptor in bytes */ | ||||
|     USBDESCR_DEVICE,        /* descriptor type */ | ||||
|     0x01, 0x01,             /* USB version supported */ | ||||
|     USB_CFG_DEVICE_CLASS, | ||||
|     USB_CFG_DEVICE_SUBCLASS, | ||||
|     0,                      /* protocol */ | ||||
|     8,                      /* max packet size */ | ||||
|     USB_CFG_VENDOR_ID,      /* 2 bytes */ | ||||
|     USB_CFG_DEVICE_ID,      /* 2 bytes */ | ||||
|     USB_CFG_DEVICE_VERSION, /* 2 bytes */ | ||||
|     USB_CFG_DESCR_PROPS_STRING_VENDOR != 0 ? 1 : 0,         /* manufacturer string index */ | ||||
|     USB_CFG_DESCR_PROPS_STRING_DEVICE != 0 ? 2 : 0,         /* product string index */ | ||||
|     USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER != 0 ? 3 : 0,  /* serial number string index */ | ||||
|     1,          /* number of configurations */ | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| #if USB_CFG_DESCR_PROPS_HID_REPORT != 0 && USB_CFG_DESCR_PROPS_HID == 0 | ||||
| #undef USB_CFG_DESCR_PROPS_HID | ||||
| #define USB_CFG_DESCR_PROPS_HID     9   /* length of HID descriptor in config descriptor below */ | ||||
| #endif | ||||
|  | ||||
| #if USB_CFG_DESCR_PROPS_CONFIGURATION == 0 | ||||
| #undef USB_CFG_DESCR_PROPS_CONFIGURATION | ||||
| #define USB_CFG_DESCR_PROPS_CONFIGURATION   sizeof(usbDescriptorConfiguration) | ||||
| PROGMEM char usbDescriptorConfiguration[] = {    /* USB configuration descriptor */ | ||||
|     9,          /* sizeof(usbDescriptorConfiguration): length of descriptor in bytes */ | ||||
|     USBDESCR_CONFIG,    /* descriptor type */ | ||||
|     18 + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT + (USB_CFG_DESCR_PROPS_HID & 0xff), 0, | ||||
|                 /* total length of data returned (including inlined descriptors) */ | ||||
|     1,          /* number of interfaces in this configuration */ | ||||
|     1,          /* index of this configuration */ | ||||
|     0,          /* configuration name string index */ | ||||
| #if USB_CFG_IS_SELF_POWERED | ||||
|     USBATTR_SELFPOWER,  /* attributes */ | ||||
| #else | ||||
|     USBATTR_BUSPOWER,   /* attributes */ | ||||
| #endif | ||||
|     USB_CFG_MAX_BUS_POWER/2,            /* max USB current in 2mA units */ | ||||
| /* interface descriptor follows inline: */ | ||||
|     9,          /* sizeof(usbDescrInterface): length of descriptor in bytes */ | ||||
|     USBDESCR_INTERFACE, /* descriptor type */ | ||||
|     0,          /* index of this interface */ | ||||
|     0,          /* alternate setting for this interface */ | ||||
|     USB_CFG_HAVE_INTRIN_ENDPOINT,   /* endpoints excl 0: number of endpoint descriptors to follow */ | ||||
|     USB_CFG_INTERFACE_CLASS, | ||||
|     USB_CFG_INTERFACE_SUBCLASS, | ||||
|     USB_CFG_INTERFACE_PROTOCOL, | ||||
|     0,          /* string index for interface */ | ||||
| #if (USB_CFG_DESCR_PROPS_HID & 0xff)    /* HID descriptor */ | ||||
|     9,          /* sizeof(usbDescrHID): length of descriptor in bytes */ | ||||
|     USBDESCR_HID,   /* descriptor type: HID */ | ||||
|     0x01, 0x01, /* BCD representation of HID version */ | ||||
|     0x00,       /* target country code */ | ||||
|     0x01,       /* number of HID Report (or other HID class) Descriptor infos to follow */ | ||||
|     0x22,       /* descriptor type: report */ | ||||
|     USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH, 0,  /* total length of report descriptor */ | ||||
| #endif | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT    /* endpoint descriptor for endpoint 1 */ | ||||
|     7,          /* sizeof(usbDescrEndpoint) */ | ||||
|     USBDESCR_ENDPOINT,  /* descriptor type = endpoint */ | ||||
|     0x81,       /* IN endpoint number 1 */ | ||||
|     0x03,       /* attrib: Interrupt endpoint */ | ||||
|     8, 0,       /* maximum packet size */ | ||||
|     USB_CFG_INTR_POLL_INTERVAL, /* in ms */ | ||||
| #endif | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| /* We don't use prog_int or prog_int16_t for compatibility with various libc | ||||
|  * versions. Here's an other compatibility hack: | ||||
|  */ | ||||
| #ifndef PRG_RDB | ||||
| #define PRG_RDB(addr)   pgm_read_byte(addr) | ||||
| #endif | ||||
|  | ||||
| typedef union{ | ||||
|     unsigned    word; | ||||
|     uchar       *ptr; | ||||
|     uchar       bytes[2]; | ||||
| }converter_t; | ||||
| /* We use this union to do type conversions. This is better optimized than | ||||
|  * type casts in gcc 3.4.3 and much better than using bit shifts to build | ||||
|  * ints from chars. Byte ordering is not a problem on an 8 bit platform. | ||||
|  */ | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT | ||||
| uchar   usbTxPacketCnt1; | ||||
|  | ||||
| void    usbSetInterrupt(uchar *data, uchar len) | ||||
| { | ||||
| uchar       *p, i; | ||||
|  | ||||
| #if USB_CFG_IMPLEMENT_HALT | ||||
|     if(usbTxLen1 == USBPID_STALL) | ||||
|         return; | ||||
| #endif | ||||
| #if 0   /* No runtime checks! Caller is responsible for valid data! */ | ||||
|     if(len > 8) /* interrupt transfers are limited to 8 bytes */ | ||||
|         len = 8; | ||||
| #endif | ||||
|     i = USBPID_DATA1; | ||||
|     if(usbTxPacketCnt1 & 1) | ||||
|         i = USBPID_DATA0; | ||||
|     if(usbTxLen1 & 0x10){       /* packet buffer was empty */ | ||||
|         usbTxPacketCnt1++; | ||||
|     }else{ | ||||
|         usbTxLen1 = USBPID_NAK; /* avoid sending incomplete interrupt data */ | ||||
|     } | ||||
|     p = usbTxBuf1; | ||||
|     *p++ = i; | ||||
|     for(i=len;i--;) | ||||
|         *p++ = *data++; | ||||
|     usbCrc16Append(&usbTxBuf1[1], len); | ||||
|     usbTxLen1 = len + 4;    /* len must be given including sync byte */ | ||||
|     DBG2(0x21, usbTxBuf1, len + 3); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT3 | ||||
| uchar   usbTxPacketCnt3; | ||||
|  | ||||
| void    usbSetInterrupt3(uchar *data, uchar len) | ||||
| { | ||||
| uchar       *p, i; | ||||
|  | ||||
|     i = USBPID_DATA1; | ||||
|     if(usbTxPacketCnt3 & 1) | ||||
|         i = USBPID_DATA0; | ||||
|     if(usbTxLen3 & 0x10){       /* packet buffer was empty */ | ||||
|         usbTxPacketCnt3++; | ||||
|     }else{ | ||||
|         usbTxLen3 = USBPID_NAK; /* avoid sending incomplete interrupt data */ | ||||
|     } | ||||
|     p = usbTxBuf3; | ||||
|     *p++ = i; | ||||
|     for(i=len;i--;) | ||||
|         *p++ = *data++; | ||||
|     usbCrc16Append(&usbTxBuf3[1], len); | ||||
|     usbTxLen3 = len + 4;    /* len must be given including sync byte */ | ||||
|     DBG2(0x23, usbTxBuf3, len + 3); | ||||
| } | ||||
| #endif | ||||
|  | ||||
|  | ||||
| static uchar    usbRead(uchar *data, uchar len) | ||||
| { | ||||
| #if USB_CFG_IMPLEMENT_FN_READ | ||||
|     if(usbMsgFlags & USB_FLG_USE_DEFAULT_RW){ | ||||
| #endif | ||||
|         uchar i = len, *r = usbMsgPtr; | ||||
|         if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){    /* ROM data */ | ||||
|             while(i--){ | ||||
|                 uchar c = PRG_RDB(r);    /* assign to char size variable to enforce byte ops */ | ||||
|                 *data++ = c; | ||||
|                 r++; | ||||
|             } | ||||
|         }else{                  /* RAM data */ | ||||
|             while(i--) | ||||
|                 *data++ = *r++; | ||||
|         } | ||||
|         usbMsgPtr = r; | ||||
|         return len; | ||||
| #if USB_CFG_IMPLEMENT_FN_READ | ||||
|     }else{ | ||||
|         if(len != 0)    /* don't bother app with 0 sized reads */ | ||||
|             return usbFunctionRead(data, len); | ||||
|         return 0; | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | ||||
|  | ||||
| #define GET_DESCRIPTOR(cfgProp, staticName)         \ | ||||
|     if(cfgProp){                                    \ | ||||
|         if((cfgProp) & USB_PROP_IS_RAM)             \ | ||||
|             flags &= ~USB_FLG_MSGPTR_IS_ROM;        \ | ||||
|         if((cfgProp) & USB_PROP_IS_DYNAMIC){        \ | ||||
|             replyLen = usbFunctionDescriptor(rq);   \ | ||||
|         }else{                                      \ | ||||
|             replyData = (uchar *)(staticName);      \ | ||||
|             SET_REPLY_LEN((cfgProp) & 0xff);        \ | ||||
|         }                                           \ | ||||
|     } | ||||
| /* We use if() instead of #if in the macro above because #if can't be used | ||||
|  * in macros and the compiler optimizes constant conditions anyway. | ||||
|  */ | ||||
|  | ||||
|  | ||||
| /* Don't make this function static to avoid inlining. | ||||
|  * The entire function would become too large and exceed the range of | ||||
|  * relative jumps. | ||||
|  * 2006-02-25: Either gcc 3.4.3 is better than the gcc used when the comment | ||||
|  * above was written, or other parts of the code have changed. We now get | ||||
|  * better results with an inlined function. Test condition: PowerSwitch code. | ||||
|  */ | ||||
| static void usbProcessRx(uchar *data, uchar len) | ||||
| { | ||||
| usbRequest_t    *rq = (void *)data; | ||||
| uchar           replyLen = 0, flags = USB_FLG_USE_DEFAULT_RW; | ||||
| /* We use if() cascades because the compare is done byte-wise while switch() | ||||
|  * is int-based. The if() cascades are therefore more efficient. | ||||
|  */ | ||||
|     DBG2(0x10 + ((usbRxToken >> 6) & 3), data, len); | ||||
| #if USB_CFG_IMPLEMENT_FN_WRITEOUT | ||||
|     if(usbRxToken & 0x80){ | ||||
|         usbFunctionWriteOut(data, len); | ||||
|         return; /* no reply expected, hence no usbMsgPtr, usbMsgFlags, usbMsgLen set */ | ||||
|     } | ||||
|     if(usbRxToken == (uchar)(USBPID_SETUP & 0x7f)){ /* MSb contains endpoint (== 0) */ | ||||
| #else | ||||
|     if(usbRxToken == (uchar)USBPID_SETUP){ | ||||
| #endif | ||||
|         if(len == 8){   /* Setup size must be always 8 bytes. Ignore otherwise. */ | ||||
|             uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; | ||||
|             if(type == USBRQ_TYPE_STANDARD){ | ||||
|                 #define SET_REPLY_LEN(len)  replyLen = (len); usbMsgPtr = replyData | ||||
|                 /* This macro ensures that replyLen and usbMsgPtr are always set in the same way. | ||||
|                  * That allows optimization of common code in if() branches */ | ||||
|                 uchar *replyData = usbTxBuf + 9; /* there is 3 bytes free space at the end of the buffer */ | ||||
|                 replyData[0] = 0;   /* common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ | ||||
|                 if(rq->bRequest == USBRQ_GET_STATUS){           /* 0 */ | ||||
|                     uchar __attribute__((__unused__)) recipient = rq->bmRequestType & USBRQ_RCPT_MASK;  /* assign arith ops to variables to enforce byte size */ | ||||
| #if USB_CFG_IS_SELF_POWERED | ||||
|                     if(recipient == USBRQ_RCPT_DEVICE) | ||||
|                         replyData[0] =  USB_CFG_IS_SELF_POWERED; | ||||
| #endif | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT && USB_CFG_IMPLEMENT_HALT | ||||
|                     if(recipient == USBRQ_RCPT_ENDPOINT && rq->wIndex.bytes[0] == 0x81)   /* request status for endpoint 1 */ | ||||
|                         replyData[0] = usbTxLen1 == USBPID_STALL; | ||||
| #endif | ||||
|                     replyData[1] = 0; | ||||
|                     SET_REPLY_LEN(2); | ||||
|                 }else if(rq->bRequest == USBRQ_SET_ADDRESS){    /* 5 */ | ||||
|                     usbNewDeviceAddr = rq->wValue.bytes[0]; | ||||
|                 }else if(rq->bRequest == USBRQ_GET_DESCRIPTOR){ /* 6 */ | ||||
|                     flags = USB_FLG_MSGPTR_IS_ROM | USB_FLG_USE_DEFAULT_RW; | ||||
|                     if(rq->wValue.bytes[1] == USBDESCR_DEVICE){ /* 1 */ | ||||
|                         GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice) | ||||
|                     }else if(rq->wValue.bytes[1] == USBDESCR_CONFIG){   /* 2 */ | ||||
|                         GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration) | ||||
|                     }else if(rq->wValue.bytes[1] == USBDESCR_STRING){   /* 3 */ | ||||
| #if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC | ||||
|                         if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM) | ||||
|                             flags &= ~USB_FLG_MSGPTR_IS_ROM; | ||||
|                         replyLen = usbFunctionDescriptor(rq); | ||||
| #else   /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ | ||||
|                         if(rq->wValue.bytes[0] == 0){   /* descriptor index */ | ||||
|                             GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0) | ||||
|                         }else if(rq->wValue.bytes[0] == 1){ | ||||
|                             GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor) | ||||
|                         }else if(rq->wValue.bytes[0] == 2){ | ||||
|                             GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_DEVICE, usbDescriptorStringDevice) | ||||
|                         }else if(rq->wValue.bytes[0] == 3){ | ||||
|                             GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber) | ||||
|                         }else if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ | ||||
|                             replyLen = usbFunctionDescriptor(rq); | ||||
|                         } | ||||
| #endif  /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ | ||||
|                     }else if(rq->wValue.bytes[1] == USBDESCR_HID){          /* 0x21 */ | ||||
|                         GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18) | ||||
|                     }else if(rq->wValue.bytes[1] == USBDESCR_HID_REPORT){   /* 0x22 */ | ||||
|                         GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport) | ||||
|                     }else if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ | ||||
|                         replyLen = usbFunctionDescriptor(rq); | ||||
|                     } | ||||
|                 }else if(rq->bRequest == USBRQ_GET_CONFIGURATION){  /* 8 */ | ||||
|                     replyData = &usbConfiguration;  /* send current configuration value */ | ||||
|                     SET_REPLY_LEN(1); | ||||
|                 }else if(rq->bRequest == USBRQ_SET_CONFIGURATION){  /* 9 */ | ||||
|                     usbConfiguration = rq->wValue.bytes[0]; | ||||
| #if USB_CFG_IMPLEMENT_HALT | ||||
|                     usbTxLen1 = USBPID_NAK; | ||||
| #endif | ||||
|                 }else if(rq->bRequest == USBRQ_GET_INTERFACE){      /* 10 */ | ||||
|                     SET_REPLY_LEN(1); | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT | ||||
|                 }else if(rq->bRequest == USBRQ_SET_INTERFACE){      /* 11 */ | ||||
|                     usbTxPacketCnt1 = 0;        /* reset data toggling for interrupt endpoint */ | ||||
| #   if USB_CFG_HAVE_INTRIN_ENDPOINT3 | ||||
|                     usbTxPacketCnt3 = 0;        /* reset data toggling for interrupt endpoint */ | ||||
| #   endif | ||||
| #   if USB_CFG_IMPLEMENT_HALT | ||||
|                     usbTxLen1 = USBPID_NAK; | ||||
|                 }else if(rq->bRequest == USBRQ_CLEAR_FEATURE || rq->bRequest == USBRQ_SET_FEATURE){   /* 1|3 */ | ||||
|                     if(rq->wValue.bytes[0] == 0 && rq->wIndex.bytes[0] == 0x81){   /* feature 0 == HALT for endpoint == 1 */ | ||||
|                         usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL; | ||||
|                         usbTxPacketCnt1 = 0;    /* reset data toggling for interrupt endpoint */ | ||||
| #       if USB_CFG_HAVE_INTRIN_ENDPOINT3 | ||||
|                         usbTxPacketCnt3 = 0;    /* reset data toggling for interrupt endpoint */ | ||||
| #       endif | ||||
|                     } | ||||
| #   endif | ||||
| #endif | ||||
|                 }else{ | ||||
|                     /* the following requests can be ignored, send default reply */ | ||||
|                     /* 1: CLEAR_FEATURE, 3: SET_FEATURE, 7: SET_DESCRIPTOR */ | ||||
|                     /* 12: SYNCH_FRAME */ | ||||
|                 } | ||||
|                 #undef SET_REPLY_LEN | ||||
|             }else{  /* not a standard request -- must be vendor or class request */ | ||||
|                 replyLen = usbFunctionSetup(data); | ||||
|             } | ||||
| #if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE | ||||
|             if(replyLen == 0xff){   /* use user-supplied read/write function */ | ||||
|                 if((rq->bmRequestType & USBRQ_DIR_MASK) == USBRQ_DIR_DEVICE_TO_HOST){ | ||||
|                     replyLen = rq->wLength.bytes[0];    /* IN transfers only */ | ||||
|                 } | ||||
|                 flags &= ~USB_FLG_USE_DEFAULT_RW;  /* we have no valid msg, use user supplied read/write functions */ | ||||
|             }else   /* The 'else' prevents that we limit a replyLen of 0xff to the maximum transfer len. */ | ||||
| #endif | ||||
|             if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0])  /* limit length to max */ | ||||
|                 replyLen = rq->wLength.bytes[0]; | ||||
|         } | ||||
|         /* make sure that data packets which are sent as ACK to an OUT transfer are always zero sized */ | ||||
|     }else{  /* DATA packet from out request */ | ||||
| #if USB_CFG_IMPLEMENT_FN_WRITE | ||||
|         if(!(usbMsgFlags & USB_FLG_USE_DEFAULT_RW)){ | ||||
|             uchar rval = usbFunctionWrite(data, len); | ||||
|             replyLen = 0xff; | ||||
|             if(rval == 0xff){       /* an error occurred */ | ||||
|                 usbMsgLen = 0xff;   /* cancel potentially pending data packet for ACK */ | ||||
|                 usbTxLen = USBPID_STALL; | ||||
|             }else if(rval != 0){    /* This was the final package */ | ||||
|                 replyLen = 0;       /* answer with a zero-sized data packet */ | ||||
|             } | ||||
|             flags = 0;    /* start with a DATA1 package, stay with user supplied write() function */ | ||||
|         } | ||||
| #endif | ||||
|     } | ||||
|     usbMsgFlags = flags; | ||||
|     usbMsgLen = replyLen; | ||||
| } | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| static void usbBuildTxBlock(void) | ||||
| { | ||||
| uchar       wantLen, len, txLen, token; | ||||
|  | ||||
|     wantLen = usbMsgLen; | ||||
|     if(wantLen > 8) | ||||
|         wantLen = 8; | ||||
|     usbMsgLen -= wantLen; | ||||
|     token = USBPID_DATA1; | ||||
|     if(usbMsgFlags & USB_FLG_TX_PACKET) | ||||
|         token = USBPID_DATA0; | ||||
|     usbMsgFlags++; | ||||
|     len = usbRead(usbTxBuf + 1, wantLen); | ||||
|     if(len <= 8){           /* valid data packet */ | ||||
|         usbCrc16Append(&usbTxBuf[1], len); | ||||
|         txLen = len + 4;    /* length including sync byte */ | ||||
|         if(len < 8)         /* a partial package identifies end of message */ | ||||
|             usbMsgLen = 0xff; | ||||
|     }else{ | ||||
|         txLen = USBPID_STALL;   /* stall the endpoint */ | ||||
|         usbMsgLen = 0xff; | ||||
|     } | ||||
|     usbTxBuf[0] = token; | ||||
|     usbTxLen = txLen; | ||||
|     DBG2(0x20, usbTxBuf, txLen-1); | ||||
| } | ||||
|  | ||||
| static inline uchar isNotSE0(void) | ||||
| { | ||||
| uchar   rval; | ||||
| /* We want to do | ||||
|  *     return (USBIN & USBMASK); | ||||
|  * here, but the compiler does int-expansion acrobatics. | ||||
|  * We can avoid this by assigning to a char-sized variable. | ||||
|  */ | ||||
|     rval = USBIN & USBMASK; | ||||
|     return rval; | ||||
| } | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| void    usbPoll(void) | ||||
| { | ||||
| uchar   len; | ||||
|  | ||||
|     if((len = usbRxLen) > 0){ | ||||
| /* We could check CRC16 here -- but ACK has already been sent anyway. If you | ||||
|  * need data integrity checks with this driver, check the CRC in your app | ||||
|  * code and report errors back to the host. Since the ACK was already sent, | ||||
|  * retries must be handled on application level. | ||||
|  * unsigned crc = usbCrc16((uchar *)(unsigned)(usbAppBuf + 1), usbRxLen - 3); | ||||
|  */ | ||||
|         len -= 3;       /* remove PID and CRC */ | ||||
|         if(len < 128){  /* no overflow */ | ||||
|             converter_t appBuf; | ||||
|             appBuf.ptr = (uchar *)usbRxBuf; | ||||
|             appBuf.bytes[0] = usbAppBuf; | ||||
|             appBuf.bytes[0]++; | ||||
|             usbProcessRx(appBuf.ptr, len); | ||||
|         } | ||||
| #if USB_CFG_HAVE_FLOWCONTROL | ||||
|         if(usbRxLen > 0)    /* only mark as available if not inactivated */ | ||||
|             usbRxLen = 0; | ||||
| #else | ||||
|         usbRxLen = 0;   /* mark rx buffer as available */ | ||||
| #endif | ||||
|     } | ||||
|     if(usbMsgLen != 0xff){  /* transmit data pending? */ | ||||
|         if(usbTxLen & 0x10) /* transmit system idle */ | ||||
|             usbBuildTxBlock(); | ||||
|     } | ||||
|     if(isNotSE0()){ /* SE0 state */ | ||||
|         usbIsReset = 0; | ||||
|     }else{ | ||||
|         /* check whether SE0 lasts for more than 2.5us (3.75 bit times) */ | ||||
|         if(!usbIsReset){ | ||||
|             uchar i; | ||||
|             for(i=100;i;i--){ | ||||
|                 if(isNotSE0()) | ||||
|                     goto notUsbReset; | ||||
|             } | ||||
|             usbIsReset = 1; | ||||
|             usbNewDeviceAddr = 0; | ||||
|             usbDeviceAddr = 0; | ||||
| #if USB_CFG_IMPLEMENT_HALT | ||||
|             usbTxLen1 = USBPID_NAK; | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT3 | ||||
|             usbTxLen3 = USBPID_NAK; | ||||
| #endif | ||||
| #endif | ||||
|             DBG1(0xff, 0, 0); | ||||
| notUsbReset:; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| void    usbInit(void) | ||||
| { | ||||
|     usbInputBuf = (uchar)usbRxBuf[0]; | ||||
|     usbAppBuf = (uchar)usbRxBuf[1]; | ||||
| #if USB_INTR_CFG_SET != 0 | ||||
|     USB_INTR_CFG |= USB_INTR_CFG_SET; | ||||
| #endif | ||||
| #if USB_INTR_CFG_CLR != 0 | ||||
|     USB_INTR_CFG &= ~(USB_INTR_CFG_CLR); | ||||
| #endif | ||||
|     USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT); | ||||
| } | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
							
								
								
									
										657
									
								
								firmware/usbdrv/usbdrv.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										657
									
								
								firmware/usbdrv/usbdrv.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,657 @@ | ||||
| /* Name: usbdrv.h | ||||
|  * Project: AVR USB driver | ||||
|  * Author: Christian Starkjohann | ||||
|  * Creation Date: 2004-12-29 | ||||
|  * Tabsize: 4 | ||||
|  * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | ||||
|  * License: Proprietary, free under certain conditions. See Documentation. | ||||
|  * This Revision: $Id: usbdrv.h,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  */ | ||||
|  | ||||
| #ifndef __usbdrv_h_included__ | ||||
| #define __usbdrv_h_included__ | ||||
| #include "usbconfig.h" | ||||
| #include "iarcompat.h" | ||||
|  | ||||
| /* | ||||
| Hardware Prerequisites: | ||||
| ======================= | ||||
| USB lines D+ and D- MUST be wired to the same I/O port. D+ must (also) be | ||||
| connected to INT0. D- requires a pullup of 1.5k to +3.5V (and the device | ||||
| must be powered at 3.5V) to identify as low-speed USB device. A pullup of | ||||
| 1M SHOULD be connected from D+ to +3.5V to prevent interference when no USB | ||||
| master is connected. We use D+ as interrupt source and not D- because it | ||||
| does not trigger on keep-alive and RESET states. | ||||
|  | ||||
| As a compile time option, the 1.5k pullup resistor on D- can be made | ||||
| switchable to allow the device to disconnect at will. See the definition of | ||||
| usbDeviceConnect() and usbDeviceDisconnect() further down in this file. | ||||
|  | ||||
| Please adapt the values in usbconfig.h according to your hardware! | ||||
|  | ||||
| The device MUST be clocked at 12 MHz. This is more than the 10 MHz allowed by | ||||
| an AT90S2313 powered at 4.5V. However, if the supply voltage to maximum clock | ||||
| relation is interpolated linearly, an ATtiny2313 meets the requirement by | ||||
| specification. In practice, the AT90S2313 can be overclocked and works well. | ||||
|  | ||||
|  | ||||
| Limitations: | ||||
| ============ | ||||
| Compiling: | ||||
| You should link the usbdrv.o module first because it has special alignment | ||||
| requirements for the receive buffer (the buffer must not cross a 256 byte | ||||
| page boundary, it must not even touch it at the end). If you can't link it | ||||
| first, you must use other measures to ensure alignment. | ||||
| Note: gcc does not always assign variable addresses in the order as the modules | ||||
| are linked or the variables are declared. You can choose a memory section for | ||||
| the receive buffer with the configuration option "USB_BUFFER_SECTION". This | ||||
| option defaults to ".bss". If you use your own section, you can place it at | ||||
| an arbitrary location with a linker option similar to | ||||
| "-Wl,--section-start=.mybuffer=0x800060". Use "avr-nm -ng" on the binary and | ||||
| search for "usbRxBuf" to find tbe base address of the 22 bytes rx buffer. | ||||
|  | ||||
| Robustness with respect to communication errors: | ||||
| The driver assumes error-free communication. It DOES check for errors in | ||||
| the PID, but does NOT check bit stuffing errors, SE0 in middle of a byte, | ||||
| token CRC (5 bit) and data CRC (16 bit). CRC checks can not be performed due | ||||
| to timing constraints: We must start sending a reply within 7 bit times. | ||||
| Bit stuffing and misplaced SE0 would have to be checked in real-time, but CPU | ||||
| performance does not permit that. The driver does not check Data0/Data1 | ||||
| toggling, but application software can implement the check. | ||||
|  | ||||
| Sampling jitter: | ||||
| The driver guarantees a sampling window of 1/2 bit. The USB spec requires | ||||
| that the receiver has at most 1/4 bit sampling window. The 1/2 bit window | ||||
| should still work reliably enough because we work at low speed. If you want | ||||
| to meet the spec, define the macro "USB_CFG_SAMPLE_EXACT" to 1 in usbconfig.h. | ||||
| This will unroll a loop which results in bigger code size. | ||||
|  | ||||
| Input characteristics: | ||||
| Since no differential receiver circuit is used, electrical interference | ||||
| robustness may suffer. The driver samples only one of the data lines with | ||||
| an ordinary I/O pin's input characteristics. However, since this is only a | ||||
| low speed USB implementation and the specification allows for 8 times the | ||||
| bit rate over the same hardware, we should be on the safe side. Even the spec | ||||
| requires detection of asymmetric states at high bit rate for SE0 detection. | ||||
|  | ||||
| Number of endpoints: | ||||
| The driver supports up to four endpoints: One control endpoint (endpoint 0), | ||||
| two interrupt-in (or bulk-in) endpoints (endpoint 1 and 3) and one | ||||
| interrupt-out (or bulk-out) endpoint (endpoint 1). Please note that the USB | ||||
| standard forbids bulk endpoints for low speed devices! Most operating systems | ||||
| allow them anyway, but the AVR will spend 90% of the CPU time in the USB | ||||
| interrupt polling for bulk data. | ||||
| By default, only the control endpoint 0 is enabled. To get the other endpoints, | ||||
| define USB_CFG_HAVE_INTRIN_ENDPOINT, USB_CFG_HAVE_INTRIN_ENDPOINT3 and/or | ||||
| USB_CFG_IMPLEMENT_FN_WRITEOUT respectively (see usbconfig-prototype.h for | ||||
| details). | ||||
|  | ||||
| Maximum data payload: | ||||
| Data payload of control in and out transfers may be up to 254 bytes. In order | ||||
| to accept payload data of out transfers, you need to implement | ||||
| 'usbFunctionWrite()'. | ||||
|  | ||||
| USB Suspend Mode supply current: | ||||
| The USB standard limits power consumption to 500uA when the bus is in suspend | ||||
| mode. This is not a problem for self-powered devices since they don't need | ||||
| bus power anyway. Bus-powered devices can achieve this only by putting the | ||||
| CPU in sleep mode. The driver does not implement suspend handling by itself. | ||||
| However, the application may implement activity monitoring and wakeup from | ||||
| sleep. The host sends regular SE0 states on the bus to keep it active. These | ||||
| SE0 states can be detected by wiring the INT1 pin to D-. It is not necessary | ||||
| to enable the interrupt, checking the interrupt pending flag should suffice. | ||||
| Before entering sleep mode, the application should enable INT1 for a wakeup | ||||
| on the next bus activity. | ||||
|  | ||||
| Operation without an USB master: | ||||
| The driver behaves neutral without connection to an USB master if D- reads | ||||
| as 1. To avoid spurious interrupts, we recommend a high impedance (e.g. 1M) | ||||
| pullup resistor on D+. If D- becomes statically 0, the driver may block in | ||||
| the interrupt routine. | ||||
|  | ||||
| Interrupt latency: | ||||
| The application must ensure that the USB interrupt is not disabled for more | ||||
| than 20 cycles. This implies that all interrupt routines must either be | ||||
| declared as "INTERRUPT" instead of "SIGNAL" (see "avr/signal.h") or that they | ||||
| are written in assembler with "sei" as the first instruction. | ||||
|  | ||||
| Maximum interrupt duration / CPU cycle consumption: | ||||
| The driver handles all USB communication during the interrupt service | ||||
| routine. The routine will not return before an entire USB message is received | ||||
| and the reply is sent. This may be up to ca. 1200 cycles = 100us if the host | ||||
| conforms to the standard. The driver will consume CPU cycles for all USB | ||||
| messages, even if they address another (low-speed) device on the same bus. | ||||
|  | ||||
| */ | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
| /* --------------------------- Module Interface ---------------------------- */ | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| #define USBDRV_VERSION  20060718 | ||||
| /* This define uniquely identifies a driver version. It is a decimal number | ||||
|  * constructed from the driver's release date in the form YYYYMMDD. If the | ||||
|  * driver's behavior or interface changes, you can use this constant to | ||||
|  * distinguish versions. If it is not defined, the driver's release date is | ||||
|  * older than 2006-01-25. | ||||
|  */ | ||||
|  | ||||
| #ifndef __ASSEMBLER__ | ||||
|  | ||||
| #ifndef uchar | ||||
| #define uchar   unsigned char | ||||
| #endif | ||||
| #ifndef schar | ||||
| #define schar   signed char | ||||
| #endif | ||||
| /* shortcuts for well defined 8 bit integer types */ | ||||
|  | ||||
| struct usbRequest;  /* forward declaration */ | ||||
|  | ||||
| extern void     usbInit(void); | ||||
| /* This function must be called before interrupts are enabled and the main | ||||
|  * loop is entered. | ||||
|  */ | ||||
| extern void     usbPoll(void); | ||||
| /* This function must be called at regular intervals from the main loop. | ||||
|  * Maximum delay between calls is somewhat less than 50ms (USB timeout for | ||||
|  * accepting a Setup message). Otherwise the device will not be recognized. | ||||
|  * Please note that debug outputs through the UART take ~ 0.5ms per byte | ||||
|  * at 19200 bps. | ||||
|  */ | ||||
| extern uchar    *usbMsgPtr; | ||||
| /* This variable may be used to pass transmit data to the driver from the | ||||
|  * implementation of usbFunctionWrite(). It is also used internally by the | ||||
|  * driver for standard control requests. | ||||
|  */ | ||||
| extern uchar    usbFunctionSetup(uchar data[8]); | ||||
| /* This function is called when the driver receives a SETUP transaction from | ||||
|  * the host which is not answered by the driver itself (in practice: class and | ||||
|  * vendor requests). All control transfers start with a SETUP transaction where | ||||
|  * the host communicates the parameters of the following (optional) data | ||||
|  * transfer. The SETUP data is available in the 'data' parameter which can | ||||
|  * (and should) be casted to 'usbRequest_t *' for a more user-friendly access | ||||
|  * to parameters. | ||||
|  * | ||||
|  * If the SETUP indicates a control-in transfer, you should provide the | ||||
|  * requested data to the driver. There are two ways to transfer this data: | ||||
|  * (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data | ||||
|  * block and return the length of the data in 'usbFunctionSetup()'. The driver | ||||
|  * will handle the rest. Or (2) return 0xff in 'usbFunctionSetup()'. The driver | ||||
|  * will then call 'usbFunctionRead()' when data is needed. See the | ||||
|  * documentation for usbFunctionRead() for details. | ||||
|  * | ||||
|  * If the SETUP indicates a control-out transfer, the only way to receive the | ||||
|  * data from the host is through the 'usbFunctionWrite()' call. If you | ||||
|  * implement this function, you must return 0xff in 'usbFunctionSetup()' to | ||||
|  * indicate that 'usbFunctionWrite()' should be used. See the documentation of | ||||
|  * this function for more information. If you just want to ignore the data sent | ||||
|  * by the host, return 0 in 'usbFunctionSetup()'. | ||||
|  * | ||||
|  * Note that calls to the functions usbFunctionRead() and usbFunctionWrite() | ||||
|  * are only done if enabled by the configuration in usbconfig.h. | ||||
|  */ | ||||
| extern uchar usbFunctionDescriptor(struct usbRequest *rq); | ||||
| /* You need to implement this function ONLY if you provide USB descriptors at | ||||
|  * runtime (which is an expert feature). It is very similar to | ||||
|  * usbFunctionSetup() above, but it is called only to request USB descriptor | ||||
|  * data. See the documentation of usbFunctionSetup() above for more info. | ||||
|  */ | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT | ||||
| void    usbSetInterrupt(uchar *data, uchar len); | ||||
| /* This function sets the message which will be sent during the next interrupt | ||||
|  * IN transfer. The message is copied to an internal buffer and must not exceed | ||||
|  * a length of 8 bytes. The message may be 0 bytes long just to indicate the | ||||
|  * interrupt status to the host. | ||||
|  * If you need to transfer more bytes, use a control read after the interrupt. | ||||
|  */ | ||||
| extern volatile uchar usbTxLen1; | ||||
| #define usbInterruptIsReady()   (usbTxLen1 & 0x10) | ||||
| /* This macro indicates whether the last interrupt message has already been | ||||
|  * sent. If you set a new interrupt message before the old was sent, the | ||||
|  * message already buffered will be lost. | ||||
|  */ | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT3 | ||||
| void    usbSetInterrupt3(uchar *data, uchar len); | ||||
| extern volatile uchar usbTxLen3; | ||||
| #define usbInterruptIsReady3()   (usbTxLen3 & 0x10) | ||||
| /* Same as above for endpoint 3 */ | ||||
| #endif | ||||
| #endif /* USB_CFG_HAVE_INTRIN_ENDPOINT */ | ||||
| #if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH    /* simplified interface for backward compatibility */ | ||||
| #define usbHidReportDescriptor  usbDescriptorHidReport | ||||
| /* should be declared as: PROGMEM char usbHidReportDescriptor[]; */ | ||||
| /* If you implement an HID device, you need to provide a report descriptor. | ||||
|  * The HID report descriptor syntax is a bit complex. If you understand how | ||||
|  * report descriptors are constructed, we recommend that you use the HID | ||||
|  * Descriptor Tool from usb.org, see http://www.usb.org/developers/hidpage/. | ||||
|  * Otherwise you should probably start with a working example. | ||||
|  */ | ||||
| #endif  /* USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH */ | ||||
| #if USB_CFG_IMPLEMENT_FN_WRITE | ||||
| extern uchar    usbFunctionWrite(uchar *data, uchar len); | ||||
| /* This function is called by the driver to provide a control transfer's | ||||
|  * payload data (control-out). It is called in chunks of up to 8 bytes. The | ||||
|  * total count provided in the current control transfer can be obtained from | ||||
|  * the 'length' property in the setup data. If an error occurred during | ||||
|  * processing, return 0xff (== -1). The driver will answer the entire transfer | ||||
|  * with a STALL token in this case. If you have received the entire payload | ||||
|  * successfully, return 1. If you expect more data, return 0. If you don't | ||||
|  * know whether the host will send more data (you should know, the total is | ||||
|  * provided in the usbFunctionSetup() call!), return 1. | ||||
|  * NOTE: If you return 0xff for STALL, 'usbFunctionWrite()' may still be called | ||||
|  * for the remaining data. You must continue to return 0xff for STALL in these | ||||
|  * calls. | ||||
|  * In order to get usbFunctionWrite() called, define USB_CFG_IMPLEMENT_FN_WRITE | ||||
|  * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. | ||||
|  */ | ||||
| #endif /* USB_CFG_IMPLEMENT_FN_WRITE */ | ||||
| #if USB_CFG_IMPLEMENT_FN_READ | ||||
| extern uchar usbFunctionRead(uchar *data, uchar len); | ||||
| /* This function is called by the driver to ask the application for a control | ||||
|  * transfer's payload data (control-in). It is called in chunks of up to 8 | ||||
|  * bytes each. You should copy the data to the location given by 'data' and | ||||
|  * return the actual number of bytes copied. If you return less than requested, | ||||
|  * the control-in transfer is terminated. If you return 0xff, the driver aborts | ||||
|  * the transfer with a STALL token. | ||||
|  * In order to get usbFunctionRead() called, define USB_CFG_IMPLEMENT_FN_READ | ||||
|  * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. | ||||
|  */ | ||||
| #endif /* USB_CFG_IMPLEMENT_FN_READ */ | ||||
| #if USB_CFG_IMPLEMENT_FN_WRITEOUT | ||||
| extern void usbFunctionWriteOut(uchar *data, uchar len); | ||||
| /* This function is called by the driver when data on interrupt-out or bulk- | ||||
|  * out endpoint 1 is received. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT | ||||
|  * to 1 in usbconfig.h to get this function called. | ||||
|  */ | ||||
| #endif /* USB_CFG_IMPLEMENT_FN_WRITEOUT */ | ||||
| #ifdef USB_CFG_PULLUP_IOPORTNAME | ||||
| #define usbDeviceConnect()      ((USB_PULLUP_DDR |= (1<<USB_CFG_PULLUP_BIT)), \ | ||||
|                                   (USB_PULLUP_OUT |= (1<<USB_CFG_PULLUP_BIT))) | ||||
| /* This macro (intended to look like a function) connects the device to the | ||||
|  * USB bus. It is only available if you have defined the constants | ||||
|  * USB_CFG_PULLUP_IOPORT and USB_CFG_PULLUP_BIT in usbconfig.h. | ||||
|  */ | ||||
| #define usbDeviceDisconnect()   (USB_PULLUP_OUT &= ~(1<<USB_CFG_PULLUP_BIT)) | ||||
| /* This macro (intended to look like a function) disconnects the device from | ||||
|  * the USB bus. It is only available if you have defined the constants | ||||
|  * USB_CFG_PULLUP_IOPORT and USB_CFG_PULLUP_BIT in usbconfig.h. | ||||
|  */ | ||||
| #endif /* USB_CFG_PULLUP_IOPORT */ | ||||
| extern unsigned usbCrc16(unsigned data, uchar len); | ||||
| #define usbCrc16(data, len) usbCrc16((unsigned)(data), len) | ||||
| /* This function calculates the binary complement of the data CRC used in | ||||
|  * USB data packets. The value is used to build raw transmit packets. | ||||
|  * You may want to use this function for data checksums or to verify received | ||||
|  * data. We enforce 16 bit calling conventions for compatibility with IAR's | ||||
|  * tiny memory model. | ||||
|  */ | ||||
| extern unsigned usbCrc16Append(unsigned data, uchar len); | ||||
| #define usbCrc16Append(data, len)    usbCrc16Append((unsigned)(data), len) | ||||
| /* This function is equivalent to usbCrc16() above, except that it appends | ||||
|  * the 2 bytes CRC (lowbyte first) in the 'data' buffer after reading 'len' | ||||
|  * bytes. | ||||
|  */ | ||||
| extern uchar    usbConfiguration; | ||||
| /* This value contains the current configuration set by the host. The driver | ||||
|  * allows setting and querying of this variable with the USB SET_CONFIGURATION | ||||
|  * and GET_CONFIGURATION requests, but does not use it otherwise. | ||||
|  * You may want to reflect the "configured" status with a LED on the device or | ||||
|  * switch on high power parts of the circuit only if the device is configured. | ||||
|  */ | ||||
| #define USB_STRING_DESCRIPTOR_HEADER(stringLength) ((2*(stringLength)+2) | (3<<8)) | ||||
| /* This macro builds a descriptor header for a string descriptor given the | ||||
|  * string's length. See usbdrv.c for an example how to use it. | ||||
|  */ | ||||
| #if USB_CFG_HAVE_FLOWCONTROL | ||||
| extern volatile schar   usbRxLen; | ||||
| #define usbDisableAllRequests()     usbRxLen = -1 | ||||
| /* Must be called from usbFunctionWrite(). This macro disables all data input | ||||
|  * from the USB interface. Requests from the host are answered with a NAK | ||||
|  * while they are disabled. | ||||
|  */ | ||||
| #define usbEnableAllRequests()      usbRxLen = 0 | ||||
| /* May only be called if requests are disabled. This macro enables input from | ||||
|  * the USB interface after it has been disabled with usbDisableAllRequests(). | ||||
|  */ | ||||
| #define usbAllRequestsAreDisabled() (usbRxLen < 0) | ||||
| /* Use this macro to find out whether requests are disabled. It may be needed | ||||
|  * to ensure that usbEnableAllRequests() is never called when requests are | ||||
|  * enabled. | ||||
|  */ | ||||
| #endif | ||||
|  | ||||
| extern uchar    usbTxPacketCnt1; | ||||
| extern uchar    usbTxPacketCnt3; | ||||
| /* The two variables above are mostly for internal use by the driver. You may | ||||
|  * have to reset usbTxPacketCnt1 to 0 if you start data toggling at DATA0 for | ||||
|  * interrupt-IN endpoint 1 and usbTxPacketCnt3 for interrupt-IN endpoint 3 | ||||
|  * respectively. | ||||
|  */ | ||||
|  | ||||
| #endif  /* __ASSEMBLER__ */ | ||||
|  | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
| /* ----------------- Definitions for Descriptor Properties ----------------- */ | ||||
| /* ------------------------------------------------------------------------- */ | ||||
| /* This is advanced stuff. See usbconfig-prototype.h for more information | ||||
|  * about the various methods to define USB descriptors. If you do nothing, | ||||
|  * the default descriptors will be used. | ||||
|  */ | ||||
| #define USB_PROP_IS_DYNAMIC     (1 << 8) | ||||
| /* If this property is set for a descriptor, usbFunctionDescriptor() will be | ||||
|  * used to obtain the particular descriptor. | ||||
|  */ | ||||
| #define USB_PROP_IS_RAM         (1 << 9) | ||||
| /* If this property is set for a descriptor, the data is read from RAM | ||||
|  * memory instead of Flash. The property is used for all methods to provide | ||||
|  * external descriptors. | ||||
|  */ | ||||
| #define USB_PROP_LENGTH(len)    ((len) & 0xff) | ||||
| /* If a static external descriptor is used, this is the total length of the | ||||
|  * descriptor in bytes. | ||||
|  */ | ||||
|  | ||||
| /* all descriptors which may have properties: */ | ||||
| #ifndef USB_CFG_DESCR_PROPS_DEVICE | ||||
| #define USB_CFG_DESCR_PROPS_DEVICE                  0 | ||||
| #endif | ||||
| #ifndef USB_CFG_DESCR_PROPS_CONFIGURATION | ||||
| #define USB_CFG_DESCR_PROPS_CONFIGURATION           0 | ||||
| #endif | ||||
| #ifndef USB_CFG_DESCR_PROPS_STRINGS | ||||
| #define USB_CFG_DESCR_PROPS_STRINGS                 0 | ||||
| #endif | ||||
| #ifndef USB_CFG_DESCR_PROPS_STRING_0 | ||||
| #define USB_CFG_DESCR_PROPS_STRING_0                0 | ||||
| #endif | ||||
| #ifndef USB_CFG_DESCR_PROPS_STRING_VENDOR | ||||
| #define USB_CFG_DESCR_PROPS_STRING_VENDOR           0 | ||||
| #endif | ||||
| #ifndef USB_CFG_DESCR_PROPS_STRING_DEVICE | ||||
| #define USB_CFG_DESCR_PROPS_STRING_DEVICE           0 | ||||
| #endif | ||||
| #ifndef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER | ||||
| #define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER    0 | ||||
| #endif | ||||
| #ifndef USB_CFG_DESCR_PROPS_HID | ||||
| #define USB_CFG_DESCR_PROPS_HID                     0 | ||||
| #endif | ||||
| #if !(USB_CFG_DESCR_PROPS_HID_REPORT) | ||||
| #   undef USB_CFG_DESCR_PROPS_HID_REPORT | ||||
| #   if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* do some backward compatibility tricks */ | ||||
| #       define USB_CFG_DESCR_PROPS_HID_REPORT       USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH | ||||
| #   else | ||||
| #       define USB_CFG_DESCR_PROPS_HID_REPORT       0 | ||||
| #   endif | ||||
| #endif | ||||
| #ifndef USB_CFG_DESCR_PROPS_UNKNOWN | ||||
| #define USB_CFG_DESCR_PROPS_UNKNOWN                 0 | ||||
| #endif | ||||
|  | ||||
| /* ------------------ forward declaration of descriptors ------------------- */ | ||||
| /* If you use external static descriptors, they must be stored in global | ||||
|  * arrays as declared below: | ||||
|  */ | ||||
| #ifndef __ASSEMBLER__ | ||||
| extern | ||||
| #if !(USB_CFG_DESCR_PROPS_DEVICE & USB_PROP_IS_RAM) | ||||
| PROGMEM | ||||
| #endif | ||||
| char usbDescriptorDevice[]; | ||||
|  | ||||
| extern | ||||
| #if !(USB_CFG_DESCR_PROPS_CONFIGURATION & USB_PROP_IS_RAM) | ||||
| PROGMEM | ||||
| #endif | ||||
| char usbDescriptorConfiguration[]; | ||||
|  | ||||
| extern | ||||
| #if !(USB_CFG_DESCR_PROPS_HID_REPORT & USB_PROP_IS_RAM) | ||||
| PROGMEM | ||||
| #endif | ||||
| char usbDescriptorHidReport[]; | ||||
|  | ||||
| extern | ||||
| #if !(USB_CFG_DESCR_PROPS_STRING_0 & USB_PROP_IS_RAM) | ||||
| PROGMEM | ||||
| #endif | ||||
| char usbDescriptorString0[]; | ||||
|  | ||||
| extern | ||||
| #if !(USB_CFG_DESCR_PROPS_STRING_VENDOR & USB_PROP_IS_RAM) | ||||
| PROGMEM | ||||
| #endif | ||||
| int usbDescriptorStringVendor[]; | ||||
|  | ||||
| extern | ||||
| #if !(USB_CFG_DESCR_PROPS_STRING_PRODUCT & USB_PROP_IS_RAM) | ||||
| PROGMEM | ||||
| #endif | ||||
| int usbDescriptorStringDevice[]; | ||||
|  | ||||
| extern | ||||
| #if !(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER & USB_PROP_IS_RAM) | ||||
| PROGMEM | ||||
| #endif | ||||
| int usbDescriptorStringSerialNumber[]; | ||||
|  | ||||
| #endif /* __ASSEMBLER__ */ | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
| /* ------------------------ General Purpose Macros ------------------------- */ | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| #define USB_CONCAT(a, b)            a ## b | ||||
| #define USB_CONCAT_EXPANDED(a, b)   USB_CONCAT(a, b) | ||||
|  | ||||
| #define USB_OUTPORT(name)           USB_CONCAT(PORT, name) | ||||
| #define USB_INPORT(name)            USB_CONCAT(PIN, name) | ||||
| #define USB_DDRPORT(name)           USB_CONCAT(DDR, name) | ||||
| /* The double-define trick above lets us concatenate strings which are | ||||
|  * defined by macros. | ||||
|  */ | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
| /* ------------------------- Constant definitions -------------------------- */ | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| #if !defined USB_CFG_VENDOR_ID || !defined USB_CFG_DEVICE_ID | ||||
| static uchar Warning_You_should_define_USB_CFG_VENDOR_ID_and_USB_CFG_DEVICE_ID_in_usbconfig_h; | ||||
| /* The unused variable above should generate a warning on all compilers. IAR cc | ||||
|  * does not understand the "#warning" preprocessor direcetive. | ||||
|  * If the user has not defined IDs, we default to obdev's free IDs. | ||||
|  * See USBID-License.txt for details. | ||||
|  */ | ||||
| #endif | ||||
|  | ||||
| /* make sure we have a VID and PID defined, byte order is lowbyte, highbyte */ | ||||
| #ifndef USB_CFG_VENDOR_ID | ||||
| #   define  USB_CFG_VENDOR_ID   0xc0, 0x16  /* 5824 in dec, stands for VOTI */ | ||||
| #endif | ||||
|  | ||||
| #ifndef USB_CFG_DEVICE_ID | ||||
| #   if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH | ||||
| #       define USB_CFG_DEVICE_ID    0xdf, 0x05  /* 1503 in dec, shared PID for HIDs */ | ||||
| #   elif USB_CFG_INTERFACE_CLASS == 2 | ||||
| #       define USB_CFG_DEVICE_ID    0xe1, 0x05  /* 1505 in dec, shared PID for CDC Modems */ | ||||
| #   else | ||||
| #       define USB_CFG_DEVICE_ID    0xdc, 0x05  /* 1500 in dec, obdev's free PID */ | ||||
| #   endif | ||||
| #endif | ||||
|  | ||||
| #ifndef USB_BUFFER_SECTION | ||||
| #   define  USB_BUFFER_SECTION  ".bss"      /* if user has not selected a named section */ | ||||
| #endif | ||||
|  | ||||
| /* Derive Output, Input and DataDirection ports from port names */ | ||||
| #ifndef USB_CFG_IOPORTNAME | ||||
| #error "You must define USB_CFG_IOPORTNAME in usbconfig.h, see usbconfig-prototype.h" | ||||
| #endif | ||||
|  | ||||
| #define USBOUT          USB_OUTPORT(USB_CFG_IOPORTNAME) | ||||
| #define USB_PULLUP_OUT  USB_OUTPORT(USB_CFG_PULLUP_IOPORTNAME) | ||||
| #define USBIN           USB_INPORT(USB_CFG_IOPORTNAME) | ||||
| #define USBDDR          USB_DDRPORT(USB_CFG_IOPORTNAME) | ||||
| #define USB_PULLUP_DDR  USB_DDRPORT(USB_CFG_PULLUP_IOPORTNAME) | ||||
|  | ||||
| #define USBMINUS    USB_CFG_DMINUS_BIT | ||||
| #define USBPLUS     USB_CFG_DPLUS_BIT | ||||
| #define USBIDLE     (1<<USB_CFG_DMINUS_BIT) /* value representing J state */ | ||||
| #define USBMASK     ((1<<USB_CFG_DPLUS_BIT) | (1<<USB_CFG_DMINUS_BIT))  /* mask for USB I/O bits */ | ||||
|  | ||||
| /* defines for backward compatibility with older driver versions: */ | ||||
| #define USB_CFG_IOPORT          USB_OUTPORT(USB_CFG_IOPORTNAME) | ||||
| #ifdef USB_CFG_PULLUP_IOPORTNAME | ||||
| #define USB_CFG_PULLUP_IOPORT   USB_OUTPORT(USB_CFG_PULLUP_IOPORTNAME) | ||||
| #endif | ||||
|  | ||||
|  | ||||
| #define USB_BUFSIZE     11  /* PID, 8 bytes data, 2 bytes CRC */ | ||||
|  | ||||
| /* ----- Try to find registers and bits responsible for ext interrupt 0 ----- */ | ||||
|  | ||||
| #ifndef USB_INTR_CFG    /* allow user to override our default */ | ||||
| #   if defined  EICRA | ||||
| #       define USB_INTR_CFG EICRA | ||||
| #   else | ||||
| #       define USB_INTR_CFG MCUCR | ||||
| #   endif | ||||
| #endif | ||||
| #ifndef USB_INTR_CFG_SET    /* allow user to override our default */ | ||||
| #   define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01))    /* cfg for rising edge */ | ||||
| #endif | ||||
| #ifndef USB_INTR_CFG_CLR    /* allow user to override our default */ | ||||
| #   define USB_INTR_CFG_CLR 0    /* no bits to clear */ | ||||
| #endif | ||||
|  | ||||
| #ifndef USB_INTR_ENABLE     /* allow user to override our default */ | ||||
| #   if defined GIMSK | ||||
| #       define USB_INTR_ENABLE  GIMSK | ||||
| #   elif defined EIMSK | ||||
| #       define USB_INTR_ENABLE  EIMSK | ||||
| #   else | ||||
| #       define USB_INTR_ENABLE  GICR | ||||
| #   endif | ||||
| #endif | ||||
| #ifndef USB_INTR_ENABLE_BIT /* allow user to override our default */ | ||||
| #   define USB_INTR_ENABLE_BIT  INT0 | ||||
| #endif | ||||
|  | ||||
| #ifndef USB_INTR_PENDING    /* allow user to override our default */ | ||||
| #   if defined  EIFR | ||||
| #       define USB_INTR_PENDING EIFR | ||||
| #   else | ||||
| #       define USB_INTR_PENDING GIFR | ||||
| #   endif | ||||
| #endif | ||||
| #ifndef USB_INTR_PENDING_BIT    /* allow user to override our default */ | ||||
| #   define USB_INTR_PENDING_BIT INTF0 | ||||
| #endif | ||||
|  | ||||
| /* | ||||
| The defines above don't work for the following chips | ||||
| at90c8534: no ISC0?, no PORTB, can't find a data sheet | ||||
| at86rf401: no PORTB, no MCUCR etc, low clock rate | ||||
| atmega103: no ISC0? (maybe omission in header, can't find data sheet) | ||||
| atmega603: not defined in avr-libc | ||||
| at43usb320, at43usb355, at76c711: have USB anyway | ||||
| at94k: is different... | ||||
|  | ||||
| at90s1200, attiny11, attiny12, attiny15, attiny28: these have no RAM | ||||
| */ | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
| /* ----------------- USB Specification Constants and Types ----------------- */ | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| /* USB Token values */ | ||||
| #define USBPID_SETUP    0x2d | ||||
| #define USBPID_OUT      0xe1 | ||||
| #define USBPID_IN       0x69 | ||||
| #define USBPID_DATA0    0xc3 | ||||
| #define USBPID_DATA1    0x4b | ||||
|  | ||||
| #define USBPID_ACK      0xd2 | ||||
| #define USBPID_NAK      0x5a | ||||
| #define USBPID_STALL    0x1e | ||||
|  | ||||
| #ifndef __ASSEMBLER__ | ||||
| typedef union usbWord{ | ||||
|     unsigned    word; | ||||
|     uchar       bytes[2]; | ||||
| }usbWord_t; | ||||
|  | ||||
| typedef struct usbRequest{ | ||||
|     uchar       bmRequestType; | ||||
|     uchar       bRequest; | ||||
|     usbWord_t   wValue; | ||||
|     usbWord_t   wIndex; | ||||
|     usbWord_t   wLength; | ||||
| }usbRequest_t; | ||||
| /* This structure matches the 8 byte setup request */ | ||||
| #endif | ||||
|  | ||||
| /* bmRequestType field in USB setup: | ||||
|  * d t t r r r r r, where | ||||
|  * d ..... direction: 0=host->device, 1=device->host | ||||
|  * t ..... type: 0=standard, 1=class, 2=vendor, 3=reserved | ||||
|  * r ..... recipient: 0=device, 1=interface, 2=endpoint, 3=other | ||||
|  */ | ||||
|  | ||||
| /* USB setup recipient values */ | ||||
| #define USBRQ_RCPT_MASK         0x1f | ||||
| #define USBRQ_RCPT_DEVICE       0 | ||||
| #define USBRQ_RCPT_INTERFACE    1 | ||||
| #define USBRQ_RCPT_ENDPOINT     2 | ||||
|  | ||||
| /* USB request type values */ | ||||
| #define USBRQ_TYPE_MASK         0x60 | ||||
| #define USBRQ_TYPE_STANDARD     (0<<5) | ||||
| #define USBRQ_TYPE_CLASS        (1<<5) | ||||
| #define USBRQ_TYPE_VENDOR       (2<<5) | ||||
|  | ||||
| /* USB direction values: */ | ||||
| #define USBRQ_DIR_MASK              0x80 | ||||
| #define USBRQ_DIR_HOST_TO_DEVICE    (0<<7) | ||||
| #define USBRQ_DIR_DEVICE_TO_HOST    (1<<7) | ||||
|  | ||||
| /* USB Standard Requests */ | ||||
| #define USBRQ_GET_STATUS        0 | ||||
| #define USBRQ_CLEAR_FEATURE     1 | ||||
| #define USBRQ_SET_FEATURE       3 | ||||
| #define USBRQ_SET_ADDRESS       5 | ||||
| #define USBRQ_GET_DESCRIPTOR    6 | ||||
| #define USBRQ_SET_DESCRIPTOR    7 | ||||
| #define USBRQ_GET_CONFIGURATION 8 | ||||
| #define USBRQ_SET_CONFIGURATION 9 | ||||
| #define USBRQ_GET_INTERFACE     10 | ||||
| #define USBRQ_SET_INTERFACE     11 | ||||
| #define USBRQ_SYNCH_FRAME       12 | ||||
|  | ||||
| /* USB descriptor constants */ | ||||
| #define USBDESCR_DEVICE         1 | ||||
| #define USBDESCR_CONFIG         2 | ||||
| #define USBDESCR_STRING         3 | ||||
| #define USBDESCR_INTERFACE      4 | ||||
| #define USBDESCR_ENDPOINT       5 | ||||
| #define USBDESCR_HID            0x21 | ||||
| #define USBDESCR_HID_REPORT     0x22 | ||||
| #define USBDESCR_HID_PHYS       0x23 | ||||
|  | ||||
| #define USBATTR_BUSPOWER        0x80 | ||||
| #define USBATTR_SELFPOWER       0x40 | ||||
| #define USBATTR_REMOTEWAKE      0x20 | ||||
|  | ||||
| /* USB HID Requests */ | ||||
| #define USBRQ_HID_GET_REPORT    0x01 | ||||
| #define USBRQ_HID_GET_IDLE      0x02 | ||||
| #define USBRQ_HID_GET_PROTOCOL  0x03 | ||||
| #define USBRQ_HID_SET_REPORT    0x09 | ||||
| #define USBRQ_HID_SET_IDLE      0x0a | ||||
| #define USBRQ_HID_SET_PROTOCOL  0x0b | ||||
|  | ||||
| /* ------------------------------------------------------------------------- */ | ||||
|  | ||||
| #endif /* __usbdrv_h_included__ */ | ||||
							
								
								
									
										784
									
								
								firmware/usbdrv/usbdrvasm.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										784
									
								
								firmware/usbdrv/usbdrvasm.S
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,784 @@ | ||||
| /* Name: usbdrvasm.S | ||||
|  * Project: AVR USB driver | ||||
|  * Author: Christian Starkjohann | ||||
|  * Creation Date: 2004-12-29 | ||||
|  * Tabsize: 4 | ||||
|  * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | ||||
|  * License: Proprietary, free under certain conditions. See Documentation. | ||||
|  * This Revision: $Id: usbdrvasm.S,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| General Description: | ||||
| This module implements the assembler part of the USB driver. See usbdrv.h | ||||
| for a description of the entire driver. | ||||
| Since almost all of this code is timing critical, don't change unless you | ||||
| really know what you are doing! Many parts require not only a maximum number | ||||
| of CPU cycles, but even an exact number of cycles! | ||||
|  | ||||
|  | ||||
| Timing constraints according to spec (in bit times): | ||||
| timing subject                                      min max    CPUcycles | ||||
| --------------------------------------------------------------------------- | ||||
| EOP of OUT/SETUP to sync pattern of DATA0 (both rx) 2   16     16-128 | ||||
| EOP of IN to sync pattern of DATA0 (rx, then tx)    2   7.5    16-60 | ||||
| DATAx (rx) to ACK/NAK/STALL (tx)                    2   7.5    16-60 | ||||
| */ | ||||
|  | ||||
| #include "iarcompat.h" | ||||
| #ifndef __IAR_SYSTEMS_ASM__ | ||||
|     /* configs for io.h */ | ||||
| #   define __SFR_OFFSET 0 | ||||
| #   define _VECTOR(N)   __vector_ ## N   /* io.h does not define this for asm */ | ||||
| #   include <avr/io.h> /* for CPU I/O register definitions and vectors */ | ||||
| #endif  /* __IAR_SYSTEMS_ASM__ */ | ||||
| #include "usbdrv.h" /* for common defs */ | ||||
|  | ||||
|  | ||||
| /* register names */ | ||||
| #define x1      r16 | ||||
| #define x2      r17 | ||||
| #define shift   r18 | ||||
| #define cnt     r19 | ||||
| #define x3      r20 | ||||
| #define x4      r21 | ||||
|  | ||||
| /* Some assembler dependent definitions and declarations: */ | ||||
|  | ||||
| #ifdef __IAR_SYSTEMS_ASM__ | ||||
|  | ||||
| #   define nop2     rjmp    $+2 /* jump to next instruction */ | ||||
| #   define XL       r26 | ||||
| #   define XH       r27 | ||||
| #   define YL       r28 | ||||
| #   define YH       r29 | ||||
| #   define ZL       r30 | ||||
| #   define ZH       r31 | ||||
| #   define lo8(x)   LOW(x) | ||||
| #   define hi8(x)   ((x)>>8)    /* not HIGH to allow XLINK to make a proper range check */ | ||||
|  | ||||
|     extern  usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBuf | ||||
|     extern  usbCurrentTok, usbRxLen, usbRxToken, usbAppBuf, usbTxLen | ||||
|     extern  usbTxBuf, usbMsgLen, usbTxLen1, usbTxBuf1, usbTxLen3, usbTxBuf3 | ||||
|     public  usbCrc16 | ||||
|     public  usbCrc16Append | ||||
|  | ||||
|     COMMON  INTVEC | ||||
|     ORG     INT0_vect | ||||
|     rjmp    SIG_INTERRUPT0 | ||||
|     RSEG    CODE | ||||
|  | ||||
| #else /* __IAR_SYSTEMS_ASM__ */ | ||||
|  | ||||
| #   define nop2     rjmp    .+0 /* jump to next instruction */ | ||||
|  | ||||
|     .text | ||||
|     .global SIG_INTERRUPT0 | ||||
|     .type   SIG_INTERRUPT0, @function | ||||
|     .global usbCrc16 | ||||
|     .global usbCrc16Append | ||||
|  | ||||
| #endif /* __IAR_SYSTEMS_ASM__ */ | ||||
|  | ||||
|  | ||||
| SIG_INTERRUPT0: | ||||
| ;Software-receiver engine. Strict timing! Don't change unless you can preserve timing! | ||||
| ;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled | ||||
| ;max allowable interrupt latency: 32 cycles -> max 25 cycles interrupt disable | ||||
| ;max stack usage: [ret(2), x1, SREG, x2, cnt, shift, YH, YL, x3, x4] = 11 bytes | ||||
| usbInterrupt: | ||||
| ;order of registers pushed: | ||||
| ;x1, SREG, x2, cnt, shift, [YH, YL, x3] | ||||
|     push    x1              ;2  push only what is necessary to sync with edge ASAP | ||||
|     in      x1, SREG        ;1 | ||||
|     push    x1              ;2 | ||||
| ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] | ||||
| ;sync up with J to K edge during sync pattern -- use fastest possible loops | ||||
| ;first part has no timeout because it waits for IDLE or SE1 (== disconnected) | ||||
| #if !USB_CFG_SAMPLE_EXACT | ||||
|     ldi     x1, 5           ;1 setup a timeout for waitForK | ||||
| #endif | ||||
| waitForJ: | ||||
|     sbis    USBIN, USBMINUS ;1 wait for D- == 1 | ||||
|     rjmp    waitForJ        ;2 | ||||
| #if USB_CFG_SAMPLE_EXACT | ||||
| ;The following code represents the unrolled loop in the else branch. It | ||||
| ;results in a sampling window of 1/4 bit which meets the spec. | ||||
|     sbis    USBIN, USBMINUS | ||||
|     rjmp    foundK | ||||
|     sbis    USBIN, USBMINUS | ||||
|     rjmp    foundK | ||||
|     sbis    USBIN, USBMINUS | ||||
|     rjmp    foundK | ||||
|     nop | ||||
|     nop2 | ||||
| foundK: | ||||
| #else | ||||
| waitForK: | ||||
|     dec     x1              ;1 | ||||
|     sbic    USBIN, USBMINUS ;1 wait for D- == 0 | ||||
|     brne    waitForK        ;2 | ||||
| #endif | ||||
| ;{2, 6} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] | ||||
| ;we have 1 bit time for setup purposes, then sample again: | ||||
|     push    x2              ;2 | ||||
|     push    cnt             ;2 | ||||
|     push    shift           ;2 | ||||
| shortcutEntry: | ||||
|     ldi     cnt, 1          ;1 pre-init bit counter (-1 because no dec follows, -1 because 1 bit already sampled) | ||||
|     ldi     x2, 1<<USB_CFG_DPLUS_BIT    ;1 -> 8   edge sync ended with D- == 0 | ||||
| ;now wait until SYNC byte is over. Wait for either 2 bits low (success) or 2 bits high (failure) | ||||
| waitNoChange: | ||||
|     in      x1, USBIN       ;1 <-- sample, timing: edge + {2, 6} cycles | ||||
|     eor     x2, x1          ;1 | ||||
|     sbrc    x2, USBMINUS    ;1 | 2 | ||||
|     ldi     cnt, 2          ;1 | 0 cnt = numBits - 1 (because dec follows) | ||||
|     mov     x2, x1          ;1 | ||||
|     dec     cnt             ;1 | ||||
|     brne    waitNoChange    ;2 | 1 | ||||
|     sbrc    x1, USBMINUS    ;2 | ||||
|     rjmp    sofError        ;0 two consecutive "1" bits -> framing error | ||||
| ;start reading data, but don't check for bitstuffing because these are the | ||||
| ;first bits. Use the cycles for initialization instead. Note that we read and | ||||
| ;store the binary complement of the data stream because eor results in 1 for | ||||
| ;a change and 0 for no change. | ||||
|     in      x1, USBIN       ;1 <-- sample bit 0, timing: edge + {3, 7} cycles | ||||
|     eor     x2, x1          ;1 | ||||
|     ldi     shift, 0x00     ;1 prepare for bitstuff check later on in loop | ||||
|     bst     x2, USBMINUS    ;1 | ||||
|     bld     shift, 0        ;1 | ||||
|     push    YH              ;2 -> 7 | ||||
|     in      x2, USBIN       ;1 <-- sample bit 1, timing: edge + {2, 6} cycles | ||||
|     eor     x1, x2          ;1 | ||||
|     bst     x1, USBMINUS    ;1 | ||||
|     bld     shift, 1        ;1 | ||||
|     push    YL              ;2 | ||||
|     lds     YL, usbInputBuf ;2 -> 8 | ||||
|     in      x1, USBIN       ;1 <-- sample bit 2, timing: edge + {2, 6} cycles | ||||
|     eor     x2, x1          ;1 | ||||
|     bst     x2, USBMINUS    ;1 | ||||
|     bld     shift, 2        ;1 | ||||
|     ldi     cnt, USB_BUFSIZE;1 | ||||
|     ldi     YH, hi8(usbRxBuf);1 assume that usbRxBuf does not cross a page | ||||
|     push    x3              ;2 -> 8 | ||||
|     in      x2, USBIN       ;1 <-- sample bit 3, timing: edge + {2, 6} cycles | ||||
|     eor     x1, x2          ;1 | ||||
|     bst     x1, USBMINUS    ;1 | ||||
|     bld     shift, 3        ;1 | ||||
|     ser     x3              ;1 | ||||
|     nop                     ;1 | ||||
|     rjmp    rxbit4          ;2 -> 8 | ||||
|  | ||||
| shortcutToStart:            ;{,43} into next frame: max 5.5 sync bits missed | ||||
| #if !USB_CFG_SAMPLE_EXACT | ||||
|     ldi     x1, 5           ;2 setup timeout | ||||
| #endif | ||||
| waitForJ1: | ||||
|     sbis    USBIN, USBMINUS ;1 wait for D- == 1 | ||||
|     rjmp    waitForJ1       ;2 | ||||
| #if USB_CFG_SAMPLE_EXACT | ||||
| ;The following code represents the unrolled loop in the else branch. It | ||||
| ;results in a sampling window of 1/4 bit which meets the spec. | ||||
|     sbis    USBIN, USBMINUS | ||||
|     rjmp    foundK1 | ||||
|     sbis    USBIN, USBMINUS | ||||
|     rjmp    foundK1 | ||||
|     sbis    USBIN, USBMINUS | ||||
|     rjmp    foundK1 | ||||
|     nop | ||||
|     nop2 | ||||
| foundK1: | ||||
| #else | ||||
| waitForK1: | ||||
|     dec     x1              ;1 | ||||
|     sbic    USBIN, USBMINUS ;1 wait for D- == 0 | ||||
|     brne    waitForK1       ;2 | ||||
| #endif | ||||
|     pop     YH              ;2 correct stack alignment | ||||
|     nop2                    ;2 delay for the same time as the pushes in the original code | ||||
|     rjmp    shortcutEntry   ;2 | ||||
|  | ||||
| ; ################# receiver loop ################# | ||||
| ; extra jobs done during bit interval: | ||||
| ; bit 6:    se0 check | ||||
| ; bit 7:    or, store, clear | ||||
| ; bit 0:    recover from delay  [SE0 is unreliable here due to bit dribbling in hubs] | ||||
| ; bit 1:    se0 check | ||||
| ; bit 2:    se0 check | ||||
| ; bit 3:    overflow check | ||||
| ; bit 4:    se0 check | ||||
| ; bit 5:    rjmp | ||||
|  | ||||
| ; stuffed* helpers have the functionality of a subroutine, but we can't afford | ||||
| ; the overhead of a call. We therefore need a separate routine for each caller | ||||
| ; which jumps back appropriately. | ||||
|  | ||||
| stuffed5:               ;1 for branch taken | ||||
|     in      x2, USBIN   ;1 <-- sample @ +1 | ||||
|     andi    x2, USBMASK ;1 | ||||
|     breq    se0a        ;1 | ||||
|     andi    x3, ~0x20   ;1 | ||||
|     ori     shift, 0x20 ;1 | ||||
|     rjmp    rxbit6      ;2 | ||||
|  | ||||
| stuffed6:               ;1 for branch taken | ||||
|     in      x1, USBIN   ;1 <-- sample @ +1 | ||||
|     andi    x1, USBMASK ;1 | ||||
|     breq    se0a        ;1 | ||||
|     andi    x3, ~0x40   ;1 | ||||
|     ori     shift, 0x40 ;1 | ||||
|     rjmp    rxbit7      ;2 | ||||
|  | ||||
| ; This is somewhat special because it has to compensate for the delay in bit 7 | ||||
| stuffed7:               ;1 for branch taken | ||||
|     andi    x1, USBMASK ;1 already sampled by caller | ||||
|     breq    se0a        ;1 | ||||
|     mov     x2, x1      ;1 ensure correct NRZI sequence | ||||
|     ori     shift, 0x80 ;1 no need to set reconstruction in x3: shift has already been used | ||||
|     in      x1, USBIN   ;1 <-- sample bit 0 | ||||
|     rjmp    unstuffed7  ;2 | ||||
|  | ||||
| stuffed0:               ;1 for branch taken | ||||
|     in      x1, USBIN   ;1 <-- sample @ +1 | ||||
|     andi    x1, USBMASK ;1 | ||||
|     breq    se0a        ;1 | ||||
|     andi    x3, ~0x01   ;1 | ||||
|     ori     shift, 0x01 ;1 | ||||
|     rjmp    rxbit1      ;2 | ||||
|  | ||||
| ;----------------------------- | ||||
| rxLoop: | ||||
|     breq    stuffed5    ;1 | ||||
| rxbit6: | ||||
|     in      x1, USBIN   ;1 <-- sample bit 6 | ||||
|     andi    x1, USBMASK ;1 | ||||
|     breq    se0a        ;1 | ||||
|     eor     x2, x1      ;1 | ||||
|     bst     x2, USBMINUS;1 | ||||
|     bld     shift, 6    ;1 | ||||
|     cpi     shift, 0x02 ;1 | ||||
|     brlo    stuffed6    ;1 | ||||
| rxbit7: | ||||
|     in      x2, USBIN   ;1 <-- sample bit 7 | ||||
|     eor     x1, x2      ;1 | ||||
|     bst     x1, USBMINUS;1 | ||||
|     bld     shift, 7    ;1 | ||||
|     eor     x3, shift   ;1 x3 is 0 at bit locations we changed, 1 at others | ||||
|     st      y+, x3      ;2 the eor above reconstructed modified bits and inverted rx data | ||||
|     ser     x3          ;1 | ||||
| rxbit0: | ||||
|     in      x1, USBIN   ;1 <-- sample bit 0 | ||||
|     cpi     shift, 0x04 ;1 | ||||
|     brlo    stuffed7    ;1 | ||||
| unstuffed7: | ||||
|     eor     x2, x1      ;1 | ||||
|     bst     x2, USBMINUS;1 | ||||
|     bld     shift, 0    ;1 | ||||
|     andi    shift, 0xf9 ;1 | ||||
|     breq    stuffed0    ;1 | ||||
| rxbit1: | ||||
|     in      x2, USBIN   ;1 <-- sample bit 1 | ||||
|     andi    x2, USBMASK ;1 | ||||
| se0a:                   ; enlarge jump range to SE0 | ||||
|     breq    se0         ;1 check for SE0 more often close to start of byte | ||||
|     eor     x1, x2      ;1 | ||||
|     bst     x1, USBMINUS;1 | ||||
|     bld     shift, 1    ;1 | ||||
|     andi    shift, 0xf3 ;1 | ||||
|     breq    stuffed1    ;1 | ||||
| rxbit2: | ||||
|     in      x1, USBIN   ;1 <-- sample bit 2 | ||||
|     andi    x1, USBMASK ;1 | ||||
|     breq    se0         ;1 | ||||
|     eor     x2, x1      ;1 | ||||
|     bst     x2, USBMINUS;1 | ||||
|     bld     shift, 2    ;1 | ||||
|     andi    shift, 0xe7 ;1 | ||||
|     breq    stuffed2    ;1 | ||||
| rxbit3: | ||||
|     in      x2, USBIN   ;1 <-- sample bit 3 | ||||
|     eor     x1, x2      ;1 | ||||
|     bst     x1, USBMINUS;1 | ||||
|     bld     shift, 3    ;1 | ||||
|     dec     cnt         ;1  check for buffer overflow | ||||
|     breq    overflow    ;1 | ||||
|     andi    shift, 0xcf ;1 | ||||
|     breq    stuffed3    ;1 | ||||
| rxbit4: | ||||
|     in      x1, USBIN   ;1 <-- sample bit 4 | ||||
|     andi    x1, USBMASK ;1 | ||||
|     breq    se0         ;1 | ||||
|     eor     x2, x1      ;1 | ||||
|     bst     x2, USBMINUS;1 | ||||
|     bld     shift, 4    ;1 | ||||
|     andi    shift, 0x9f ;1 | ||||
|     breq    stuffed4    ;1 | ||||
| rxbit5: | ||||
|     in      x2, USBIN   ;1 <-- sample bit 5 | ||||
|     eor     x1, x2      ;1 | ||||
|     bst     x1, USBMINUS;1 | ||||
|     bld     shift, 5    ;1 | ||||
|     andi    shift, 0x3f ;1 | ||||
|     rjmp    rxLoop      ;2 | ||||
| ;----------------------------- | ||||
|  | ||||
| stuffed1:               ;1 for branch taken | ||||
|     in      x2, USBIN   ;1 <-- sample @ +1 | ||||
|     andi    x2, USBMASK ;1 | ||||
|     breq    se0         ;1 | ||||
|     andi    x3, ~0x02   ;1 | ||||
|     ori     shift, 0x02 ;1 | ||||
|     rjmp    rxbit2      ;2 | ||||
|  | ||||
| stuffed2:               ;1 for branch taken | ||||
|     in      x1, USBIN   ;1 <-- sample @ +1 | ||||
|     andi    x1, USBMASK ;1 | ||||
|     breq    se0         ;1 | ||||
|     andi    x3, ~0x04   ;1 | ||||
|     ori     shift, 0x04 ;1 | ||||
|     rjmp    rxbit3      ;2 | ||||
|  | ||||
| stuffed3:               ;1 for branch taken | ||||
|     in      x2, USBIN   ;1 <-- sample @ +1 | ||||
|     andi    x2, USBMASK ;1 | ||||
|     breq    se0         ;1 | ||||
|     andi    x3, ~0x08   ;1 | ||||
|     ori     shift, 0x08 ;1 | ||||
|     rjmp    rxbit4      ;2 | ||||
|  | ||||
| stuffed4:               ;1 for branch taken | ||||
|     in      x1, USBIN   ;1 <-- sample @ +1 | ||||
|     andi    x1, USBMASK ;1 | ||||
|     breq    se0         ;1 | ||||
|     andi    x3, ~0x10   ;1 | ||||
|     ori     shift, 0x10 ;1 | ||||
|     rjmp    rxbit5      ;2 | ||||
|  | ||||
| ;################ end receiver loop ############### | ||||
|  | ||||
| overflow:                   ; ignore package if buffer overflow | ||||
|     rjmp    rxDoReturn      ; enlarge jump range | ||||
|  | ||||
| ;This is the only non-error exit point for the software receiver loop | ||||
| ;{4, 20} cycles after start of SE0, typically {10, 18} after SE0 start = {-6, 2} from end of SE0 | ||||
| ;next sync starts {16,} cycles after SE0 -> worst case start: +4 from next sync start | ||||
| ;we don't check any CRCs here because there is no time left. | ||||
| se0:                            ;{-6, 2} from end of SE0 / {,4} into next frame | ||||
|     mov     cnt, YL             ;1 assume buffer in lower 256 bytes of memory | ||||
|     lds     YL, usbInputBuf     ;2 reposition to buffer start | ||||
|     sub     cnt, YL             ;1 length of message | ||||
|     ldi     x1, 1<<USB_INTR_PENDING_BIT ;1 | ||||
|     cpi     cnt, 3              ;1 | ||||
|     out     USB_INTR_PENDING, x1;1 clear pending intr and check flag later. SE0 must be over. {,10} into next frame | ||||
|     brlo    rxDoReturn          ;1 ensure valid packet size, ignore others | ||||
|     ld      x1, y               ;2 PID | ||||
|     ldd     x2, y+1             ;2 ADDR + 1 bit endpoint number | ||||
|     mov     x3, x2              ;1 store for endpoint number | ||||
|     andi    x2, 0x7f            ;1 mask endpoint number bit | ||||
|     lds     shift, usbDeviceAddr;2 | ||||
|     cpi     x1, USBPID_SETUP    ;1 | ||||
|     breq    isSetupOrOut        ;2 -> 19 = {13, 21} from SE0 end | ||||
|     cpi     x1, USBPID_OUT      ;1 | ||||
|     breq    isSetupOrOut        ;2 -> 22 = {16, 24} from SE0 end / {,24} into next frame | ||||
|     cpi     x1, USBPID_IN       ;1 | ||||
|     breq    handleIn            ;1 | ||||
| #define USB_DATA_MASK   ~(USBPID_DATA0 ^ USBPID_DATA1) | ||||
|     andi    x1, USB_DATA_MASK   ;1 | ||||
|     cpi     x1, USBPID_DATA0 & USB_DATA_MASK ;1 | ||||
|     brne    rxDoReturn          ;1 not a data PID -- ignore | ||||
| isData: | ||||
|     lds     x2, usbCurrentTok   ;2 | ||||
|     tst     x2                  ;1 | ||||
|     breq    rxDoReturn          ;1 for other device or spontaneous data -- ignore | ||||
|     lds     x1, usbRxLen        ;2 | ||||
|     cpi     x1, 0               ;1 | ||||
|     brne    sendNakAndReti      ;1 no buffer space available / {30, 38} from SE0 end | ||||
| ; 2006-03-11: The following two lines fix a problem where the device was not | ||||
| ; recognized if usbPoll() was called less frequently than once every 4 ms. | ||||
|     cpi     cnt, 4              ;1 zero sized data packets are status phase only -- ignore and ack | ||||
|     brmi    sendAckAndReti      ;1 keep rx buffer clean -- we must not NAK next SETUP | ||||
|     sts     usbRxLen, cnt       ;2 store received data, swap buffers | ||||
|     sts     usbRxToken, x2      ;2 | ||||
|     lds     x1, usbAppBuf       ;2 | ||||
|     sts     usbAppBuf, YL       ;2 | ||||
|     sts     usbInputBuf, x1     ;2 buffers now swapped | ||||
|     rjmp    sendAckAndReti      ;2 -> {43, 51} from SE0 end | ||||
|  | ||||
| handleIn:                       ; {18, 26} from SE0 end | ||||
|     cp      x2, shift           ;1 shift contains our device addr | ||||
|     brne    rxDoReturn          ;1 other device | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT | ||||
|     sbrc    x3, 7               ;2 x3 contains addr + endpoint | ||||
|     rjmp    handleIn1           ;0 | ||||
| #endif | ||||
|     lds     cnt, usbTxLen       ;2 | ||||
|     sbrc    cnt, 4              ;2 | ||||
|     rjmp    sendCntAndReti      ;0 -> {27, 35} from SE0 end | ||||
|     ldi     x1, USBPID_NAK      ;1 | ||||
|     sts     usbTxLen, x1        ;2 buffer is now free | ||||
|     ldi     YL, lo8(usbTxBuf)   ;1 | ||||
|     ldi     YH, hi8(usbTxBuf)   ;1 | ||||
|     rjmp    usbSendAndReti      ;2 -> {34, 43} from SE0 end | ||||
|  | ||||
| ; Comment about when to set usbTxLen to USBPID_NAK: | ||||
| ; We should set it back when we receive the ACK from the host. This would | ||||
| ; be simple to implement: One static variable which stores whether the last | ||||
| ; tx was for endpoint 0 or 1 and a compare in the receiver to distinguish the | ||||
| ; ACK. However, we set it back immediately when we send the package, | ||||
| ; assuming that no error occurs and the host sends an ACK. We save one byte | ||||
| ; RAM this way and avoid potential problems with endless retries. The rest of | ||||
| ; the driver assumes error-free transfers anyway. | ||||
|  | ||||
| otherOutOrSetup: | ||||
|     clr     x1 | ||||
|     sts     usbCurrentTok, x1 | ||||
| rxDoReturn: | ||||
|     pop     x3                  ;2 | ||||
|     pop     YL                  ;2 | ||||
|     pop     YH                  ;2 | ||||
|     rjmp    sofError            ;2 | ||||
|  | ||||
| isSetupOrOut:                   ; we must be fast here -- a data package may follow / {,24} into next frame | ||||
|     cp      x2, shift           ;1 shift contains our device addr | ||||
|     brne    otherOutOrSetup     ;1 other device -- ignore | ||||
| #if USB_CFG_IMPLEMENT_FN_WRITEOUT   /* if we need second OUT endpoint, store endpoint address */ | ||||
|     andi    x1, 0x7f            ;1 mask out MSb in token | ||||
|     andi    x3, 0x80            ;1 mask out all but endpoint address | ||||
|     or      x1, x3              ;1 merge endpoint into currentToken | ||||
|     sts     usbCurrentTok, x1   ;2 | ||||
|     brmi    dontResetEP0        ;1 endpoint 1 -> don't reset endpoint 0 input | ||||
| #else | ||||
|     sts     usbCurrentTok, x1   ;2 | ||||
| #endif | ||||
| ;A transmission can still have data in the output buffer while we receive a | ||||
| ;SETUP package with an IN phase. To avoid that the old data is sent as a reply, | ||||
| ;we abort transmission. We don't need to reset usbMsgLen because it is used | ||||
| ;from the main loop only where the setup is processed anyway. | ||||
|     ldi     x1, USBPID_NAK      ;1 | ||||
|     sts     usbTxLen, x1        ;2 abort transmission | ||||
| dontResetEP0: | ||||
|     pop     x3                  ;2 | ||||
|     pop     YL                  ;2 | ||||
|     in      x1, USB_INTR_PENDING;1 | ||||
|     sbrc    x1, USB_INTR_PENDING_BIT;1 check whether data is already arriving {,41} into next frame | ||||
|     rjmp    shortcutToStart     ;2 save the pops and pushes -- a new interrupt is aready pending | ||||
| ;If the jump above was not taken, we can be at {,2} into the next frame here | ||||
|     pop     YH                  ;2 | ||||
| txDoReturn: | ||||
| sofError:                       ; error in start of frame -- ignore frame | ||||
|     ldi     x1, 1<<USB_INTR_PENDING_BIT;1 many int0 events occurred during our processing -- clear pending flag | ||||
|     out     USB_INTR_PENDING, x1;1 | ||||
|     pop     shift               ;2 | ||||
|     pop     cnt                 ;2 | ||||
|     pop     x2                  ;2 | ||||
|     pop     x1                  ;2 | ||||
|     out     SREG, x1            ;1 | ||||
|     pop     x1                  ;2 | ||||
|     reti                        ;4 -> {,21} into next frame -> up to 3 sync bits missed | ||||
|  | ||||
| sendCntAndReti:                 ; 19 cycles until SOP | ||||
|     mov     x3, cnt             ;1 | ||||
|     rjmp    usbSendX3           ;2 | ||||
| sendNakAndReti:                 ; 19 cycles until SOP | ||||
|     ldi     x3, USBPID_NAK      ;1 | ||||
|     rjmp    usbSendX3           ;2 | ||||
| sendAckAndReti:                 ; 17 cycles until SOP | ||||
|     ldi     x3, USBPID_ACK      ;1 | ||||
| usbSendX3: | ||||
|     ldi     YL, 20              ;1 'x3' is R20 | ||||
|     ldi     YH, 0               ;1 | ||||
|     ldi     cnt, 2              ;1 | ||||
| ;;;;rjmp    usbSendAndReti      fallthrough | ||||
|  | ||||
| ; USB spec says: | ||||
| ; idle = J | ||||
| ; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 | ||||
| ; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 | ||||
| ; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) | ||||
|  | ||||
| ;usbSend: | ||||
| ;pointer to data in 'Y' | ||||
| ;number of bytes in 'cnt' -- including sync byte | ||||
| ;uses: x1...x4, shift, cnt, Y | ||||
| usbSendAndReti:             ; SOP starts 13 cycles after call | ||||
|     push    x4              ;2 | ||||
|     ldi     x4, USBMASK     ;1 exor mask | ||||
|     sbi     USBOUT, USBMINUS;1 prepare idle state; D+ and D- must have been 0 (no pullups) | ||||
|     in      x1, USBOUT      ;1 port mirror for tx loop | ||||
|     sbi     USBDDR, USBMINUS;1 | ||||
|     sbi     USBDDR, USBPLUS ;1 set D+ and D- to output: acquire bus | ||||
| ; need not init x2 (bitstuff history) because sync starts with 0 | ||||
|     ldi     shift, 0x80     ;1 sync byte is first byte sent | ||||
|     rjmp    txLoop          ;2 -> 13 + 3 = 16 cycles until SOP | ||||
|  | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT    /* placed here due to relative jump range */ | ||||
| handleIn1:                  ;{23, 31} from SE0 | ||||
|     ldi     x1, USBPID_NAK  ;1 | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT3 | ||||
| ; 2006-06-10 as suggested by O.Tamura: support second INTR IN / BULK IN endpoint | ||||
|     ldd     x2, y+2         ;2 | ||||
|     sbrc    x2, 0           ;2 1 | ||||
|     rjmp    handleIn3       ;0 2 | ||||
| #endif | ||||
|     lds     cnt, usbTxLen1  ;2 | ||||
|     sbrc    cnt, 4          ;2 | ||||
|     rjmp    sendCntAndReti  ;0 | ||||
|     sts     usbTxLen1, x1   ;2 | ||||
|     ldi     YL, lo8(usbTxBuf1);1 | ||||
|     ldi     YH, hi8(usbTxBuf1);1 | ||||
|     rjmp    usbSendAndReti  ;2 -> arrives at usbSendAndReti {34, 42} from SE0 | ||||
|  | ||||
| #if USB_CFG_HAVE_INTRIN_ENDPOINT3 | ||||
| handleIn3: | ||||
|     lds     cnt, usbTxLen3  ;2 | ||||
|     sbrc    cnt, 4          ;2 | ||||
|     rjmp    sendCntAndReti  ;0 | ||||
|     sts     usbTxLen3, x1   ;2 | ||||
|     ldi     YL, lo8(usbTxBuf3);1 | ||||
|     ldi     YH, hi8(usbTxBuf3);1 | ||||
|     rjmp    usbSendAndReti  ;2 -> arrives at usbSendAndReti {39, 47} from SE0 | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| bitstuff0:                  ;1 (for branch taken) | ||||
|     eor     x1, x4          ;1 | ||||
|     ldi     x2, 0           ;1 | ||||
|     out     USBOUT, x1      ;1 <-- out | ||||
|     rjmp    didStuff0       ;2 branch back 2 cycles earlier | ||||
| bitstuff1:                  ;1 (for branch taken) | ||||
|     eor     x1, x4          ;1 | ||||
|     ldi     x2, 0           ;1 | ||||
|     sec                     ;1 set carry so that brsh will not jump | ||||
|     out     USBOUT, x1      ;1 <-- out | ||||
|     rjmp    didStuff1       ;2 jump back 1 cycle earler | ||||
| bitstuff2:                  ;1 (for branch taken) | ||||
|     eor     x1, x4          ;1 | ||||
|     ldi     x2, 0           ;1 | ||||
|     rjmp    didStuff2       ;2 jump back 3 cycles earlier and do out | ||||
| bitstuff3:                  ;1 (for branch taken) | ||||
|     eor     x1, x4          ;1 | ||||
|     ldi     x2, 0           ;1 | ||||
|     rjmp    didStuff3       ;2 jump back earlier | ||||
|  | ||||
| txLoop: | ||||
|     sbrs    shift, 0        ;1 | ||||
|     eor     x1, x4          ;1 | ||||
|     out     USBOUT, x1      ;1 <-- out | ||||
|     ror     shift           ;1 | ||||
|     ror     x2              ;1 | ||||
| didStuff0: | ||||
|     cpi     x2, 0xfc        ;1 | ||||
|     brsh    bitstuff0       ;1 | ||||
|     sbrs    shift, 0        ;1 | ||||
|     eor     x1, x4          ;1 | ||||
|     ror     shift           ;1 | ||||
|     out     USBOUT, x1      ;1 <-- out | ||||
|     ror     x2              ;1 | ||||
|     cpi     x2, 0xfc        ;1 | ||||
| didStuff1: | ||||
|     brsh    bitstuff1       ;1 | ||||
|     sbrs    shift, 0        ;1 | ||||
|     eor     x1, x4          ;1 | ||||
|     ror     shift           ;1 | ||||
|     ror     x2              ;1 | ||||
| didStuff2: | ||||
|     out     USBOUT, x1      ;1 <-- out | ||||
|     cpi     x2, 0xfc        ;1 | ||||
|     brsh    bitstuff2       ;1 | ||||
|     sbrs    shift, 0        ;1 | ||||
|     eor     x1, x4          ;1 | ||||
|     ror     shift           ;1 | ||||
|     ror     x2              ;1 | ||||
| didStuff3: | ||||
|     cpi     x2, 0xfc        ;1 | ||||
|     out     USBOUT, x1      ;1 <-- out | ||||
|     brsh    bitstuff3       ;1 | ||||
|     nop2                    ;2 | ||||
|     ld      x3, y+          ;2 | ||||
|     sbrs    shift, 0        ;1 | ||||
|     eor     x1, x4          ;1 | ||||
|     out     USBOUT, x1      ;1 <-- out | ||||
|     ror     shift           ;1 | ||||
|     ror     x2              ;1 | ||||
| didStuff4: | ||||
|     cpi     x2, 0xfc        ;1 | ||||
|     brsh    bitstuff4       ;1 | ||||
|     sbrs    shift, 0        ;1 | ||||
|     eor     x1, x4          ;1 | ||||
|     ror     shift           ;1 | ||||
|     out     USBOUT, x1      ;1 <-- out | ||||
|     ror     x2              ;1 | ||||
|     cpi     x2, 0xfc        ;1 | ||||
| didStuff5: | ||||
|     brsh    bitstuff5       ;1 | ||||
|     sbrs    shift, 0        ;1 | ||||
|     eor     x1, x4          ;1 | ||||
|     ror     shift           ;1 | ||||
|     ror     x2              ;1 | ||||
| didStuff6: | ||||
|     out     USBOUT, x1      ;1 <-- out | ||||
|     cpi     x2, 0xfc        ;1 | ||||
|     brsh    bitstuff6       ;1 | ||||
|     sbrs    shift, 0        ;1 | ||||
|     eor     x1, x4          ;1 | ||||
|     ror     shift           ;1 | ||||
|     ror     x2              ;1 | ||||
| didStuff7: | ||||
|     cpi     x2, 0xfc        ;1 | ||||
|     out     USBOUT, x1      ;1 <-- out | ||||
|     brsh    bitstuff7       ;1 | ||||
|     mov     shift, x3       ;1 | ||||
|     dec     cnt             ;1 | ||||
|     brne    txLoop          ;2 | 1 | ||||
|     cbr     x1, USBMASK     ;1 prepare SE0 [spec says EOP may be 15 to 18 cycles] | ||||
|     pop     x4              ;2 | ||||
|     out     USBOUT, x1      ;1 <-- out SE0 -- from now 2 bits = 16 cycles until bus idle | ||||
|     ldi     cnt, 2          ;| takes cnt * 3 cycles | ||||
| se0Delay:                   ;| | ||||
|     dec     cnt             ;| | ||||
|     brne    se0Delay        ;| -> 2 * 3 = 6 cycles | ||||
| ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: | ||||
| ;set address only after data packet was sent, not after handshake | ||||
|     lds     x2, usbNewDeviceAddr;2 | ||||
|     subi    YL, 20 + 2          ;1 | ||||
|     sbci    YH, 0               ;1 | ||||
|     breq    skipAddrAssign      ;2 | ||||
|     sts     usbDeviceAddr, x2   ;0  if not skipped: SE0 is one cycle longer | ||||
| skipAddrAssign: | ||||
| ;end of usbDeviceAddress transfer | ||||
|     ori     x1, USBIDLE     ;1 | ||||
|     in      x2, USBDDR      ;1 | ||||
|     cbr     x2, USBMASK     ;1 set both pins to input | ||||
|     out     USBOUT, x1      ;1 <-- out J (idle) -- end of SE0 (EOP signal) | ||||
|     cbr     x1, USBMASK     ;1 configure no pullup on both pins | ||||
|     pop     x3              ;2 | ||||
|     pop     YL              ;2 | ||||
|     out     USBDDR, x2      ;1 <-- release bus now | ||||
|     out     USBOUT, x1      ;1 set pullup state | ||||
|     pop     YH              ;2 | ||||
|     rjmp    txDoReturn      ;2 [we want to jump to rxDoReturn, but this saves cycles] | ||||
|  | ||||
|  | ||||
| bitstuff4:                  ;1 (for branch taken) | ||||
|     eor     x1, x4          ;1 | ||||
|     ldi     x2, 0           ;1 | ||||
|     out     USBOUT, x1      ;1 <-- out | ||||
|     rjmp    didStuff4       ;2 jump back 2 cycles earlier | ||||
| bitstuff5:                  ;1 (for branch taken) | ||||
|     eor     x1, x4          ;1 | ||||
|     ldi     x2, 0           ;1 | ||||
|     sec                     ;1 set carry so that brsh is not taken | ||||
|     out     USBOUT, x1      ;1 <-- out | ||||
|     rjmp    didStuff5       ;2 jump back 1 cycle earlier | ||||
| bitstuff6:                  ;1 (for branch taken) | ||||
|     eor     x1, x4          ;1 | ||||
|     ldi     x2, 0           ;1 | ||||
|     rjmp    didStuff6       ;2 jump back 3 cycles earlier and do out there | ||||
| bitstuff7:                  ;1 (for branch taken) | ||||
|     eor     x1, x4          ;1 | ||||
|     ldi     x2, 0           ;1 | ||||
|     rjmp    didStuff7       ;2 jump back 4 cycles earlier | ||||
|  | ||||
| ; ######################## utility functions ######################## | ||||
|  | ||||
| #ifdef __IAR_SYSTEMS_ASM__ | ||||
| /* Register assignments for usbCrc16 on IAR cc */ | ||||
| /* Calling conventions on IAR: | ||||
|  * First parameter passed in r16/r17, second in r18/r19 and so on. | ||||
|  * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) | ||||
|  * Result is passed in r16/r17 | ||||
|  * In case of the "tiny" memory model, pointers are only 8 bit with no | ||||
|  * padding. We therefore pass argument 1 as "16 bit unsigned". | ||||
|  */ | ||||
| RTMODEL "__rt_version", "3" | ||||
| /* The line above will generate an error if cc calling conventions change. | ||||
|  * The value "3" above is valid for IAR 4.10B/W32 | ||||
|  */ | ||||
| #   define argLen   r18 /* argument 2 */ | ||||
| #   define argPtrL  r16 /* argument 1 */ | ||||
| #   define argPtrH  r17 /* argument 1 */ | ||||
|  | ||||
| #   define resCrcL  r16 /* result */ | ||||
| #   define resCrcH  r17 /* result */ | ||||
|  | ||||
| #   define ptrL     ZL | ||||
| #   define ptrH     ZH | ||||
| #   define ptr      Z | ||||
| #   define byte     r22 | ||||
| #   define bitCnt   r19 | ||||
| #   define polyL    r20 | ||||
| #   define polyH    r21 | ||||
| #   define scratch  r23 | ||||
|  | ||||
| #else  /* __IAR_SYSTEMS_ASM__ */  | ||||
| /* Register assignments for usbCrc16 on gcc */ | ||||
| /* Calling conventions on gcc: | ||||
|  * First parameter passed in r24/r25, second in r22/23 and so on. | ||||
|  * Callee must preserve r1-r17, r28/r29 | ||||
|  * Result is passed in r24/r25 | ||||
|  */ | ||||
| #   define argLen   r22 /* argument 2 */ | ||||
| #   define argPtrL  r24 /* argument 1 */ | ||||
| #   define argPtrH  r25 /* argument 1 */ | ||||
|  | ||||
| #   define resCrcL  r24 /* result */ | ||||
| #   define resCrcH  r25 /* result */ | ||||
|  | ||||
| #   define ptrL     XL | ||||
| #   define ptrH     XH | ||||
| #   define ptr      x | ||||
| #   define byte     r18 | ||||
| #   define bitCnt   r19 | ||||
| #   define polyL    r20 | ||||
| #   define polyH    r21 | ||||
| #   define scratch  r23 | ||||
|  | ||||
| #endif | ||||
|  | ||||
| ; extern unsigned usbCrc16(unsigned char *data, unsigned char len); | ||||
| ; data: r24/25 | ||||
| ; len: r22 | ||||
| ; temp variables: | ||||
| ;   r18: data byte | ||||
| ;   r19: bit counter | ||||
| ;   r20/21: polynomial | ||||
| ;   r23: scratch | ||||
| ;   r24/25: crc-sum | ||||
| ;   r26/27=X: ptr | ||||
| usbCrc16: | ||||
|     mov     ptrL, argPtrL | ||||
|     mov     ptrH, argPtrH | ||||
|     ldi     resCrcL, 0xff | ||||
|     ldi     resCrcH, 0xff | ||||
|     ldi     polyL, lo8(0xa001) | ||||
|     ldi     polyH, hi8(0xa001) | ||||
| crcByteLoop: | ||||
|     subi    argLen, 1 | ||||
|     brcs    crcReady | ||||
|     ld      byte, ptr+ | ||||
|     ldi     bitCnt, 8 | ||||
| crcBitLoop: | ||||
|     mov     scratch, byte | ||||
|     eor     scratch, resCrcL | ||||
|     lsr     resCrcH | ||||
|     ror     resCrcL | ||||
|     lsr     byte | ||||
|     sbrs    scratch, 0 | ||||
|     rjmp    crcNoXor | ||||
|     eor     resCrcL, polyL | ||||
|     eor     resCrcH, polyH | ||||
| crcNoXor: | ||||
|     dec     bitCnt | ||||
|     brne    crcBitLoop | ||||
|     rjmp    crcByteLoop | ||||
| crcReady: | ||||
|     com     resCrcL | ||||
|     com     resCrcH | ||||
|     ret | ||||
|  | ||||
| ; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len); | ||||
| usbCrc16Append: | ||||
|     rcall   usbCrc16 | ||||
|     st      ptr+, resCrcL | ||||
|     st      ptr+, resCrcH | ||||
|     ret | ||||
							
								
								
									
										21
									
								
								firmware/usbdrv/usbdrvasm.asm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								firmware/usbdrv/usbdrvasm.asm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| /* Name: usbdrvasm.asm | ||||
|  * Project: AVR USB driver | ||||
|  * Author: Christian Starkjohann | ||||
|  * Creation Date: 2006-03-01 | ||||
|  * Tabsize: 4 | ||||
|  * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH | ||||
|  * License: Proprietary, free under certain conditions. See Documentation. | ||||
|  * This Revision: $Id: usbdrvasm.asm,v 1.1 2006/09/26 18:18:27 rschaten Exp $ | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| General Description: | ||||
| The IAR compiler/assembler system prefers assembler files with file extension | ||||
| ".asm". We simply provide this file as an alias for usbdrvasm.S. | ||||
|  | ||||
| Thanks to Oleg Semyonov for his help with the IAR tools port! | ||||
| */ | ||||
|  | ||||
| #include "usbdrvasm.S" | ||||
|  | ||||
| end | ||||
							
								
								
									
										1252
									
								
								usb-led-fader.doxygen
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1252
									
								
								usb-led-fader.doxygen
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user